安卓系统对存储权限的管理随着版本迭代逐步收紧,主要分为以下阶段:
Android 6.0 (API 23) 前
READ_EXTERNAL_STORAGE
和 WRITE_EXTERNAL_STORAGE
控制全部外部存储访问 Android 6.0 Android 9 (API 23-28)
READ_EXTERNAL_STORAGE
权限 WRITE_EXTERNAL_STORAGE
Android 10 (API 29) 起
Storage Access Framework
或 MediaStore
Android 11 (API 30) 起
REQUEST_INSTALL_PACKAGES
权限 存储类型 | 路径示例 | 访问特性 |
---|---|---|
应用私有存储 | /data/data/包名/ |
无需声明权限,仅本应用可读写 |
应用外部存储 | /storage/emulated/0/Android/data/包名/ |
需申请存储权限,不同安卓版本访问规则不同 |
系统公共目录 | /storage/emulated/0/Download/ |
Android 10+ 需通过 SAF 访问,旧版本可直接读写(需权限) |
缓存目录 | /storage/emulated/0/Android/cache/ |
系统可自动清理,需存储权限 |
媒体库目录 | /storage/emulated/0/Music/ |
Android 10+ 必须通过 MediaStore API 操作,禁止直接文件访问 |
Manifest声明权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
运行时动态申请
// 检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); }
处理权限回调
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限授予成功 } else { // 权限被拒绝 } }
方法 | 适用场景 | Android 10+ 限制 |
---|---|---|
传统File API | 访问应用私有目录、旧版外部存储操作 | 禁止直接访问公共目录,需迁移至 SAF/MediaStore |
Storage Access Framework | 跨应用文件访问、用户手动选择文件 | 推荐方式,通过 Intent.ACTION_OPEN_DOCUMENT 启动 |
MediaStore API | 操作图片/视频/音频等媒体文件 | 强制使用,替代直接文件路径操作 |
ContentResolver | 访问数据库、系统级数据 | 需配合Uri权限使用 |
问题1:Android 10+ 应用无法读取外部文件
registerForActivityResult().launch(new OpenDocumentTreeRequest())
ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "test.jpg"); Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
问题2:请求存储权限被系统自动拒绝
<uses-permission android:name="..."/>
并处理 onRequestPermissionsResult
WRITE_EXTERNAL_STORAGE
,改用 SAF/MediaStore Q1:如何判断设备是否支持外部存储?
A:可通过 Context.getExternalFilesDir(null)
检测返回值是否为 null
,若为 null
,则设备可能无外部存储或存储不可用,代码示例:
File externalFilesDir = getExternalFilesDir(null); boolean hasExternalStorage = externalFilesDir != null;
Q2:如何处理不同安卓版本的文件路径兼容性?
A:采用以下策略:
getFilesDir()
/getCacheDir()
,无需处理权限 Environment.getExternalStorageDirectory()
+ 权限检查