摘要:为增强用户隐私与数据安全,加强应用对共享文件访问权限的管理,在Android Q中用户可通过新的运行时权限允许应用访问照片、视频或音频等文件。此外,Android Q规定应用必须通过系统文件选择器才能访问下载文件,这也就意味着,应用的访问权限完全由用户掌控。
接下来,我们将针对Android Q Beta 1测试版本在隐私和安全保护方面新增的特性和变更进行介绍,手把手教您该如何进行适配。由于文章篇幅限制,本文将重点为大家介绍新增特性以及这些变更所带来的兼容性影响。
1.隐私权中存储空间的限制
从Android Q Beta 1开始,此更改具有以下属性:
- 如果您访问和共享外部存储中的文件,则会影响您的应用
- 通过使用隔离的沙箱或媒体收集目录进行缓解
- 通过运行多个ADB命令启用行为
对于每个应用程序,Android Q都会创建一个独立的存储沙箱,限制其他应用程序访问您的应用程序存储在外部存储设备上的文件。常见的外部存储设备是/sdcard
。
该名称具有以下优点:
- 需要更少的权限。应用程序沙箱中的文件对您的应用程序是私有的。因此,您不再需要任何权限来访问和保存自己的文件在外部存储中。
- 相对于设备上的其他应用程序,隐私性更强。没有其他应用可以直接访问您应用的独立存储沙箱中的文件。此访问限制使您的应用程序更容易维护沙盒文件的隐私。
注意:如果用户卸载了您的应用,则会清除隔离存储沙箱中的文件。
在外部存储上存储文件的最佳位置是在返回的位置 Context.getExternalFilesDir()
,因为此位置在所有Android版本中表现一致。使用此方法时,请传入与要创建或打开的文件类型对应的媒体环境。例如,要访问或保存app-private图像,请致电 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
。
如果您的应用创建了属于该用户的文件,并且该用户希望在卸载您的应用时保留该文件,则将其保存到其中一个公共媒体集合中,也称为共享集合。共享集合包括:照片和视频,音乐和下载。
您的应用程序无需请求任何权限即可在这些共享集合中创建和修改自己的文件。但是,如果您的应用需要创建和修改其他应用已创建的文件,则必须首先请求相应的权限:
访问照片和视频共享集合中的其他应用程序文件需要
READ_MEDIA_IMAGES
或READ_MEDIA_VIDEO
权限,具体取决于您的应用程序需要访问的文件类型。访问音乐共享集合中的其他应用程序文件需要获得该
READ_MEDIA_AUDIO
权限。
注意:无权访问Downloads共享集合。您的应用可以访问此集合中自己的文件。但是,要访问此集合中的其他应用程序文件,您必须允许用户使用系统的文件选择器应用程序选择文件。注意:如果您的应用使用存储访问框架,则无需请求这些媒体范围的权限。
详细了解如何使用其他应用的文件。
在请求必要的权限后,您的应用使用MediaStore
API 访问这些集合:
- 对于照片和视频共享集合,请使用
MediaStore.Images
或MediaStore.Video
。 - 对于Music共享集合,请使用
MediaStore.Audio
。 - 对于Downloads共享集合,请使用
MediaStore.Downloads
。
警告:对于Android Q上新安装的应用程序,调用 getExternalStoragePublicDirectory()
仅提供对应用程序存储在其隔离存储沙箱中的文件的访问权限。
要保持对其他应用程序文件的访问权限,请通过以下方式之一更新应用程序的逻辑:
- 使用
MediaStore
并请求与READ_MEDIA_*
您要访问的媒体收藏对应的权限。 - 使用存储访问框架,它不需要任何权限。
要以本机代码访问媒体文件,请使用MediaStore
基于Java或基于Kotlin的代码检索文件,然后将相应的文件描述符传递到本机代码中。有关更多信息,请参阅有关如何从本机代码访问媒体文件的部分。
默认情况下,当用户卸载您的应用时,Android Q会清除您保存到沙箱中的文件。要在卸载应用程序时保留这些文件,请使用存储访问框架,或将文件保存到共享集合。
要保留共享集合中的文件,请在相关MediaStore
集合中插入新行 ,并按以下方式填充其列:
- 至少,为
DISPLAY_NAME
和MIME_TYPE
列提供值。 - (可选)您可以使用
PRIMARY_DIRECTORY
和SECONDARY_DIRECTORY
列来影响文件在磁盘上的放置位置 。 - 保持
DATA
列未定义。这样,该平台可以灵活地将文件保存在沙箱之外。
插入行后,您可以使用API ContentResolver.openFileDescriptor()
来读取或写入新创建的文件的数据。
但是,如果用户稍后重新安装了您的应用,则您的应用无法访问这些文件,除非它执行以下操作之一:
- 请求相应的集合权限。
- 从Storage Access Framework向用户发送请求。
这种情况类似于应用程序尝试访问其他应用程序文件的情况。
Android Q增加了一些增强功能,使用户可以更好地控制在外部存储上访问照片的方式。
一些照片在其Exif元数据中包含位置信息,允许用户查看拍摄照片的位置。由于此位置信息是敏感的,因此默认情况下Android Q会对信息进行编辑。对位置信息的这种限制不同于适用于相机特性的限制。
注意:如果您的应用是用户的默认Photo Manager应用,则平台会自动让您的应用访问照片中的位置信息。
如果您的应用需要访问照片的位置信息,请完成以下步骤:
- 将新
ACCESS_MEDIA_LOCATION
权限添加到应用的清单中。 - 从您的MediaStore对象,调用
setRequireOriginal()
,传入照片的URI。
此过程的示例显示在以下代码段中:
如果您的应用是相机应用,则它无法直接访问照片和视频共享集中保存的照片,除非它是设备的默认Photo Manager应用。要将用户定向到图库应用,请使用 ACTION_REVIEW
意图。
本节介绍了您的应用如何与存储在共享集合中的其他应用程序文件进行交互。
要访问和读取其他应用程序已保存到外部存储设备的媒体文件,请完成以下步骤:
- 根据包含您要访问的文件的共享集合,请求必要的权限。
- 使用
ContentResolver
对象查找并打开文件。
注:本ContentResolver
类包括一个新的方法,loadThumbnail()
即提供您的应用程序与文件的预览。最好先调用,loadThumbnail()
以便用户可以查看媒体文件的快照,而无需您的应用程序自行加载整个文件。此方法还允许更灵活的请求,例如请求特定维度和取消请求的能力。
通过将文件保存到共享集合,您的应用程序将成为该文件的所有者。通常,只有当您是文件所有者时,您的应用才能写入共享集合中的文件。但是,如果您的应用程序充当特定用例的用户默认应用程序,您还可以写入其他应用程序拥有的文件:
- 如果您的应用是用户的默认Photo Manager应用,则可以修改其他应用保存到“ 照片和视频”共享集合中的图像文件。
- 如果您的应用是用户的默认音乐应用,则可以修改其他应用保存到音乐共享收藏集的音频文件。
注意:无论是默认的照片管理器还是音乐应用,您的应用都应保持正常运行。
要修改其他应用最初保存到外部存储设备的媒体文件,请使用ContentResolver
对象查找文件并进行就地修改。执行编辑/修改操作时,请捕获, RecoverableSecurityException
以便您可以请求用户授予您对该特定项目的写入权限。
您可能会遇到以下情况:您的应用需要使用本机代码中的特定媒体文件,例如另一个应用与您的应用共享的文件,或来自用户媒体集的媒体文件。在这些情况下,请在基于Java或基于Koltin的代码中开始媒体文件发现,然后将文件的关联文件描述符传递到本机代码中。
以下代码段显示了如何将媒体对象的文件描述符传递到应用程序的本机代码中:
要了解有关以本机代码访问文件的更多信息,请参阅15:20开始的Android Dev Summit '18中的“ 文件换成话题”。
在某些用例中,您的应用可能需要打开或创建无权访问的文件:
- 在照片编辑应用中,打开一张图纸。
- 在业务生产力应用程序中,将文本文档保存到用户选择的位置。
对于这些情况,请使用存储访问框架,该框架允许用户选择要打开的特定文件,或选择特定位置来保存文件。
如果您管理一套需要相互访问彼此文件的应用程序,请使用content://
URI,我们已将其推荐为安全性最佳实践。
有关更多信息,请参阅有关如何设置文件共享的文档。
访问外部存储中文件的限制仅适用于以Android Q为目标的应用,或者在运行Android Q的设备上新安装的应用。
当满足以下每个条件时,系统会将应用程序的文件访问权限置于兼容模式:
- 您的应用针对Android 9(API级别28)或更低版本。
- 您的应用安装在从Android 9升级到Android Q的设备上。
当您的应用处于兼容模式时,以下文件访问行为适用:
- 您的应用可以访问存储在
MediaStore
集合中的所有文件,甚至是您的应用尚未创建的文件。 - 面向用户的存储权限允许或拒绝您的应用程序作为一个整体访问外部存储,而不是单独的共享集合,如 照片和视频或音乐。
在首次卸载您的应用程序之前,此兼容模式仍然有效。
注意:如果稍后在同一设备上重新安装您的应用,则不会重新激活兼容模式。<devsite-
在Android 9(API级别28)及更低版本中,所有存储设备上的所有文件都显示在单个"external"
卷名称下。Android Q为每个外部存储设备提供唯一的卷名。此命名系统可帮助您有效地组织和索引内容,并使您可以控制新内容的存储位置。
注意:主外部存储设备始终使用卷名称 "external"
。
要唯一标识外部存储中的特定文件,必须同时使用卷名和ID。例如,主存储设备上的文件将是content://media/external/images/media/12
,但是被调用的辅助存储设备上的相应文件FA23-3E92
将是content://media/FA23-3E92/images/media/12
。
您可以通过将此卷名称传递到特定的媒体集合来访问存储在特定卷上的文件,例如MediaStore.Images.getContentUri()
。
要获取所有当前可用卷的名称列表,请调用 MediaStore.getAllVolumeNames()
,如以下代码段所示:
在没有可移动外部存储的设备上,使用以下命令启用虚拟磁盘以进行测试:
为了帮助您使应用程序与此新行为更改兼容,该平台提供了多种方法来调整与更改相关的多个参数。
要在Android Q Beta 1中启用此行为更改,请在终端窗口中执行以下命令:
运行此命令后,设备将重新启动。如果没有,请等一下再尝试再次运行该命令。
要确认行为更改已生效,请使用以下命令:
测试应用程序时,可以 通过在终端窗口中运行以下命令来启用外部文件存储访问的兼容性模式:
要禁用兼容模式,请在Android Q上卸载并重新安装您的应用,或在终端窗口中运行以下命令:
要获得对文件管理器应用程序可能执行的外部存储中的目录的广泛访问,请使用ACTION_OPEN_DOCUMENT_TREE
intent。有关示例,请参阅 GitHub上的 android-DirectorySelection示例。
注意:Android Q中不推荐使用该类中的createAccessIntent()
方法StorageVolume
,因此不应使用此方法浏览外部存储设备。如果您这样做,运行Android Q设备的用户将无法在您的应用中查看保存在外部存储中的文件。