Android获取文件管理器选中的.txt文件真实路径问题
解决Android API21+ App导入.txt文件时onActivityResult失效及Uri读取问题
看起来你遇到的问题在Android文件导入场景里很常见,我来一步步帮你排查解决:
一、先搞定onActivityResult不触发的问题
首先得确认你的文件选择器启动逻辑和回调处理是否正确,分两种适配情况:
1. 用传统的startActivityForResult(兼容API21-29)
先检查启动文件选择器的Intent是否配置正确:
private static final int REQUEST_CODE_IMPORT = 101; // 启动文件选择器的方法 private void launchTxtPicker() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("text/plain"); // 指定只筛选txt格式文件 intent.addCategory(Intent.CATEGORY_OPENABLE); // 确保选择的是可读取的文件 startActivityForResult(intent, REQUEST_CODE_IMPORT); }
然后检查onActivityResult的实现,一定要调用super方法,否则可能导致回调失效:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // 这行绝对不能少! if (requestCode == REQUEST_CODE_IMPORT && resultCode == RESULT_OK && data != null) { Uri fileUri = data.getData(); if (fileUri != null) { // 拿到Uri后读取文件内容 readTxtContent(fileUri); } } }
2. 推荐用AndroidX的Activity Result API(API21+全兼容,替代弃用的startActivityForResult)
如果你的项目依赖AndroidX,更推荐这种方式,避免API30+的兼容性隐患:
// 先注册回调逻辑 private final ActivityResultLauncher<Intent> importTxtLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK) { Intent data = result.getData(); if (data != null && data.getData() != null) { readTxtContent(data.getData()); } } } ); // 启动文件选择器 private void launchTxtPicker() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("text/plain"); intent.addCategory(Intent.CATEGORY_OPENABLE); importTxtLauncher.launch(intent); }
二、解决Uri读取时的FileNotFoundError
这是最容易踩的坑:文件管理器返回的Uri是content://格式,不是真实的本地文件路径,不能直接用new File(uri.getPath())读取。必须通过ContentResolver读取输入流:
private String readTxtContent(Uri fileUri) { StringBuilder contentBuilder = new StringBuilder(); try (InputStream inputStream = getContentResolver().openInputStream(fileUri); BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream)))) { String line; while ((line = reader.readLine()) != null) { contentBuilder.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); // 给用户反馈读取失败 Toast.makeText(this, "文件读取失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } return contentBuilder.toString().trim(); }
三、权限问题排查(API23+)
如果你的App运行在Android 6.0(API23)及以上,大部分场景下用ACTION_GET_CONTENT选择文件时,不需要自己申请READ_EXTERNAL_STORAGE权限(因为是通过系统文件管理器的Content Provider访问),但为了兼容特殊目录的文件,可以加个权限检查逻辑:
private static final int REQUEST_CODE_STORAGE_PERM = 102; private void checkPermissionAndLaunchPicker() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE_PERM); } else { launchTxtPicker(); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_STORAGE_PERM) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { launchTxtPicker(); } else { Toast.makeText(this, "需要存储权限才能导入文件", Toast.LENGTH_SHORT).show(); } } }
最后总结常见踩坑点
- 忘记在
onActivityResult中调用super.onActivityResult,导致回调不触发 - 直接用
uri.getPath()创建File,忽略了content://Uri的特殊性 - Intent配置错误,比如没加
CATEGORY_OPENABLE或者setType设置不对
内容的提问来源于stack exchange,提问作者Tonher
相关产品推荐
相关产品推荐

