Android:通过保存的URI字符串启动时加载文件失败
问题原因及解决方案
核心问题
- 临时权限未持久化:通过
Intent.ACTION_OPEN_DOCUMENT_TREE获取的目录Uri,默认仅在当前应用会话内有效。重启应用后,系统会回收这个临时权限,导致保存的Uri无法访问目录内容。READ_EXTERNAL_STORAGE权限在这里不生效,因为SAF(存储访问框架)的权限机制和传统文件权限是分开的。 - Uri处理错误:
loadImages方法里没必要用DocumentsContract.buildChildDocumentsUriUsingTree构建子Uri,直接用选中的Tree Uri创建DocumentFile即可,多余的Uri转换会导致访问路径错误。
修复步骤
- 持久化Uri权限:在
onActivityResult拿到Uri后,调用contentResolver.takePersistableUriPermission获取持久权限,这样重启应用后仍能访问该目录。 - 修正Uri使用方式:简化
loadImages方法,直接用传入的Tree Uri实例化DocumentFile。 - 权限申请补充:启动选择目录的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
相关产品推荐
相关产品推荐

