Android适配 文件存储
文件操作
Android 10 (Q-29)
Scoped Storage 分区存储
限制了应用在外部存储可以肆意创建的能力. 改善公共目录的简洁干净.
文件位置 | 所需权限 | 访问方法 | 卸载时是否移除文件 |
---|---|---|---|
应用目录下 | 无 | getExternalFilesDir() | 是 |
媒体集合(照片,视频,音频) | READ_EXTERNAL_STORAGE(仅当访问其他应用创建的文件时才需要) | MediaStore | 否 |
下载内容(文档和电子书籍) | 无 | 存储访问框架(SAF) | 否 |
使用SAF可以访问表格中的任意位置, 而无需请求权限.
-
包名下的路径可以通过真是路径创建修改.
-
媒体集合, 下载内容等路径只能通过Uri访问
-
getCacheDir() -> /data/user/0/包名/cache
-
getFilesDir() -> /data/user/0/包名/files
-
getExternalCacheDir() -> /storage/emulated/0/Android/data/包名/cache
-
getExternalFilesDir(Sting type) -> /storage/emulated/0/Android/data/包名/files/xxx/
-
getExternalMediaDirs() –> /storage/emulated/0/Android/media/包名/
-
Environment.getExternalStorageDirectory() -> /storage/emulated/0
-
Environment.getDownloadCacheDirectory() -> /data/cache
-
….(以上仅为pixle Android 10输出路径)
适配
强制使用旧存储模式(Legacy View)
(无太大意义, 因为android 11强制性新模式) 如果你能保证只在Android 10设备运行, 并想临时解决存储问题, 可以在清单文件中添加android:requestLegacyExternalStorage=“true”
需要迁移数据
应用的升级安装,还是会用旧模式, 只有首次安装, 或者卸载安装才会启用新模式. 所以可以通过一下代码在升级后判断, 把一些用户数据移动到特定目录下.
// 使用Environment.isExternalStorageLegacy()来检查APP的运行模式
if (Build.VERSION.SDK_INT = Build.VERSION_CODES.Q &&
!Environment.isExternalStorageLegacy()) {
}
首次或者强制卸载安装
- 对于**Environment.getExternalStorageDirectory()存储的文件迁移到getExternalFilesDir()或者getExternalCacheDir()**下
- 也可以使用MediaStore将文件按照图片, 视频, 音频进行分类存储.
// 将图片保存到公共目录下
public static Uri createImageUri(Context context) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.TITLE, "Image.png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
向公共目录添加具有严格的限制, 已pixel android 10为例 MIME_TYPE, RELATIVE_PATH, EXTERNAL_CONTENT_URI 3个字段应该严格的对应.
例如:
场景1:
- **insert()**插入 MediaStore.Images.Media.EXTERNAL_CONTENT_URI
- MIME_TYPE 对应 image/png
RELATIVE_PATH 对应 Download/test
前两行代码表明要插入的一个图片类型到Image媒体库. 但是传入的相对路径头部为Download目录.此时就会抛出异常.
java.lang.IllegalArgumentException: Primary directory Download not allowed for content://media/external/images/media; allowed directories are [DCIM, Pictures]
场景2:
- **insert()**插入 MediaStore.Images.Media.EXTERNAL_CONTENT_URI
MIME_TYPE 对应 text/plain- RELATIVE_PATH 对应 Pictures/test
这处故意传错MimeType, 同时也抛出了异常
MIME type text/plain cannot be inserted into content://media/external/images/media; expected MIME type under image/*
总结: 当我们Media向公共目录创建时.
- 首先确定要传递到什么类型的媒体表中. 如: MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI等
- 然后传入具体的MimeType子类型. 如text/plain, image/png, audio/wav等
- 指定RELATIVE_PATH相对路径. 如果不创建子目录可以直接传入空串. 但是如果想创建子目录**/xxxx/childDir**/ 这个时候. 如果是图片类型那么xxx的值只能为Pictures****DCIM; 如果放入的是下载文件夹那么xxx只能是Download.
- MediaStore.Images : 图片,存储在 DCIM/ 和 Pictures/ 目录中
- MediaStore.Video :视频,存储在 DCIM/、Movies/ 和 Pictures/ 目录中
- MediaStore.Audio :音频,存储在 Alarms/、Audiobooks/、Music/ Notifications/、Podcasts/ 和 Ringtones/ 目录中
- 媒体数据库所在位置:data/data/com.android.providers.media