Android应用重装后在三星SM-T800设备出现FileNotFoundException权限问题
嘿,这个问题典型是三星老设备的系统定制坑,我来帮你拆解分析,给你几个可行的解决思路:
先明确核心问题场景
你的应用在首次登录时能正常下载保存图片,但卸载重装后(未手动清除应用存储数据),在三星平板SM-T800(API 23)上触发
java.io.FileNotFoundException,错误提示为open failed: EACCES (Permission denied),发生在执行OutputStream output = new FileOutputStream(filePath);时;清除应用存储数据后恢复正常,其他同API或更高版本设备无此问题。
问题根源分析
API 23(Android 6.0)中,/Android/data/你的包名/files/属于应用专属外部存储目录,理论上不需要申请WRITE_EXTERNAL_STORAGE权限,应用本身就有读写权限。但三星SM-T800的系统在卸载应用时,不会彻底删除这个目录(尤其是用户没主动清数据的情况),残留目录的权限会发生异常——比如旧目录的权限被系统锁定,新安装的应用实例无法写入,这是三星定制系统的特殊行为,其他同API设备没有这个问题。
具体解决方案
方案1:重装后首次启动删除残留目录(推荐)
如果你的应用重装后不需要保留旧数据,最直接的办法是在应用首次启动时,删除整个应用专属外部存储目录,再重新创建:
// 获取应用专属外部存储根目录 File appExternalRoot = getExternalFilesDir(null); if (appExternalRoot != null && appExternalRoot.exists()) { // 递归删除目录下所有内容 deleteDirectory(appExternalRoot); // 重新创建目录 appExternalRoot.mkdirs(); } // 递归删除目录的工具方法 private boolean deleteDirectory(File dir) { if (dir.isDirectory()) { for (File childFile : dir.listFiles()) { deleteDirectory(childFile); } } return dir.delete(); }
这个方法能彻底清除残留的权限异常目录,让新应用实例拥有完全的读写权限。
方案2:修复目标目录权限
如果需要保留部分旧数据,可以在写入文件前,检查目标目录的权限并修复:
// 用官方API获取图片存储目录,不要硬编码路径! File imageDir = new File(getExternalFilesDir(null), "images"); if (imageDir.exists()) { // 修改目录为全局可读写(确保新应用实例能访问) imageDir.setReadable(true, false); imageDir.setWritable(true, false); imageDir.setExecutable(true, false); } else { // 不存在则创建,确保父目录也被创建 imageDir.mkdirs(); } // 然后创建文件输出流 File targetFile = new File(imageDir, "book_118.png"); OutputStream output = new FileOutputStream(targetFile);
注意:永远不要硬编码存储路径,用getExternalFilesDir()获取官方推荐的路径,避免不同设备的挂载差异。
方案3:针对三星设备额外申请存储权限
虽然理论上不需要,但针对三星T800的特殊情况,可以尝试动态申请WRITE_EXTERNAL_STORAGE权限:
- 在
AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
- 在代码中动态申请权限:
final int REQUEST_STORAGE_PERM = 1001; // 检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERM); } // 权限回调处理 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERM && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限通过后执行下载保存操作 downloadAndSaveImage(); } }
额外提醒
优先尝试方案1,因为它从根源解决了残留目录的权限问题,同时代码实现简单,也符合重装应用后清空旧数据的常规逻辑。如果必须保留旧数据,再用方案2或3。
内容的提问来源于stack exchange,提问作者gil cohen

