
Android 11(API 30)起,系统默认禁用应用对共享存储(如 /sdcard/Download)的直接读写权限;即使文件真实存在,File.exists() 也会返回 false,需改用 Storage Access Framework(SAF)获取用户授权访问。
android 11(api 30)起,系统默认禁用应用对共享存储(如 `/sdcard/download`)的直接读写权限;即使文件真实存在,`file.exists()` 也会返回 `false`,需改用 storage access framework(saf)获取用户授权访问。
在 Android 开发中,许多开发者会尝试通过 new File("/sdcard/Download/mobilenetv2-10.onnx").exists() 检测用户手动拖入模拟器 Download 目录的模型文件,却发现始终返回 false——这不是路径错误或文件丢失问题,而是 Android 存储权限模型演进导致的系统级限制。
自 Android 11(R,API 级别 30)起,Google 引入了 分区存储(Scoped Storage) 严格策略:应用默认无法通过绝对路径(如 /sdcard/Download/...)直接访问其他应用创建或用户导入的共享文件,即使已声明 READ_EXTERNAL_STORAGE 权限且目标文件物理存在。该限制旨在提升用户数据隐私与安全,File.exists()、File.length() 等操作均会静默失败。
✅ 正确做法:使用 Storage Access Framework(SAF),以用户主动授权为前提访问文件:
// Java 示例(Activity 内)
private ActivityResultLauncher<Intent> openDocumentLauncher;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 配置 SAF 启动器
openDocumentLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Uri uri = result.getData().getData();
try {
// 通过 ContentResolver 安全读取
InputStream is = getContentResolver().openInputStream(uri);
// ✅ 此时可加载 ONNX 模型(如使用 ONNX Runtime)
// 示例:Model model = OrtEnvironment.getEnvironment().loadModel(is);
Log.d("SAF", "File accessed successfully: " + uri);
} catch (Exception e) {
Log.e("SAF", "Failed to open file", e);
}
}
}
);
}
// 触发文件选择(例如点击按钮时)
public void onSelectModelClick(View view) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/octet-stream"); // 或更精确地:"application/vnd.onnx"
// 可选:设置初始目录(Android 11+ 支持)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,
DocumentsContract.buildDocumentUri("com.android.externalstorage.documents",
"primary:Download/mobilenetv2-10.onnx"));
openDocumentLauncher.launch(intent);
}⚠️ 注意事项:
- 不要硬编码路径:/sdcard/Download 在不同设备上可能映射为 /storage/emulated/0/Download,且 Android 10+ 起该路径对应用不可见;
- 避免降级兼容方案:虽可通过 android:requestLegacyExternalStorage="true"(仅适用于 targetSdk
- 权限声明已非必需:SAF 不依赖 READ_EXTERNAL_STORAGE,无需在 AndroidManifest.xml 中声明存储权限;
- 推荐 MIME 类型:对于 .onnx 文件,建议使用 "application/octet-stream"(通用二进制)或注册自定义 MIME 类型,确保系统能正确识别;
- 持久化访问(可选):若需多次访问同一文件,可在首次获取 Uri 后调用 getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION),实现跨进程/重启后的持续访问。
总结:面对 File.exists() 在 Download 目录始终为 false 的问题,本质是 Android 存储模型升级的必然结果。拥抱 SAF 不仅符合平台规范,更能提升应用稳定性与合规性。将文件访问逻辑从“路径直连”转向“用户授权 + URI 流式读取”,是现代 Android 开发的标准实践。










