自从android10使用分区存储后,文件的操作显得更为复杂,幸运的是,谷歌为我们提供许多操作简单的api,这篇文章主要讲的是android除沙盒目录和共享目录外其他目录的操作
android10以上,如果想要获取其他文件,最简单的方法是申请MANAGE_EXTERNAL_STORAGE权限,获取该权限之后,任何文件都可以正常操作,然后你就不用往下看了……
由于registerForActivityResult()正式版已发布很久,并且官方为registerForActivityResult提供了许多简便易用的api,这里获取文件的方法使用registerForActivityResult
如果找不到registerForActivityResult,你需要将activity版本提升到1.2.0,fragment版本提升到1.3.0以上
//打开文件选择器,选择txt文件
val select = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
}
//txt的mimeType,其他文件的则使用相对应的type,比如pdf,则传application/pdf,图片文件传image/*
select.launch("text/plain")
//多选文件:
val select =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
}
select.launch("text/plain")
//多类型多文件
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()){
}.launch(arrayOf("mimeType1","mimeType2"))
//多类型单文件
registerForActivityResult(ActivityResultContracts.OpenDocument()){
}.launch(arrayOf("mimeType1","mimeType2"))
//打开文件夹选择器
private val select = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
}
select.launch(null)
//获取到文件夹uri后,如果需要遍例文件夹下所有文件:
val tree = DocumentFile.fromTreeUri(this,文件夹uri)
if(tree != null){
val uris = tree.listFiles()
uris.forEach {
//该文件夹下文件uri
it.uri
}
}
DocumentFile操作访问文件效率远远不及file操作,如果只是一个文件倒没什么,但如果是个文件夹,里面上百个文件,那么访问速度慢的问题就体现出来了,这时可以将获取到的文件夹uri转成file再操作,这种方法目前还可以用,但我不知道什么时候谷歌会把这方便禁掉
注:需要requestLegacyExternalStorage="true" 和READ_EXTERNAL_STORAGE权限
fun getTreeFile(context: Context, uri: Uri): File? {
val docId = DocumentsContract.getTreeDocumentId(uri)
val split = docId.split(":")
val type = split[0]
if(type.equals("primary",true)){
return runCatching { File("${Environment.getExternalStorageDirectory()}/${split[1]}") }.getOrNull()
}else{
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")
val getVolumeList: Method = manager.javaClass.getMethod("getVolumeList")
val getUuid = storageVolumeClazz.getMethod("getUuid")
val getState = storageVolumeClazz.getMethod("getState")
val getPath = storageVolumeClazz.getMethod("getPath")
val result = getVolumeList.invoke(manager)
val length = Array.getLength(result)
for (i in 0 until length){
val ele = Array.get(result,i)
if(Environment.MEDIA_MOUNTED == getState.invoke(ele) || Environment.MEDIA_MOUNTED_READ_ONLY == getState.invoke(ele)){
val uuid = getUuid.invoke(ele) as? String
if(uuid == type){
return runCatching { File("${getPath.invoke(ele)}/${split[1]}") }.getOrNull()
}
}
}
}
return null
}
访问文件夹,也是需要访问权限的,一般来说,通过文件管理器获取的uri,会在获取的时候得到授权,这个权限会直到你的应用重启时才失效,如果你需要永久保留这个文件夹的使用权限,可以通过这段代码申请:
applicationContext.contentResolver.takePersistableUriPermission(
需要保留访问权限的uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)