前言

最近一直在致力于AndroidQ的适配。截至今日,AndroidQ分区存储适配已完成。过程中遇到了很多坑。当前互联网上的大多数帖子都概述了这些变化。接下来的几篇文章是关于分区存储的。特此记录一下实际经验代码和填坑经验的总结,为大家提供帮助。

本文主要是AndroidQ(10)分区存储适配的具体实现

  • 积分:
  • Android Q 文件存储机制已修改为沙盒模式
  • APP只能访问自己目录下的文件和公共媒体文件
  • Android Q及以下版本,仍使用旧的文件存储方式

这里注意:适配Android Q时,还必须兼容Q系统版本及以下,使用SDK_VERSION来区分

背景

存储权限

Android Q 仍然使用 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 作为存储相关的运行时权限,但现在即使获得这些权限,对外部存储的访问也受到限制,只能访问自己目录下的文件和公共内部文件。

外部存储结构划分

公共目录:下载、文档、图片、DCIM、电影、音乐、铃声等

地址:/storage/emulated/0/下载(图片)等

公共目录中的文件在卸载APP时不会被删除。

APP私人目录

地址:/storage/emulated/0/Android/data/包名/文件

私有目录存放应用程序的私有文件,卸载应用程序时会被删除。

适应指导

在AndroidQ中使用ContentResolver来添加、删除、修改和检查文件

1。获取(创建)自己目录中的文件夹

获取并创建,如果手机中没有对应的文件夹,系统会自动生成

//在自己的目录下创建apk文件夹文件 apkFile = context.getExternalFilesDir("apk");

2。在自己的目录中创建文件

生成要下载的路径,通过输入输出流进行读写

String apkFilePath = context.getExternalFilesDir("apk").getAbsolutePath();
File newFile = new File(apkFilePath + File.separator + "temp.apk");
输出流 os = null;
尝试 {
  os = new FileOutputStream(newFile);
  if (os != null) {
    os.write("文件已创建".getBytes(StandardCharsets.UTF_8));
    os.flush();
  }
} catch (IOException e) {
} 最后 {
  尝试 {
    if (os != null) {
      os.close();
    }
  } catch (IOException e1) {

  }
}

3。在公共目录下创建文件夹

通过 MediaStore.insert 写入

如果(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
  返回空值;
}
ContentResolver 解析器 = context.getContentResolver();
ContentValues值= new ContentValues();
value.put(MediaStore.Downloads.DISPLAY_NAME,文件名);
value.put(MediaStore.Downloads.DESCRIPTION,文件名);
//设置文件类型value.put(MediaStore.Downloads.MIME_TYPE,"application/vnd.android.package-archive");
//注意MediaStore.Downloads.RELATIVE_PATH需要targetVersion=29, //所以该方法只能在Android10手机上执行
value.put(MediaStore.Downloads.RELATIVE_PATH,"下载" + File.separator + "apk");
Uri 外部 = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
Uri insertUri =solver.insert(external,values);
返回插入Uri;

4。在公共目录下指定文件夹中创建文件

结合上面的代码,我们主要是在public目录下创建文件或者文件夹来获取本地路径uri。不同的Uri可以保存在不同的公共目录中。接下来就可以使用输入输出流来写入文件了

重要:AndroidQ不支持file://类型访问文件,只能通过uri访问

ContentResolver 解析器 = context.getContentResolver();
Uri insertUri =solver.insert(external,values);
if(insertUri == null) {
  返回;
}
String mFilePath = insertUri.toString();
输入流 = null;
输出流 os = null;
尝试 {
  os =solver.openOutputStream(insertUri);
  如果(操作系统==空){
    返回;
  }
  整型读取;
  文件sourceFile = new File(sourcePath);
  if (sourceFile.exists()) { // 当文件存在时是 = new FileInputStream(sourceFile); // 读取原始文件
    字节[]缓冲区=新字节[1024];
    while ((read = www.sxzhongrui.com(buffer)) != -1) {
      os.write(缓冲区,读取);
    }
  }
} catch (异常 e) {
  e.printStackTrace();
}最后 {
  尝试 {
    如果(是!=空){
      is.close();
    }
    if (os != null) {
      os.close();
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

5。通过MediaStore读取public目录下的文件

ParcelFileDescriptor ParcelFileDescriptor = null;
文件描述符 文件描述符 = null;
位图 tagBitmap = null;
尝试 {
  ParcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri,"r");

  if (parcelFileDescriptor!= null && ParcelFileDescriptor.getFileDescriptor() != null) {
    fileDescriptor = ParcelFileDescriptor.getFileDescriptor();
    //将uri转换为bitmap类型
    tagBitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
  }
} catch (FileNotFoundException e) {
  e.printStackTrace();} catch (IOException e) {
  e.printStackTrace();
} 最后 {
  尝试 {
    if (parcelFileDescriptor!= null) {
      ParcelFileDescriptor.close();
    }
  } catch (IOException e) {
  }
}

6。使用MediaStore删除文件

context.getContentResolver().delete(fileUri,null,null);

7。 APP通过MediaStore访问文件所需权限

标题 1 不允许 READ_EXTERNAL
音频 可以读写APP本身创建的文件,但不能直接使用路径访问 您可以阅读其他APP创建的媒体文件。删除、修改操作需要用户授权
图片 可以读写APP本身创建的文件,但不能直接使用路径访问 您可以阅读其他APP创建的媒体文件。删除、修改操作需要用户授权
文件 可以读写APP本身创建的文件,但不能直接使用路径访问 无法读取或写入其他应用程序创建的非媒体文件
下载 可以读写APP本身创建的文件,但不能直接使用路径访问 无法读取或写入其他应用程序创建的非媒体文件

后续我们会介绍AndroidQ存储的具体功能,敬请关注~