Android本地存储文件读取与分享异常:部分文件无法附加至Gmail等服务的求助
我来帮你捋捋这个偶发的文件无法附加问题,从你的代码和日志来看,大概率是这几个地方出了问题,咱们一个个排查:
1. 文件状态不稳定,导致listFiles()读不全文件
从你的日志能看到,有时候member_add.json会突然从文件列表里消失,这很可能是因为这个文件正在被写入或者还没完成系统同步,此时listFiles()会跳过它。另外,外部存储的文件可能因为系统扫描、其他应用操作等原因,暂时处于不可读取的状态。
解决方案:遍历前过滤可读取的文件
在获取文件列表时,添加过滤器只保留可正常读取的文件,避免把无效文件加入附件:
try { File root = Environment.getExternalStorageDirectory(); File directory = new File(root.getAbsolutePath() + "/logs"); // 添加过滤器,只接受可读取的文件 File[] files = directory.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.isFile() && pathname.exists() && pathname.canRead(); } }); attachments.clear(); if (files != null && files.length > 0) { Log.e("Report:", "-------------for loop start--------"); for (File value : files) { Log.e("Report:", value.getName()); Uri uri = FileProvider.getUriForFile(getContext(), "bd.com.cmed.pksf.provider", value); attachments.add(uri); } Log.e("Report:", "-------------for loop end--------"); } } catch (Exception e) { Log.e("Report:", e.getMessage()); }
2. FileProvider权限未正确授予给邮件应用
虽然你在Manifest里设置了grantUriPermissions="true",但如果启动邮件Intent时没有显式授予读取权限,Gmail等应用可能无法访问对应的Uri,导致文件无法附加。
解决方案:启动Intent时添加权限标记
在创建发送邮件的Intent时,一定要添加FLAG_GRANT_READ_URI_PERMISSION,确保目标应用能读取Uri:
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); emailIntent.setType("application/json"); emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); // 授予临时读取权限给接收Intent的应用 emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 可选:指定Gmail包名,避免权限泄漏给其他应用(Android 12+建议) emailIntent.setPackage("com.google.android.gm"); startActivity(Intent.createChooser(emailIntent, "分享日志文件"));
3. 外部存储路径的兼容性问题(Android 10+)
Environment.getExternalStorageDirectory()在Android 10(API 29)及以上已经被废弃,而且外部根目录的文件访问权限限制越来越严格,可能导致文件读取不稳定。如果这些日志文件是你的应用生成的,强烈建议改用应用专属外部存储目录。
解决方案:迁移到应用专属存储
把日志文件放在getExternalFilesDir()目录下,这里不需要READ/WRITE_EXTERNAL_STORAGE权限,也不会被系统或其他应用干扰:
// 改用应用专属外部存储的logs目录 File directory = new File(getContext().getExternalFilesDir(null), "logs"); // 如果目录不存在就创建 if (!directory.exists()) { directory.mkdirs(); } // 后续的文件读取逻辑和之前一致
同时,你需要更新provider_paths.xml,确保能访问这个目录:
<paths> <external-files-path name="external_files" path="."/> </paths>
4. 系统目录列表缓存问题
有时候系统会缓存目录的文件列表,导致listFiles()返回的不是最新状态。可以改用Java NIO的Files.list()来获取实时的文件列表,它能绕过系统缓存。
解决方案:用Files.list()替代listFiles()
try { File directory = new File(getContext().getExternalFilesDir(null), "logs"); if (!directory.exists()) { directory.mkdirs(); } // 用Java NIO获取实时文件列表 try (Stream<Path> pathStream = Files.list(directory.toPath())) { attachments.clear(); Log.e("Report:", "-------------for loop start--------"); pathStream.forEach(path -> { File file = path.toFile(); if (file.isFile() && file.canRead()) { Log.e("Report:", file.getName()); Uri uri = FileProvider.getUriForFile(getContext(), "bd.com.cmed.pksf.provider", file); attachments.add(uri); } }); Log.e("Report:", "-------------for loop end--------"); } } catch (Exception e) { Log.e("Report:", e.getMessage()); }
总结
综合来看,你可以先尝试添加文件可读性过滤和在Intent中添加权限标记这两个改动,这两个是最常见的导致偶发附件失败的原因。如果问题还存在,再考虑迁移到应用专属存储,这样能从根源上解决外部存储的权限和兼容性问题。
内容的提问来源于stack exchange,提问作者Ariful Haque

