分区存储获取文件
分区存储获取文件
从 Android Jetpack 的 Activity Result API 开始,推荐使用 ActivityResultLauncher 来处理活动结果(如启动文件选择器或拍照等操作)。相比于传统的 startActivityForResult() 方法,ActivityResultLauncher 提供了更简洁、现代化的 API。
以下是使用 ActivityResultLauncher 和 Storage Access Framework (SAF) 获取文件的完整示例代码:
1. 使用 ActivityResultLauncher 启动 SAF 文件选择器
1 | import android.content.Intent; |
2. 关键点解析
2.1 注册 ActivityResultLauncher
- 使用
registerForActivityResult()方法注册一个ActivityResultLauncher。 - 第一个参数是
ActivityResultContracts.StartActivityForResult(),表示我们希望接收一个活动结果。 - 第二个参数是回调函数,当用户完成文件选择后会触发该回调。
1 | filePickerLauncher = registerForActivityResult( |
2.2 启动文件选择器
- 使用
filePickerLauncher.launch(intent)启动文件选择器。 Intent配置与传统的 SAF 文件选择器相同,例如设置 MIME 类型和可打开类别。
1 | Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); |
2.3 处理文件内容
- 通过
ContentResolver.openInputStream()打开文件输入流并读取内容。 - 如果需要解压
.tar或其他格式的文件,可以结合第三方库(如 Apache Commons Compress)进行处理。
3. 运行效果
- 用户点击按钮或触发逻辑,启动文件选择器。
- 用户从设备中选择一个文件。
- 应用通过
ActivityResultLauncher获取到文件的Uri,并通过输入流读取其内容。
4. 优势
- 现代 API:
ActivityResultLauncher是 Jetpack 提供的现代化 API,避免了传统startActivityForResult()的复杂性。 - 生命周期安全:
ActivityResultLauncher自动绑定到 Activity 或 Fragment 的生命周期,减少了内存泄漏的风险。 - 简洁易读:代码结构更加清晰,便于维护。
5. 注意事项
- MIME 类型:根据需求设置合适的 MIME 类型。如果不确定文件类型,可以使用通配符
*/*。 - 持久化权限:如果需要长期访问用户选择的文件,可以通过
takePersistableUriPermission()请求持久化权限。
1 | if (fileUri != null) { |
- 大文件处理:对于较大的文件,建议分块读取或直接写入本地存储以避免内存溢出。
6. 总结
使用 ActivityResultLauncher 和 SAF 的组合是现代 Android 开发中推荐的方式,既符合分区存储的要求,又简化了代码逻辑。通过这种方式,你可以轻松实现对用户选择的文件的访问和处理,而无需动态申请传统存储权限(如 READ_EXTERNAL_STORAGE)。
是的,Storage Access Framework (SAF) 的工作原理确实依赖于 Content Provider。SAF 提供了一种标准化的方式来访问设备上的文件和其他数据,而这些数据通常是通过 ContentProvider 来提供的。
SAF 和 Content Provider
Content Provider 概述
- 在 Android 中,
ContentProvider是一种用于在不同应用之间共享数据的标准接口。它封装了数据存储、检索和更新的操作,并提供了统一的接口来访问这些数据。 - 例如,
MediaStoreAPI 就是基于ContentProvider实现的,允许应用查询、插入、更新或删除媒体文件(如图片、音频、视频)。
- 在 Android 中,
SAF 如何使用 Content Provider
- 当你使用 SAF 启动文件选择器(如
Intent.ACTION_OPEN_DOCUMENT或Intent.ACTION_CREATE_DOCUMENT),实际上是在请求访问由其他应用(如文件管理器或其他存储服务)提供的内容。 - 这些内容通常是由特定的
ContentProvider提供的。一旦用户选择了某个文件,Android 系统会返回一个Uri,该Uri可以用来通过ContentResolver访问所选文件的内容。 ContentResolver是 Android 提供的一个类,允许你与任何实现了ContentProvider接口的数据源进行交互。你可以使用它来执行各种操作,比如打开输入/输出流、查询元数据等。
- 当你使用 SAF 启动文件选择器(如
具体流程
当你使用 SAF 获取文件时,整个过程大致如下:
启动文件选择器
- 使用
Intent.ACTION_OPEN_DOCUMENT或类似的意图启动文件选择器。1
2
3
4Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf"); // 示例:只显示 PDF 文件
startActivityForResult(intent, REQUEST_CODE); // 使用 ActivityResultLauncher 更推荐
- 使用
获取 Uri
- 用户从文件选择器中选择一个文件后,系统会返回一个
Uri,这个Uri指向所选文件的位置。1
2
3
4
5
6
7
8
9
10
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
handleDocument(uri);
}
}
}
- 用户从文件选择器中选择一个文件后,系统会返回一个
通过 ContentResolver 访问文件
- 使用
ContentResolver打开文件输入流并读取文件内容。1
2
3
4
5
6
7
8private void handleDocument(Uri uri) {
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
// 处理输入流...
} catch (IOException e) {
e.printStackTrace();
}
}
- 使用
在这个过程中,uri 实际上是一个指向特定 ContentProvider 的标识符,该 ContentProvider 负责提供对所选文件的实际访问。这使得 SAF 成为一种非常灵活且安全的方式,因为它允许应用仅访问用户明确选择的文件,而不是直接访问整个文件系统。
总结
- SAF 利用了
ContentProvider来实现文件的选择和访问。 - 它通过返回一个
Uri给调用方,允许调用方通过ContentResolver与提供该Uri的ContentProvider进行交互,从而实现对选定文件的读取、写入等操作。 - 这种机制不仅提高了安全性,还简化了跨应用的数据共享,同时符合 Android 分区存储的要求。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Fleming's Blog!






