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

Android 10 Kotlin应用读取外部存储文件权限异常后恢复问题求助

问题分析与解决方案

从你的描述和代码对比来看,导致两种情况出现差异的核心原因主要有两个,我来逐一拆解:

1. 权限申请回调未处理,首次授权后未触发读取逻辑

你的setupPermissions()方法只在onCreate()中调用了一次:

  • 当应用首次启动时,权限未被授予,会触发ActivityCompat.requestPermissions()弹出授权对话框;
  • 用户点击同意后,系统会自动回调onRequestPermissionsResult()方法,但你的代码中没有重写这个方法来重新执行文件读取逻辑
  • 这时候你看到的"读取失败"其实是首次onCreate()中权限未通过时的结果,而授权成功后并没有再次尝试读取文件。

而你修改代码后再次测试时,大概率是重新启动了应用——这时候权限已经被系统记住,setupPermissions()会直接进入权限已授予的分支,自然能成功读取文件。

2. Android 10 Scoped Storage 限制(次要但需注意)

Android 10(API 29)默认启用了Scoped Storage,即使申请了READ_EXTERNAL_STORAGE权限,直接访问外部存储根目录的非媒体文件(比如你的number.txt)也会被系统限制,除非你在AndroidManifest.xml<application>标签中添加兼容配置:

android:requestLegacyExternalStorage="true"

不过这个点不是你两种代码差异的核心原因(否则第二种情况也会失败),但如果后续你升级到更高版本的Android系统,这个配置会直接影响你的文件访问逻辑。


修复方案

方案一:处理权限申请回调,授权成功后重新读取文件

重写onRequestPermissionsResult()方法,当权限被授予时再次执行文件读取逻辑,同时把读取逻辑抽成单独方法方便复用:

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 101) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限授予成功,执行文件读取
            readNumberFile()
        } else {
            // 权限被拒绝,给用户友好提示
            Toast.makeText(this, "需要读取存储权限才能获取文件内容", Toast.LENGTH_SHORT).show()
        }
    }
}

// 单独抽离文件读取逻辑
private fun readNumberFile() {
    val file = File("/storage/emulated/0/number.txt")
    try {
        val pln = file.readText()
        plnText.text = pln
        Log.i("IKO_APP", pln)
    } catch (e: Exception) {
        Log.e("IKO_APP", "读取文件失败:${e.message}")
    }
}

// 修改原setupPermissions方法
private fun setupPermissions() { 
    val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) 
    if (permission == PackageManager.PERMISSION_GRANTED) { 
        readNumberFile()
    } else{ 
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 101) 
    } 
}

方案二:适配Scoped Storage(长期推荐)

如果你想遵循Android最新的存储规范,避免依赖requestLegacyExternalStorage兼容配置,可以使用**存储访问框架(SAF)**让用户手动选择文件,这种方式不需要申请READ_EXTERNAL_STORAGE权限:

// 启动系统文件选择器
private fun openFilePicker() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "text/*" // 指定选择文本文件
    }
    startActivityForResult(intent, 102)
}

// 处理文件选择结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 102 && resultCode == RESULT_OK) {
        data?.data?.let { uri ->
            contentResolver.openInputStream(uri)?.use { inputStream ->
                val pln = inputStream.bufferedReader().readText()
                plnText.text = pln
                Log.i("IKO_APP", pln)
            }
        }
    }
}

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

火山引擎 最新活动