Android Q Labs| Android Q 分区存储

Shared Storage/外部存储区

首先咱们先来明确一下什么是外部存储区?浏览器

外部存储区的英文名字叫 shared storage,也能更直观的反映出外部存储区的概念,它是一块共享的区域,应用写到这个区域的内容,取得 shared storage 权限的其余应用均可以直接访问。 缓存

咱们看到的这些方法,他们所获取内容的位置一般都是在这块外部存储区。除了前三个分别用于获取文件、缓存文件、媒体文件目录的三个方法之外,一般游戏开发者还需去访问**obb **文件目录,能够经过 getObbDirs 的方法来获取,这个目录也是在外部存储区的。安全

Android ap上应用存储区

咱们来回顾一下,在 Android ap 上应用存储区,它大概是一个什么样的状况。网络

应用程序存储区

一方面是应用程序,它都有一块属于本身的专用的内部存储区域,也叫应用私有存储区,全部存储在这个区域里面的内容,其余的应用不管有什么样的权限,都没法去直接访问。 所以这个区域就特别适合被用来保存那些属于应用、用户不须要直接访问的私有数据。app

共享外部存储区

另外一方面咱们还有一块共享的外部存储区,也就是 shared storage。存储在这个区域里的内容,其余的应用能够经过申请权限去访问,用户也能够去直接访问。访问的方法就是将手机经过 USB 数据线链接到电脑,用户就能够在电脑端直接去看到这一块存储的内容。框架

要查看外部存储区,也就是 /sdcard 的目录下的内容应用程序,须要先跟用户申请权限。通常的状况下写到外部存储区的文件,不会被算入应用所占空间大小。可是有一个例外,就是咱们能够看到,在外部存储区里常常会有一些以应用的 package 命名的目录,它是不须要任何的访问权限的。这些目录就包括刚才列举的那些方法所返回的目录。因为这些以应用的 package 命名的目录,它们是具备很是明确的全部权的结构,所以它所归属的应用不须要申请任何权限就能够直接去访问。这个全部权的设置,也就确保了当用户在卸载这款应用的时候,目录里面的文件会被系统清除。 编辑器

而咱们都知道在 Android 系统上,存储是应用程序常用的权限之一。 在Android AP 及 P 之前的版本,咱们看到应用在对于外部存储区的使用上常常会有这样一些问题:它致使的直接结果就是用户常常会发现本身的手机的内存莫名其妙被占满了。ide

Android Q的存储变化

在 Android Q 上咱们但愿经过在明确归属、保护应用程序、保护用户程序这三个方面做出努力,对外部存储区的使用作出优化,从而能提高用户的总体使用体验。post

明确归属

首先是更明确的空间归属,也就是对目录有一个更明确的全部权结构。咱们经过避免在应用程序被卸载以后,还残留在用户磁盘上挥之不去的文件,来解决内存空间使用混乱的问题。 所以在 Android Q 上首先要确保系统可以清楚的知道哪些文件是属于哪些应用程序的,这也使得用户能够更容易地管理他们的文件。固然这也意味着当应用程序被用户卸载的时候,它写到磁盘里边的内容就会被清除。有些应用被用户卸载以后,卸载前写入的一部分数据,用户是但愿在应用卸载以后还能继续去访问的,这一块的内容咱们会在后面有讨论。优化

保护应用程序

第二个方面是增强应用的私有数据的保护。当应用将文件写到外部存储区的时候,有一些状况不但愿其余的应用能够去随意访问。做为应用开发者,一般是不但愿其余应用能随意地去查看这个应用在作什么。在 Android Q 上,咱们经过禁止其余的应用能够随意地查看这部分数据,来保护应用的特有的私有数据。

保护用户程序

第三个方面是增强对用户数据的保护。当用户去下载一些私人聊天中的图片,或者是像银行类 APP 的对帐对帐单 pdf 文件的时候,用户固然不但愿手机上装的其余应用都能去查看这部份内容,从而去收集他的我的隐私信息。在 Android Q 上,咱们确保用户能对应用访问数据访问数据和文件的状况有所掌控,能知道应用程序在什么时间访问了哪些文件内容。

坚持这三点原则,也就意味着咱们正在对 Android 平台的存储方式进行一个很是深入的改变。咱们知道应用可能须要一些稍长的时间来适应这些变化,但咱们也认为从长远来看,它对 Android 平台会更有益处,可以让 Android 成为一个更好的用户平台,也能为用户提供更好的隐私保护。

Android Q 存储空间的访问

接下来,咱们看一下在 Android Q 上存储空间的访问是什么样的?

在 Android Q 上,咱们首先是在 Mediastore 这个方面着重发力。第一个变化就是应用程序如今不须要申请任何权限,就能够向 Mediastore 去提供内容。可是若是应用想要去查看其余应用为 Mediastore 提供的内容则必须获取 Storage 权限。应用程序仍是能够继续读取和写入特有的目录,也就是以它的 package 命名的目录。可是其余应用程序,即使它已经获取了权限,也没法去访问以其余应用的 package 命名的目录。应用程序可使用 system picker 去选择用户磁盘上的任何位置的文件,这个是为应用和用户提供了一种事务型的方法,能够去获取一个或者是多个文件。

咱们认为这种方式来获取文件会更加安全,由于用户会清楚地知道目前是哪一个应用正在访问哪一个或哪些文件。此外在 Android Q 上,私有存储区的工做方式是没有变化的。

MediaStore

Mediastore 是一个属于用户的公用媒体文件集合,它从 Android 发布的第一个版本就已经存在。

以前咱们就提到 Android Q 上的 MediaStore 会有重大改变,下面咱们来看一下 Android Q 上的 Mediastore 是如何工做的。

Android Q 的 Mediastore 是如何工做

Mediastore 被设计用来保存那些属于用户,而不是属于特定某一款应用的,且其它应用程序能够访问的数据内容。那么哪些内容属于 Mediastore?就这与用户的期待和指望有关。咱们有一个整体的把控原则,就是但愿做为应用开发者,须要站在用户的角度去考虑,只把那些你以为用户认为它应该属于 Mediastore 的内容放到 Mediastore 里面,好比说在相机里面拍摄到的照片等这样的数据等等。Mediastore 所能够接受的主要的类型有音频、视频和照片。 应用卸载后,写到 Mediastore 里内容是不会被删除的。可是有一个须要注意的地方:就是应用一旦被卸载以后,若是用户在后续还把这个应用从新安装回来,他去访问以前写到 Mediastore 里的内容的时候,是须要去申请权限的。只有在获取了存储权限以后,才能查看在上一个应用安装的生命周期里写入 Mediastore 的数据。那么哪些内容不属于 Mediastore 仍是与用户的指望有关系。做为开发者,在大多数的状况下,你也是能够很明确地感知到哪些内容不该该被放入到 Mediastore 里的。

咱们举一些例子来讲:好比说你的应用里面会有一些缩略图文件,或者是若是你的应用是一个音乐播放类软件,他极可能会有一些专辑的封面图片这样的文件,或者是你的应用是一个美图类的应用,可能会有一些贴纸这样的资源文件。那么这样这些类型的文件就不适合被放到 Mediastore 里面。由于一般来讲,用户是不会须要在其余的应用里去访问这些资源文件的,也一样是不但愿应用被卸载以后,这些资源文件还残留在用户的内存区域中。

在一些状况下,开发者可能没法肯定某些内容是否应该被放入用户的磁盘空间里,以及放到哪一个具体的位置。

好比说聊天应用当中,用户可能会发送大量的图片文件,若是你直接就把这些文件存到 Mediastore ,就会让 Mediastore 变得很混乱。针对这种状况,咱们建议开发者让用户本身去作出一些主动行为,好比说当用户明确作出保存行为时,才把这个文件保存到用户的磁盘上。

Android Q 上新增 Download 集合

Android Q上新增了一个 Download 集合,用于存储全部不适合被放到的 Mediastore(视频、音频、图片)集合里面的文件。

一个很具体的例子:好比说是银行对帐单这样的 pdf 文件,是会被放到 Mediastore 集合里面的文件里,若是你以为你很但愿把这个文件也放到 Downloads 集合里面,固然也是能够的。 你能够把它放到集合当中来。若是是这种状况的话,这个文件它是会被同步到适当的媒体集合里面的。

权限变动

Mediastore 在提供内容和访问内容的时候,有哪些使用细节?

在往 Mediastore 提供内容,或者是编辑和删除本身放到 Mediastore 到里面的媒体内容时,不用去申请运行时的权限。可是若是你想查看其它的应用为 Mediastore 提供的内容,就必须申请存储权限。你申请权限以后会看到除了 download 集合之外的其余集合里的内容。Mediastore 里面的文件,在应用被用户卸载以后,仍是会存储在用户的手机上。 当一个应用在向 Mediastore 提供内容的时候,直白一点说,就至关于应用在告诉系统:这个文件应该是属于用户的,用户极可能以后会但愿在其余应用程序里也须要去使用这些数据。

如何向 Mediastore 添加内容

如今来看这个代码示例:如何向 Mediastore 添加内容。

首先你是建立一个 contentvalues 的映射。至少须要设置其中的 DISPLAY-NAME 和 MIME-TYPE。这里是 Mediastore 本身去帮你选择存储的默认位置。如有须要也可以使用 relativepath 来设置使用特定的目录。

咱们看到高亮的 IS_PENDING 这一行,这里是把 IS_PENDING 设为 True 的,表示对应的 item 还没准备好,但愿系统可以暂时隐藏,直到准备好。这个是在执行一些运行时间比较长的处理,或者是等待加载的时候会比较有用。只有 item 的全部者程序,能够在项目被标记为 IS_pending 的时候去访问里面的内容。一旦你设置好了这些映射以后,而且肯定了 item 的IS_PENDING 状态,接下来就只须要选择一个集合,而后讲而后将 item 存到里面就能够了。

当你的 item 的状态已经准备好以后,你就能够将 IS_PENDING 标记设置为false,来告诉系统说他们如今已经准备好了,能够供其余的应用来使用了。

变动存储目录

Mediastore 会根据文件的 RELATIVE_PATH 去自动进行分类存储,可是你也能够去设置 MediaColumns 里面的 RELATIVE_PATH,去变动他们在磁盘上的储存位置。系统会尽可能确保你使用的 RELATIVE_PATH 是一个合理的路径。好比说对于 image.jpeg 类型的文件,它就会被放置在DCIM或者 photos 目录。

若是你还想去指定保存到某个特定的存储设备上,你能够经过使用 Mediastore 的get ExternalVolumeNames 或者是 get AllVolumeNames,而后去选择你想要的系统设备上全部的 VolumeNames,把它传递给 getContentUri 的方法。另外还有一个接口是 StorageManager 的 Storage Volume 这个方法。它能够告诉你目前系统连上的全部存储设备的底层信息。

这一页的代码演示的就是如何经过设置 RELATIVE_PATH,将一个音频文件存储到指定的设备及其指定的目录下。

关于 Mediastore 的查询也有一些须要特别注意的地方,因为如今是由系统去决定保存的位置,因此 DATA columns 已经被废弃。 但愿你们使用 ContentResolver.query() 和 ContentResolver.openFileDescriptor() 这两个方法去取代它。咱们在 Q 上有一些兼容性的逻辑来帮助应用适应这种变化。可是这种方案并非特别的完美,因此咱们仍是强烈的建议开发者尽可能去使用咱们在 Q 上提供的 ContentResolver 里面的接口去实现这个变化。

若是你正在查询 Mediastore 而后你发现里面有一些隐藏的,也就是它的 IS_PENDING 标记被设置为 true 的这些项目,而后你又确认你的应用,真的去须要访问这些项目。 咱们如今还有一个接口叫作 setincludePending(),经过调用这个接口,你能够设置获取到这些被标记为 IS_PENDING 的 item,可是即便你能获取到这些 item,你仍是不能去访问里面的具体的数据的,只有这些 item owner 的 app 能够把 IS_PENDING 的标记设置为 false 以后,才能够去访问项目里面的具体内容。若是应用有须要在媒体代码里面去访问这些打开的文件,首先你须要在你的应用的 Java/Kotlin 代码当中打开文件,而后把这个打开的 find script 传递给你的 code。

咱们这一页是一个代码片断,举例说明了如何经过 content 和 resolver 接口的方法来对 Mediastore 进行查询。

编辑文件

Storage 运行时的权限可让应用去发现并读取其余的应用,存入到 Mediastore 里面的媒体文件,可是若是你须要修改这些文件的话,你须要去获取 Right_exstorage 权限。若是你的应用没有获取到必要的权限,就直接调用了update/delete/openFileDescriptor 这些方法,那就会收到系统抛出的异常。

你能够经过 try catch 来捕获这些异常,而且在里面作一些处理,并且咱们很是鼓励开发者这样作。

Metadata

咱们还注意到在照片文件中,一般还会有一个原始的地理位置的信息,这个信息是便于用户在以后查看这些照片的时候,还能回忆起这个照片当时是在什么地方拍摄的。可是地理位置信息对用户来讲一般是比较敏感的我的信息,默认状况下 Android 会隐藏这个信息。若是你的应用须要去访问照片的地理位置信息,它须要在应用里面去声明 ACCESS_MEDIA_LOCATION 权限,而后还须要去调用 Mediastore 的 Save/Require/Origin 接口来指明须要获取某个 url 对应的文件的原始信息。

另外在 Mediatore 里面的 LATITUDE 和 LONGITUDE 这两个常量字段已经被废弃了,若是你须要获取经纬度信息的话,你如今须要使用 ExifInterface 接口来代替。

咱们这里展现的代码片断,演示如何添加 ACCESS_MEDIA_LOCATION 权限,以及如何经过 ExifInterface 接口来获取文件的经纬度信息。

分区存储/Scoped Storage

咱们如今来看一下,在 Android Q 上另外一个引入的新东西叫分区存储,也叫隔离存储沙箱。

隔离存储沙箱可让系统更好地对应用使用的存储空间有一个准确的统计,而且能够去保护应用的程序数据,以及用户的数据安全。

在应用 target Q 时,它就会自动进入分区存储模式,也就是沙箱模式了。这也就意味着应用再也不能够直接访问SD卡的目录。若是你在 target Q 的状况下还去尝试直接访问SD卡,这个目录就会收到报错。虽然不能够直接去访问 SD 卡的根目录,可是仍是能够直去访问以你的应用的 package 命名的目录。 若是想访问在你的 package name 目录之外的其余的地方的内容,你就必须使用 Mediastore 或者是存储访问框架 SAF。

本地、云端 同样处理

存储访问框架容许应用去调出 Android 里面的系统选择器,这个系统选择器会不断升级,不用担忧说之后的版本升级 UI 会发生变动。选择器不只可让用户去选择本地的存储存储位置里面的文件,经过它用户也能够去选择那些存储在云端里面的文件,或者是其余的应用,经过 content provider 来的提供的那些文件。

如何更好的适配

咱们刚才提到过说应用程序它在 Android Q 默认状况下,会被放到沙箱里面。而后咱们认为这样的设置的变动是适用于绝大多数的用户场景,好比说用户若是想选择一些照片上传到社交网络,或者是在文件编辑器中打开和保存文档,或者是在浏览器里下载文件,这些用户场景均可以被很好地处理。然而咱们在发布了 Beta 1 和 Beta 2 这两个版本以后,收到大量的开发者的反馈,广泛是说他们须要更长的一段时间来适配这种变化。因此在今年5月初的 IO 上,咱们也宣布了一个在 Beta 1 和 Beta 2 里面没有的一个新东西。 如今应用能够经过加上一个新的标签来选择退出隔离模式。若是你的应用是 Android P 以及如下的版本时,那么这个标签就默认是 false。固然你也能够手动的将它设置为 true。当你将它设置为 true 的时候,不管应用的它给它SDK target version 是多少,只要应用运行在 Android Q 系统上,就会启用隔离存储沙箱模式。咱们新增的标签只是一个暂时的解决方案,它的目的是给开发者可以有相对来讲更长的一段时间去适配这个变化,而不是说启用了这个标签,你就能够永远的不受隔离存储沙箱的控制。

咱们在明年发布的 Android 下一个大版本中会默认强制开启限制。除了下载集合之外,Mediastore 里面的其余媒体集合是不受隔离存储沙箱模式影响的。若是你的应用处于隔离存储沙箱模式,只能访问它本身向 Mediastore 的 download 集合提供的内容。

咱们这一页展现了新增的 requestLegacyExternalStorage 这个标签,经过将这个标签设置为 true,你能够暂时不受沙箱的控制。固然你也能够经过下面的 isExternalStorageLegacy 这个接口去判断当前你的应用是否受到沙箱的控制。

Android Q Labs 直播专题页面

Android Q Labs 开场演讲

Android Q 有哪些更新

Android Q 现代化您的应用

后台 Activity 启动的限制

Android Q 手势导航

Jetpack 更新

Android Q 在折叠屏设备的适配

通用系统映像介绍

Google Play 商店政策

Android Q 地理位置权限变动

Android Q 深色主题

Android Q Labs 总结演讲