You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何复用Android中ACTION_OPEN_DOCUMENT_TREE返回的URI?

解决ACTION_OPEN_DOCUMENT_TREE URI持久化复用问题

问题根源

你仅保存了URI字符串,但Android对通过ACTION_OPEN_DOCUMENT_TREE获取的URI仅授予临时访问权限,应用重启后权限会被系统收回,导致复用URI时无法正常访问目录内容。必须主动申请持久化URI权限,才能跨会话复用该URI。

修正步骤

1. 获取URI时持久化权限

在onActivityResult拿到目标URI后,先调用ContentResolver.takePersistableUriPermission()保存权限,再存储URI字符串:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == directoryPickerID && resultCode == Activity.RESULT_OK) {
        data?.data?.also { uri ->
            if (uri.toString() == "content://com.android.externalstorage.documents/tree/primary%3ADocuments") {
                // 申请持久化读写权限(按需选择READ/WRITE)
                val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                contentResolver.takePersistableUriPermission(uri, takeFlags)
                
                // 验证当前访问正常(保留原逻辑)
                val documentsTree = DocumentFile.fromTreeUri(this, uri) ?: return
                val childDocuments = documentsTree.listFiles()
                childDocuments.forEach { doc ->
                    doc.name?.let { Log.d("documentsUri", it) }
                }
                
                // 存储URI到SharedPreferences
                defaultSharedPreferences.edit()
                    .putString("documentsUri", uri.toString())
                    .apply()
            }
        }
    }
}

2. 复用URI的正确方式

后续会话中,直接读取存储的URI字符串并解析即可,此时因已持有持久化权限,DocumentFile能正常访问目录:

defaultSharedPreferences.getString("documentsUri", null)?.let { uriStr ->
    val documentsUri = Uri.parse(uriStr)
    // 可选:提前检查权限是否有效(避免用户手动撤销权限)
    val hasReadPermission = contentResolver.checkUriPermission(
        documentsUri,
        android.os.Process.myPid(),
        android.os.Process.myUid(),
        Intent.FLAG_GRANT_READ_URI_PERMISSION
    ) == PackageManager.PERMISSION_GRANTED
    
    if (hasReadPermission) {
        val documentsTree = DocumentFile.fromTreeUri(this, documentsUri) ?: return@let
        val childDocuments = documentsTree.listFiles()
        childDocuments.forEach { doc ->
            doc.name?.let { Log.d("documentsUri2", it) }
        }
    } else {
        // 权限已失效,需重新引导用户选择目录
        Log.d("documentsUri2", "目录访问权限已失效,请重新选择")
    }
}

关键说明

  • takePersistableUriPermission只能在用户通过系统选择器授予权限后调用(即onActivityResult返回RESULT_OK时),无法主动申请
  • 持久化权限会一直保留,直到用户在系统设置中手动撤销应用对该目录的访问权限
  • 若需要读写权限,必须在调用takePersistableUriPermission时同时传入READ和WRITE标志

内容的提问来源于stack exchange,提问作者corbin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:25:18