Android文件选择器

前沿

最近又在扮演合规工程师觉得。
华为应用市场把公司应用下架了,原因是权限申请时,没有明确的解释说明权限的用途。
大概就是下面这样,在顶部弄个权限说明![在这里插入图片描述]
Android文件选择器_第1张图片

好吧,看了一眼几个大厂的App,确实是做了提示。行, 那就改吧。

最佳实践

看了之前的代码,使用的是第三方的库实现,已经无法再Android API 30后的版本了,只能另外实现。
但是既然系统有自带的文件选择器,那就用系统的吧,没有必要自己实现,麻烦。用第三方库有怕更新不及时,出现一些问题修复不及时。还是用系统的靠谱,没有 UI 的要求,能用就行。

private fun onPickDoc() {
        generateActivityResultContract(
            Intent(Intent.ACTION_GET_CONTENT).apply {
                type = "*"
                addCategory(Intent.CATEGORY_OPENABLE)
            },
        ) { code, intent ->
            intent?.data?.takeIf { code == Activity.RESULT_OK }
        }.let {
            startActivityForResult(it) { uri ->
                uri ?: return@startActivityForResult

                contentResolver.query(uri, null, null, null, null)?.use { cursor ->
                    cursor.moveToFirst()
                    lastPdfName = if (cursor.count > 0) {
                        val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                        cursor.getString(nameIndex)
                    } else {
                        uri.path?.let { it1 -> File(it1).name }
                    }
                    // 这里有坑
                    uploadFile(FileUtils.copyFile(this, uri, "pdf", lastPdfName ?: "pdf"))
                }
            }
        }
    }

处理坑

在注释的地方有个问题,就是在进行上传的时候,会报错没有文件访问权限。
但是我的已经是Androi 14(API 34)了,已经没有"android.permission.WRITE_EXTERNAL_STORAGE"权限,而且已经申请了下面三个权限:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

既然权限都申请了,居然还会报错无法访问文件!
后面怀疑是不是从拿到的Uri获取的路径不对,然后各种尝试拿到真实的文件地址。
最后呢拿到真实地址了,结果还是一样,无法读取文件数据,报错没有权限。

一天后才想到,应该是这个文件可能出现在任何位置,我们的App虽然拿到了地址,但是在上传的时候,第三方SDK在处理这个文件地址时,应该没有权限去访问。

所以,只要把文件拷贝到我们App内data下的目录下,应该就有访问权限了。访问这个我们自己的目录是不需要权限的。

object FileUtils {

    fun copyFile(context: Context, sourceUri: Uri, folderPath: String, fileName: String): String? {
        val contentResolver: ContentResolver = context.contentResolver
        return try {
            val folder = File(App.instance.externalCacheDir, folderPath)
            if (!folder.mkdirs() && (!folder.exists() || !folder.isDirectory)) {
                return null
            }
            val destinationFile = File(folder, fileName)
            contentResolver.openInputStream(sourceUri)?.use { input ->
                destinationFile.outputStream().use { output ->
                    input.copyTo(output, DEFAULT_BUFFER_SIZE)
                }
            }
            destinationFile.absolutePath
        } catch (e: IOException) {
            null
        }
    }
}

最后运行代码,确实解决了文件访问问题。

你可能感兴趣的:(android)