AndroidQ沙盒机制之分区存储适配

为了让用户更好地控制自己的文件,Android Q更改了应用访问设备外部存储空间中文件的方式。Android Q用更精细的媒体特定权限来替换READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,并且无需特定权限,应用即可访问自己在外部存储设备的文件。

1、针对应用私有文件的隔离存储沙盒

对于每个应用,Android Q 都会创建一个“隔离存储沙盒”,以限制其他应用访问本应用在外部存储设备的文件。常见的外部存储设备是/sdcard。此定义具有两个优点:

①、需要的权限更少。 应用沙盒中的文件是您应用的私有文件。因此,您不再需要任何权限即可在外部存储设备中访问和保存自己的文件;

②、相对于设备上的其他应用,隐私性更强。 任何其他应用都无法直接访问您应用的隔离存储沙盒中的文件。借助此访问权限限制,您的应用可以更轻松地维护沙盒文件的隐私性;

在外部存储设备存储文件的最佳位置是Context.getExternalFilesDir()返回文件所在的位置,因此此位置的行为方式在所有Android版本中都保持一致。使用此方法时,需要在媒体环境中传递我们要创建或打开的文件类型对应的文件。例如,要保存或访问应用私有图片,请调用Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)。

2、媒体文件的共享集合

如果我们的应用创建了属于相应用户的文件,并希望卸载该应用时保留此用户,则将这些文件保存在某个通用媒体集合(共享集合)中。共享集合包括:照片、音频、视频和下载内容。

3、查看其它应用的文件所需权限

我们的应用无需请求任何权限,即可在这些共享集合中创建和修改自己的文件。但是,我们的应用要创建或修改其他应用已创建的文件,则必须先请求相应权限:

①、访问照片和视频共享集合中其他应用的文件时,需要 READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO 权限(具体取决于您的应用需要访问的文件类型);

②、访问音乐共享集合中其他应用的文件时,需要 READ_MEDIA_AUDIO 权限;

4、访问共享集合

在请求必要的权限后,我们的应用可以使用MediaStore API访问这些集合:

①、对于照片和视频共享集合,请使用 MediaStore.Images 或 MediaStore.Video;

②、对于音乐共享集合,请使用 MediaStore.Audio;

③、对于下载内容共享集合,请使用 MediaStore.Downloads;

要在原生代码中访问媒体文件,请使用基于Java或kotlin代码的MediaStore来检索相应文件,然后对相应文件描述符传递到原生代码。详情请参考从原生代码访问媒体文件部分。

5、保留应用在共享集合的文件

默认情况下,在用户卸载应用时,Android Q会清理保存在沙盒的文件。要在卸载应用时保留这些文件,请使用存储访问框架存储访问框架,或将文件保存在共享集合中。要保留共享集合的文件,请在相关的MediaStore集合中新插一行,并使用以下方法:

①、至少应为 DISPLAY_NAME 和 MIME_TYPE 列提供值;

②、(可选)您可以使用 PRIMARY_DIRECTORY 和 SECONDARY_DIRECTORY 列来影响文件在磁盘上的存储位置;

③、保留 DATA 列不定义。这样一来,平台便可以灵活地将文件保留在沙盒之外;

插入此行后,我们可以使用ContentResolver.openFileDescriptor() 这个API向文件读取或写入数据。

6、访问照片的位置信息

一些照片在Exif元数据中包含位置信息,以便用户查看照片的拍摄地点。由于此位置信息非常敏感,因此默认情况下,Android Q会对此位置信息进行隐藏。如果我们的应用需要访问照片的位置信息,需要调用以下方法:

①、将新的 ACCESS_MEDIA_LOCATION 权限添加到您应用的清单中;

②、在 MediaStore 对象中,调用 setRequireOriginal() 并传入照片的 URI;

Java示例代码如下:

Uri photoUri = Uri.withAppendedPath(
      MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
      cursor.getString(idColumnIndex));

  final double[] latLong;
  if (BuildCompat.isAtLeastQ()) {
    // When running Android Q, get location data from `ExifInterface`.
    photoUri = MediaStore.setRequireOriginal(photoUri);
    InputStream stream = getContentResolver().openInputStream(photoUri);
    if (stream != null) {
      ExifInterface exifInterface = new ExifInterface(stream);
      double[] returnedLatLong = exifInterface.getLatLong();

      // If lat/long is null, fall back to the coordinates (0, 0).
      latLong = returnedLatLong != null ? returnedLatLong : new double[2];

      // Don't reuse the stream associated with {@code ExifInterface}.
      stream.close();
    } else {
      // Failed to load the stream, so return the coordinates (0, 0).
      latLong = new double[2];
    }
  } else {
    // On devices running Android 9 (API level 28) and lower, use the
    // media store columns.
    latLong = new double[]{
        cursor.getFloat(latitudeColumnIndex),
        cursor.getFloat(longitudeColumnIndex)
    };
  }

kotlin示例代码如下:

val latLong = if (BuildCompat.isAtLeastQ()) {
    // When running Android Q, get location data from `ExifInterface`.
    photoUri = MediaStore.setRequireOriginal(photoUri)
    contentResolver.openInputStream(photoUri).use { stream ->
      ExifInterface(stream).run {
        // If lat/long is null, fall back to the coordinates (0, 0).
        latLong ?: doubleArrayOf(0.0, 0.0)
      }
    }
  } else {
    // On devices running Android 9 (API level 28) and lower, use the
    // media store columns.
    doubleArrayOf(
      cursor.getFloat(latitudeColumnIndex).toDouble(),
      cursor.getFloat(longitudeColumnIndex).toDouble()
    )
  }

7、访问其他应用创建的文件

要访问其他应用已保存在外部存储设备的媒体文件,需要以下步骤:

①、根据包含您要访问的文件的共享集合请求必要的权限;

②、使用 ContentResolver 对象查找并打开该文件;

8、向其他应用创建的文件写入数据

通过将文件保存在共享集合,我们的应用成为该文件的所有者。通常情况下,只有是共享集合的某个文件所有者时,我们的应用才可以向文件写入数据。不过,如果我们的应用是用户默认的应用,我们可以向其他应用的文件写入数据:

①、如果您的应用是用户的默认照片管理器应用,则可以修改其他应用保存到照片和视频共享集合中的图片文件;

②、如果您的应用是用户的默认音乐应用,则可以修改其他应用保存到音乐共享集合中的音频文件;

要修改其他应用保存在存储设备的媒体文件,需要使用ContentResolver找到相应文件来修改。

9、标识特定的外部存储设备

在Android 9及以下版本,所有存储设备上的所有文件都会显示单个"external"卷名称。而Android Q为每个外部存储设备提供唯一的卷名称。此命名系统可以帮助我们高效整理内容并且加入索引,还可以控制存储内容的位置。要唯一标识外部存储设备的特定文件,我们需要使用卷名称和ID。例如,主存储设备的文件是content://media/external/images/media/12,而命名为FA23-3E92辅助存储设备对应文件是content://media/FA23-3E92/images/media/12。

10、获取外部存储列表

要获取所有当前可用卷的名称列表,请调用 MediaStore.getAllVolumeNames(),如以下代码段所示:

Set<String> volumeNames = MediaStore.getAllVolumeNames(context);

到此这篇关于AndroidQ沙盒机制之分区存储适配的文章就介绍到这了,更多相关AndroidQ 分区存储适配内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-06-23

AndroidQ(10)分区存储完美适配方法

前言 最近时间在做AndroidQ的适配,截止到今天AndroidQ分区存储适配完成,期间出现很多坑,目前网上的帖子大部分都是概述变更内容,接下来的几篇帖子都是对分区存储实际经验代码总结,填坑经验,特此记录一下,也为大家提供帮助. 本篇主要是对AndroidQ(10)分区存储适配具体实现 要点: Android Q文件存储机制修改成了沙盒模式 APP只能访问自己目录下的文件和公共媒体文件 对于AndroidQ以下,还是使用老的文件存储方式 这里需要注意:在适配AndroidQ的时候还要兼容Q系统

AndroidQ分区存储权限变更及适配的实现

分区存储 在Android Q中引入了分区储存功能,在外部存储设备中为每个应用提供了一个"隔离存储沙盒".其他应用无法直接访问应用的沙盒文件.由于文件是应用的私有文件,不再需要任何权限即可访问和保存自己的文件.此变更并有助于减少应用所需的权限数量,同时保证用户文件的隐私性. 权限变更 Android Q 更改了应用对设备外部存储设备中的文件(如:/sdcard )的访问方式.继续使用 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限,只

解决android6.0以上不能读取外部存储权限的问题

通过内容提供者获取手机sdcard本地视频时, /** * 从本地的sdcard得到数据 * //1.遍历sdcard,后缀名 * //2.从内容提供者里面获取视频 * //3.如果是6.0的系统,动态获取读取sdcard的权限 */ private void getDataFromLocal() { new Thread(){ @Override public void run() { super.run(); // isGrantExternalRW((Activity) context);

关于Android 6.0权限的动态适配详解

前言 Android6.0代号棉花糖.尽管是在15年I/O大会上Google被正式发布的了.但是看看大多数人的项目中大家的 targetSdkVersion 是不是还都用的22.大家都认为6.0+的市场占有率还没那么高.那么就请看谷歌2017年9月份公布的版本分布图. 从数据来看确实没那么高O(∩_∩)O.6.0+的市场占有率仅为50% ̄□ ̄||.只因安卓用户的基数太大了吧.延伸至各种人群.虽然说占比才一半但时基数大总的用户数量还是蛮多的.这两天刚做完6.0权限的适配.那么请说一下自己测试的时候

详解Android运行时权限及APP适配方法

Android 6.0起,Android加强了权限管理,引入运行时权限概念.对于: 1. Android 5.1(API 22)及以前版本,应用权限必须声明在AndroidManifest.xml中,应用在安装时,Android会列出其所需的所有权限供用户确认安装. 2. Android 6.0(API 23)及以后版本,应用权限必须声明在AndroidManifest.xml中,但权限分为普通权限(Normal Permissions)和危险权限(Dangerous Permissions),

Android7.0行为变更之适配File Provider的方法

两个小解释: FileProvider是ContentProvider特殊的子类,ContentProvider通过创建content:// Uri来替代file:/// Uri. 在Android 7.0的以上的系统中,尝试传递file://URI可能会触发FileUriExposedException FileProvider的这个概述包括以下主题: 1.定义FileProvider 2.指定可用文件 3.检索文件的Content URI 4.授予URI的临时权限 5.将内容URI提供给其他

Android 悬浮窗权限各机型各系统适配大全(总结)

这篇博客主要介绍的是 Android 主流各种机型和各种版本的悬浮窗权限适配,但是由于碎片化的问题,所以在适配方面也无法做到完全的主流机型适配,这个需要大家的一起努力,这个博客的名字永远都是一个将来时. 悬浮窗适配 悬浮窗适配有两种方法:第一种是按照正规的流程,如果系统没有赋予 APP 弹出悬浮窗的权限,就先跳转到权限授权界面,等用户打开该权限之后,再去弹出悬浮窗,比如 QQ 等一些主流应用就是这么做得:第二种就是利用系统的漏洞,绕过权限的申请,简单粗暴,这种方法我不是特别建议,但是现在貌似有些

Android 应用适配 Android 7.0 权限要求详解

对于 Android 6.0 以下 , 对于 Android 应用的权限要求是在安装应用的时候默认直接获取:但是对于 Android 6.0 以及以上,对于应用权限的获取具有更高的要求.应用只有获取到了相关权限才可以正常运行,相反如果用户没有赋予相关权限则应用直接退出甚至崩溃,无法进行正常的运行. 具体可以参考下列获取手机定位,手机读写存储和手机状态权限的事例代码: public class AppMainActivity extends AppCompatActivity { @Overrid

Android 10 适配攻略小结

相比较去年写的Android 9适配,这次Android 10的内容有点多.没想到写了我整整两天,吐血中... 准备工作 老规矩,首先将我们项目中的 targetSdkVersion 改为 29. 1.Scoped Storage(分区存储) 说明 在Android 10之前的版本上,我们在做文件的操作时都会申请存储空间的读写权限.但是这些权限完全被滥用,造成的问题就是手机的存储空间中充斥着大量不明作用的文件,并且应用卸载后它也没有删除掉.为了解决这个问题,Android 10 中引入了 Sco

Android10填坑适配指南(实际经验代码)

今天看到一篇好的文章,分享给大家,膜拜大佬. Android10填坑适配指南,包含实际经验代码,绝不照搬翻译文档 1.Region.Op相关异常:java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed 当 targetSdkVersion >= Build.VERSION_CODES.P 时调用 canvas.clipPath(path, Region.Op

深入浅析Yii admin的权限控制

说到CMS,最需要有的东西就是权限控制,特别是一些复杂的场景,多用户,多角色,多部门,子父级查看等等.最近在开发一个线下销售的东东,这个系统分为管理员端,省代端,客户端,门店端,销售端, 部门端,部门老大下面分子部门等等,恶心的需求.我们这个项目使用yii框架开发,yii在php届还是比较流行的,虽然说laravel现在横行,但是一些部门一些团队还是采用了yii框架,比如我们. 我是刚接触yii这个框架,开始的时候对这种面向组件的框架甚是别扭.当时打算自己写权限的,自己创建权限表,关联表等,但是