目录

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向公共目录创建时.

  1. 首先确定要传递到什么类型的媒体表中. 如: MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI
  2. 然后传入具体的MimeType子类型. 如text/plain, image/png, audio/wav
  3. 指定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