概述
存储访问框架(SAF)是在Android 4.4(API 级别 19)引入的。借助 SAF,用户可轻松打开文档、图像及其他文件。
存储访问框架包含三部分:
- 文档提供程序 — 文档提供程序以
DocumentsProvider
类的子类形式实现。Android 平台包含有内置文档提供程序,如 Downloads、Images 和 Videos。 - 客户端应用 — 应用可以通过发送
ACTION_OPEN_DOCUMENT
或ACTION_CREATE_DOCUMENT
的Intent来启动选择器,并可以接收文档提供程序返回的文件。 - 选择器 — 系统界面,可让用户选择客户端应用期望格式的文档。
三者的关系如下图
客户端可以通过以下的Action来启动系统选择器,让用户做相关的操作:
ACTION_OPEN_DOCUMENT
打开用户选择的文件ACTION_CREATE_DOCUMENT
在用户选择的位置创建文件ACTION_OPEN_DOCUMENT_TREE
访问某个目录,在Android5.0(API 级别 21)首次引入。
创建应用并写入内容
我们以创建一个jpg文件写入内容为例,来看看如何使用SAF。
- 通过
ACTION_CREATE_DOCUMENT
的Intent启动系统选择器
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "image/jpeg" putExtra(Intent.EXTRA_TITLE, "invoice.jpg")}startActivityForResult(intent, REQUEST_CODE_CREATE_FILE)
- 用户选择某个目录创建invoice.jpg文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlrfzaR2-1600607710842)(/Volumes/work/Android分区存储/Android发布文章/SAF/create_document.png)]
- onActivityResult中写入内容
REQUEST_CODE_CREATE_FILE -> {
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(uri, takeFlags)
val documentFile = DocumentFile.fromSingleUri(this, uri)
val outputStream = contentResolver.openOutputStream(uri) ?: return
val inputStream = this.assets.open("friends.jpg")
try {
outputStream.use { outputStream ->
inputStream.use { inputStream ->
val byteArray = ByteArray(1024)
while (true) {
val readCount = inputStream.read(byteArray)
if (readCount == -1) {
break
}
outputStream.write(byteArray, 0, readCount)
}
}
}
} catch (e: IOException) {
Log.e("wfeii", "$e")
}
}
DocumentFile
对于返回的文件或者目录我们可以使用DocumentFile来操作,DocumentFile提供了三个静态方法,用于获取DocumentFile对象,分别是:
-
//根据File获取DocumentFile public static DocumentFile fromFile(@NonNull File file)
-
//根据Uri获取目录的DocumentFile public static DocumentFile fromTreeUri(@NonNull Context context, @NonNull Uri treeUri)
-
//根据Uri获取文件的DocumentFile public static DocumentFile fromSingleUri(@NonNull Context context, @NonNull Uri singleUri)
DocumentFile提供了很多实用的接口,比如获取文件名等等,方便我们的操作。
引入方式:
-
//androidx implementation 'androidx.legacy:legacy-support-v4:1.0.0'
-
//非androidx implementation 'com.android.support:support-v4:28.0.0'
SAF针对选择文件是很方便的,以前我们使用它的时候,是先获取其对应File的目录再来做出来,而Android11中非公共目录下文件是不能直接操作的,我们可以复制我们自己的目录下,然后再做操作。
参考文档
代码地址:https://github.com/wfeii/Android11
限于个人水平,有错误请指出,大家共同学习进步!
扫码关注公众号,查看更多内容。