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

Android:通过保存的URI字符串启动时加载文件失败

问题原因及解决方案

核心问题

  1. 临时权限未持久化:通过Intent.ACTION_OPEN_DOCUMENT_TREE获取的目录Uri,默认仅在当前应用会话内有效。重启应用后,系统会回收这个临时权限,导致保存的Uri无法访问目录内容。READ_EXTERNAL_STORAGE权限在这里不生效,因为SAF(存储访问框架)的权限机制和传统文件权限是分开的。
  2. Uri处理错误:loadImages方法里没必要用DocumentsContract.buildChildDocumentsUriUsingTree构建子Uri,直接用选中的Tree Uri创建DocumentFile即可,多余的Uri转换会导致访问路径错误。

修复步骤

  1. 持久化Uri权限:在onActivityResult拿到Uri后,调用contentResolver.takePersistableUriPermission获取持久权限,这样重启应用后仍能访问该目录。
  2. 修正Uri使用方式:简化loadImages方法,直接用传入的Tree Uri实例化DocumentFile。
  3. 权限申请补充:启动选择目录的Intent时,添加FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_PERSISTABLE_URI_PERMISSION标记,确保后续能持久化权限。

修改后的完整代码

package com.example.loadingtest

import android.Manifest
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile

class FullscreenActivity : AppCompatActivity(), View.OnClickListener {
    lateinit var preferences: SharedPreferences
    var text = ""
    val pickCode = 1
    lateinit var textField: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fullscreen)

        val permissionStorage = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
        val requestExternalStorage = 1
        val permission = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
        if (permission != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(permissionStorage, requestExternalStorage)
        }

        textField = findViewById(R.id.texttest)
        text = getString(R.string.txt_amount)
        val btn = findViewById<Button>(R.id.btntest)
        btn.setOnClickListener(this)

        preferences = getPreferences(MODE_PRIVATE)
        val path = preferences.getString("path", "")
        if (path != null && path != "") {
            loadImages(path.toUri())
        }
    }

    fun loadImages(uri: Uri) {
        // 直接用传入的Tree Uri创建DocumentFile,无需额外转换
        val tree = DocumentFile.fromTreeUri(this, uri) ?: return
        val files = tree.listFiles()
        textField.text = String.format(text, files.size)
    }

    override fun onClick(v: View?) {
        if (v == null) return
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
        // 添加临时权限与持久化标记
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
        startActivityForResult(intent, pickCode)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == pickCode && resultCode == RESULT_OK) {
            data?.data?.let { uri ->
                loadImages(uri)
                // 持久化Uri权限
                contentResolver.takePersistableUriPermission(
                    uri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION
                )
                // 保存Uri字符串到SharedPreferences
                val editor = preferences.edit()
                editor.putString("path", uri.toString())
                editor.apply()
            }
        }
    }
}

额外说明

  • 如果需要对目录进行写入操作,记得在intent.addFlags和takePersistableUriPermission中同时添加Intent.FLAG_GRANT_WRITE_URI_PERMISSION。
  • 持久化的权限会一直保留,直到用户在系统设置中手动撤销应用对该目录的访问权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:05:16