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

Android 11读取文件遇FileNotFoundException及ANR问题求助

Android 11 文件读取适配问题解决

问题根源分析

旧问题(FileNotFoundException)

Android 11引入**分区存储(Scoped Storage)**后,App无法直接通过Uri转换为File对象访问非私有目录下的文件。比如通过文档选择器、媒体库获取的Uri,对应的文件路径对App完全不可见,直接用FileReader读取必然抛出文件不存在异常——这是系统权限限制导致的,并非代码写法错误。

更新后问题(ANR)

改用getContentResolver().openInputStream()本身是正确的适配方向,但如果在主线程执行文件读取操作,一旦文件较大或IO耗时较长,就会阻塞主线程超过5秒,触发ANR(应用无响应)。

解决方案

1. 遵循Scoped Storage规范,用ContentResolver处理文件

完全放弃直接操作File对象,通过ContentResolver提供的流API读取文件内容,这是Android 11及以上版本的标准做法。

2. 耗时IO操作必须放在后台线程

所有文件读取、写入操作都要放到子线程执行,读取完成后再切回主线程更新UI(比如TextView)。推荐用Kotlin协程、Java线程池实现,避免使用已废弃的AsyncTask。

3. 权限适配要点

  • Android 11及以上,通过系统文档选择器获取的Uri无需额外申请READ_EXTERNAL_STORAGE权限,系统已临时授权该Uri的访问权限
  • 如果是访问媒体库文件,Android 11及以上也无需申请READ_EXTERNAL_STORAGE,直接通过MediaStore或ContentResolver访问即可

修正后的代码示例

Kotlin(协程实现)

// 在Activity/Fragment中调用
private fun readFileContent(uri: Uri) {
    lifecycleScope.launch(Dispatchers.IO) { // 后台线程执行IO操作
        val content = try {
            contentResolver.openInputStream(uri)?.use { inputStream ->
                inputStream.bufferedReader().readText()
            } ?: "文件读取失败"
        } catch (e: IOException) {
            "读取异常: ${e.message}"
        }
        // 切回主线程更新TextView
        withContext(Dispatchers.Main) {
            textView.text = content
        }
    }
}

Java(线程池实现)

private void readFileContent(Uri uri) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());

    executor.execute(() -> {
        StringBuilder content = new StringBuilder();
        try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
            if (inputStream != null) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
            } else {
                content.append("文件读取失败");
            }
        } catch (IOException e) {
            content.append("读取异常: ").append(e.getMessage());
        }
        // 主线程更新UI
        handler.post(() -> textView.setText(content.toString()));
        executor.shutdown();
    });
}

ANR错误日志解读

如果你的ANR日志显示类似以下内容,说明主线程被IO操作阻塞:

"main" prio=5 tid=1 Waiting
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:279)
at com.yourpackage.YourActivity.readFile(YourActivity.java:XX)

解决核心就是把上述阻塞代码移到后台线程执行。

Manifest配置注意事项

  • 若需兼容Android 10及以下版本,可在<application>标签添加android:requestLegacyExternalStorage="true",但Android 11及以上该属性会被系统忽略
  • Android 11及以上版本,文档选择器场景无需申请READ_EXTERNAL_STORAGE权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 17:20:31