You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

调用系统文件夹选择器选择下载路径时如何避免触发权限请求?

解决每次选择下载文件夹重复弹出权限请求的问题

咱们先来理清楚问题根源:你虽然申请了WRITE_EXTERNAL_STORAGE权限,但从Android 10(API 29)开始的Scoped Storage机制彻底改变了存储权限逻辑——这个权限现在仅能访问媒体文件(图片、音频、视频等),而通过ACTION_OPEN_DOCUMENT_TREE打开的文件夹属于Storage Access Framework (SAF) 范畴,它的权限是基于URI的临时权限,默认每次选择都会重新请求,和你申请的传统存储权限完全是两回事。

要避免重复弹出权限请求,核心是持久化用户选择的文件夹URI及其权限,这样下次启动应用时直接复用即可。下面是具体的实现步骤:

1. 持久化SAF权限

当用户第一次选择完文件夹后,调用ContentResolver.takePersistableUriPermission()把这个URI的读写权限持久化下来,系统就不会每次都要求用户确认权限了。

用AndroidX的ActivityResultContracts实现(官方推荐)

现在Android官方推荐用ActivityResultContracts替代旧的onActivityResult,代码更简洁且生命周期安全:

// 注册文件夹选择的Launcher
private val openDocumentTreeLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { selectedUri ->
    selectedUri?.let { uri ->
        // 持久化读写权限
        contentResolver.takePersistableUriPermission(
            uri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        )
        // 把URI保存到SharedPreferences,方便下次启动复用
        val prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE)
        prefs.edit()
            .putString("saved_download_dir", uri.toString())
            .apply()
        
        // 这里就可以用这个URI进行下载操作了
    }
}

// 启动文件夹选择器的触发代码
fun openDownloadFolderSelector() {
    openDocumentTreeLauncher.launch(null)
}

2. 下次启动时复用已保存的URI

应用重启后,先读取之前保存的URI,检查权限是否有效,有效就直接使用,不用再打开选择器:

fun initDownloadDir() {
    val prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE)
    val savedUriStr = prefs.getString("saved_download_dir", null)
    
    if (savedUriStr != null) {
        val savedUri = Uri.parse(savedUriStr)
        // 检查权限是否仍然有效
        val hasWritePermission = contentResolver.checkUriPermission(
            savedUri,
            Process.myPid(),
            Process.myUid(),
            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        ) == PackageManager.PERMISSION_GRANTED
        
        if (hasWritePermission) {
            // 直接使用这个URI进行下载操作,无需再次打开选择器
            useSavedDownloadDir(savedUri)
        } else {
            // 权限失效,重新打开选择器
            openDownloadFolderSelector()
        }
    } else {
        // 没有保存的URI,首次打开选择器
        openDownloadFolderSelector()
    }
}

// 自定义方法:使用保存的URI进行下载操作
private fun useSavedDownloadDir(uri: Uri) {
    // 在这里实现你的下载逻辑,比如创建文件、写入数据等
}

关键注意点

  • 持久化的权限会一直保留,直到用户在系统设置中撤销应用对该文件夹的访问权限,或者应用被卸载。
  • 如果用户选择的是系统默认的Download目录,你也可以考虑用MediaStore.Downloads来操作,但如果是自定义文件夹,SAF的方式更可靠。
  • 从Android 11(API 30)开始,WRITE_EXTERNAL_STORAGE权限已经被标记为deprecated,建议完全转向SAF或MediaStore来处理文件操作。

内容的提问来源于stack exchange,提问作者Ильмир Шагабиев

火山引擎 最新活动