Android R(Android 11 API 30)于2020年9月9日正式发布,随国内各终端厂商在售Android设备的版本更新升级,应用软件对Android R 版本的兼容适配已迫在眉睫。
对于Android R的新特性,这里按照以下几个方面进行了归纳:`分区存储、权限、隐私、性能、安全`。
对于Android R的新特性,这里按照以下几个方面进行了归纳:<code>分区存储、权限、隐私、性能、安全</code>。
官方文档描述:https://developer.android.google.cn/about/versions/11
从Android 10(API 29)开始,Android<code>默认开启分区存储</code>功能,不过Android 10 可通过增加<code>android:requestLegacyExternalStorage="true"</code>配置<code>停用分区存储</code>;
从Android 11(API 30)开始,<code>强制执行分区存储</code>,对于Android 11及以上设备,<code>android:requestLegacyExternalStorage="true"</code>配置将不再有效。
Android 11 分区存储官方描述:
https://developer.android.google.cn/training/data-storage#scoped-storage
Android 10 默认开启分区存储:
https://xiaxl.blog.csdn.net/article/details/103125117
开启分区存储后,应用默认情况下只能访问<code>应用专属目录(内部存储、外部存储应用专属目录)</code>,以及<code>本应用所创建的特定类型的媒体文件</code>。
应用专属目录
包括<code>内部存储</code>、<code>外部存储专属目录</code>(若应用包名com.xiaxl.demo):
<code>/data/data/com.xiaxl.demo/files,</code>
<code>/sdcard/Android/data/com.xiaxl.demo/files</code>
分别采用以下API进行访问:
<code>File appFile = new File(context.getFilesDir(), filename);</code>
<code>File appExternalFile = new File(context.getExternalFilesDir(), filename);</code>
共享存储目录
包括媒体、文档和其他文件。例如DCIM、Pictures、Movies、Download等目录;
注:
<code>Android 10(Android Q)中共享存储目录使用MediaStore API访问;</code>
<code>Android 11(Android R)中共享存储目录支持MediaStore API与File API访问。</code>
为保证应用在Android 10、Android 11设备中,使用<code>File API对共享存储目录具有相同的文件访问权限</code>。建议在应用 AndroidManifest配置文件中,增加<code>requestLegacyExternalStorage="true"</code>标识,以<code>关闭Android 10设备上的分区存储功能</code>,使<code>分区存储只对Android 11以上设备生效</code>:
应用专属目录(<code>内部存储</code>、<code>外部存储专属目录</code>)的读写,Android 4.4以上设备不需要任何权限;
共享存储路径的读写,需要<code>READ_EXTERNAL_STORAGE</code>与<code>WRITE_EXTERNAL_STORAGE</code> 权限;

Android 11以上设备中,如果您的应用再次请求<code>READ_EXTERNAL_STORAGE</code>权限时,动态权限申请弹窗将变化为<code>“您的应用正在请求访问照片和媒体”</code>。
文件媒体访问 官方描述:
如果需要与其他应用共享单个文件或应用数据,可以使用API:
<code>FileProvider</code>(分享自己的一个或多个文件)
如果应用需要将自己的一个或多个文件提供给其他应用,安全的做法是向接收方应用发送文件的内容 URI,并授予对该 URI 的临时访问权限。
Android <code>FileProvider</code> 组件提供了 <code>getUriForFile()</code> 方法,用于生成文件的内容 <code>URI</code>。
<code>ContentProvider</code>(获取替他应用提供的数据)
如果您需要向其他应用提供数据,可以使用<code>ContentProvider</code>。
<code>ContentProvider</code>是一种标准接口,可将一个进程中的数据与另一个进程中运行的代码进行连。
Android 11 共享文件官方描述:
有一些应用需要获取所有文件的访问权限,例如:文件管理器软件。
获取所有文件的访问权限,可申请<code>MANAGE_EXTERNAL_STORAGE</code>权限。
<code>MANAGE_EXTERNAL_STORAGE</code>相关官方描述:
https://developer.android.google.cn/training/data-storage/manage-all-files
Android 11 中对权限进行了如下更改:
新增 <code>READ_PHONE_NUMBERS</code>权限,获取手机号码;
<code>后台访问位置</code>权限调整;
用户<code>多次针对某项特定的权限请求</code>点<code>拒绝</code>,表示用户希望<code>不再询问</code>;
应用<code>长时间未使用</code>,系统会<code>自动重置用户已授予敏感权限</code>;
针对<code>位置、麦克风、摄像头</code>授权弹窗新增<code>仅限这一次</code>授权按钮;
<code>SYSTEM_ALERT_WINDOW</code> 权限授权方式改变为系统自动授权;
参考 Android 11 权限更新官方文档:
https://developer.android.google.cn/about/versions/11/privacy/permissions#one-time
当应用的 <code>targetSdkVersion>=30</code> 时,使用以下API<code>获取手机号码</code>时,需要申请<code>READ_PHONE_NUMBERS</code>权限,而不再是<code>READ_PHONE_STATE</code> 权限。
<code>TelephonyManager</code> 类和 <code>TelecomManager</code> 类中的 <code>getLine1Number()</code> 方法。
<code>TelephonyManager</code> 类中不受支持的 <code>getMsisdn()</code> 方法。
在Android 10及之前的设备,可以继续使用<code>READ_PHONE_STATE</code>获取手机号;
对Android11及以上设备,需获取<code>READ_PHONE_NUMBERS</code>权限,才能获取手机号;
对于<code>READ_PHONE_STATE</code>权限
Android 10 开始<code>普通应用</code>已经不能再<code>读取设备的硬件ID</code>信息;
相关信息参考 https://xiaxl.blog.csdn.net/article/details/103125117;
Android 11 开始<code>获取手机号</code>相关API更换为<code>READ_PHONE_NUMBERS</code>权限;
<code>READ_PHONE_NUMBERS</code>权限官方API描述:
https://developer.android.google.cn/reference/android/Manifest.permission#READ_PHONE_NUMBERS
在Android10设备上,同时<code>申请前台、后台位置权限</code>时,并在用户选择<code>始终允许</code>后,才能获得后台位置权限。
在Android11设备上,对于<code>targetSdkVersion<=29(Android 10)</code>的应用,同时<code>申请前台、后台位置权限</code>时,对话框不再提示始终允许字样,而是提供了位置权限的设置入口,需要<code>用户在设置页面选择始终允许</code>才能获得后台位置权限。
在Android11设备上,对于<code>targetSdkVersion=30(Android 11)</code>的应用,同时<code>申请前台、后台位置权限</code>时,系统会忽略该请求,无任何响应(<code>需首先获取前台位置权限,再次申请后台位置权限</code>)。
在Android11设备上,对于<code>targetSdkVersion=30(Android 11)</code>的应用,<code>先申请前台位置权限,后申请后台位置权限</code>。
后台访问位置权限 官方描述:
https://developer.android.google.cn/training/location/background
Android11设备上,targetSdkVersion=30的应用,申请后台位置权限,直接跳转到设置页面。
在 Android 11 中,用户<code>多次针对某项特定的权限请求</code>点击了<code>拒绝</code>,那么应用再次请求该项权限时,用户将不会看到系统权限弹窗,该操作表示用户希望<code>不再询问</code>;
在 Android 11 中,当targetSdkVersion>=30时,<code>应用在一段时间内未使用</code>,系统会通过<code>自动重置用户已授予应用的运行时敏感权限</code>来保护用户数据;
从 Android 11(API 级别 30)开始,当应用请求与<code>位置、麦克风、摄像头</code>相关权限时,面向用户的授权对话框会包含<code>仅限这一次</code>选项;如果用户在对话框中选择<code>仅限这一次</code>,系统会向应用授予临时的单次授权。
权限申请API使用方式不变:
源码参考:
https://github.com/android/permissions-samples/tree/main/RuntimePermissionsBasic;
在 Android 11 中,<code>SYSTEM_ALERT_WINDOW</code> 权限授权方式更改为:<code>根据请求自动向某些应用授予 SYSTEM_ALERT_WINDOW 权限</code>。
系统会自动向具有 <code>ROLE_CALL_SCREENING</code> 且请求 <code>SYSTEM_ALERT_WINDOW</code> 的所有应用授予该权限。如果应用失去 <code>ROLE_CALL_SCREENING</code>,就会失去该权限。
<code>ROLE_CALL_SCREENING</code>为<code>RoleManager</code>中的常量类,多用于通知用户将我们的应用替换掉手机自带的预搭载应用(短信、电话拨号);
系统会自动向通过 <code>MediaProjection</code> 截取屏幕且请求 <code>SYSTEM_ALERT_WINDOW</code> 的所有应用授予该权限,除非用户已明确拒绝向应用授予该权限。当应用停止截取屏幕时,就会失去该权限。此用例主要用于游戏直播应用。
SYSTEM_ALERT_WINDOW权限 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/permissions#system-alert
主要更改涉及以下几个方面:
软件包可见性:获取其他应用信息需在<code>AndroidManifest</code>中增加<code><queries></code>标签;
前台服务:访问位置信息、摄像头、麦克风限制;
永久 SIM 卡标识符 ICCID 获取受限;
新增<code>AppOpsManager.OnOpNotedCallback</code>监听危险权限的调用,从而保护用户的私密数据;
这样对于第三方依赖库的权限使用申请可以做一个监控
在 Android 11 及更高版本设备中,当应用的 <code>targetSdkVersion>=30</code> 时,如果应用希望获取其他应用的信息(比如:包名、软件名称),原有方式将无法获取到。
如需获取其他应用信息,需要在<code>AndroidManifest</code>中增加<code><queries></code>元素标签,告知系统希望获取哪些应用的信息或者哪一类应用的信息。
如果需要获取所有应用的信息(比如:Launcher应用、设备管理器应用):这种情况只需要在<code>AndroidManifest</code>中添加<code>QUERY_ALL_PACKAGES</code>权限即可。
<code>QUERY_ALL_PACKAGES</code>权限为普通权限,不需要进行动态申请。但提交应用市场后,应用市场可能会进行审核
软件包可见性 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/package-visibility
当应用的 <code>targetSdkVersion>=30</code> 时,<code>前台服务</code>访问<code>位置信息、摄像头、麦克风</code>时,需添加<code>foregroundServiceType</code>。
前台服务 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/foreground-services
在 Android 11 及更高版本中,使用 <code>SubscriptionInfo.getIccId()</code> 方法访问不可重置的 ICCID 受到限制。
<code>SubscriptionInfo.getIccId()</code> 方法会返回一个<code>非null的空字符串</code>。
如需唯一标识设备上安装的 SIM 卡,请改用 <code>getSubscriptionId()</code> 方法。<code>SubscriptionId</code>会提供一个索引值,用于唯一识别已安装的 SIM 卡(包括实体 SIM 卡和电子 SIM 卡),除非设备恢复出厂设置,否则此标识符的值对于给定 SIM 卡是保持不变的。
Android 11新增<code>AppOpsManager.OnOpNotedCallback</code>为开发者提供<code>对应用危险权限的使用监听,从而保护用户的私密数据</code>。
当应用以及应用的依赖包中,申请某项危险权限时,<code>AppOpsManager.OnOpNotedCallback</code>的对应回调方法将会被调用,从而<code>打印申请的权限</code>与<code>对应的API调用栈</code>。
举例:
<code>使用位置权限获取位置信息</code>时,将会回调<code>AppOpsManager.OnOpNotedCallback</code>中的<code>onNoted</code>方法,并打印<code>使用的权限</code>与<code>对应的API调用栈</code>。
打印日志如下:
从以上日志可以看出,当应用申请<code>ACCESS_COARSE_LOCATION</code>权限并<code>获取位置信息时</code>,打印了应用<code>申请的权限</code>与<code>对应的API调用栈</code>。
AppOpsManager 相关官方描述:
https://developer.android.google.cn/guide/topics/data/audit-access#audit-by-attribution-tag
JobScheduler使用频率进行限制
Android 11 为对<code>JobScheduler</code>使用频率进行一定限制。
对于 debuggable 清单属性设置为 true 的应用,过多的调用 <code>JobScheduler</code> API 将返回 <code>RESULT_FAILURE</code>。
<code>JobScheduler</code>主要用于在未来某个时间下满足一定条件时触发执行某项任务,例如:<code>当设备在空闲状态, 并且使用wifi时, 自动下载Apk</code>。
<code>JobScheduler</code>典型的使用举例如下:
官方描述参考:
https://developer.android.google.cn/about/versions/11/behavior-changes-all
官方Demo参考:
https://github.com/googlearchive/android-JobScheduler
非 SDK 接口限制
官方从 Android 9(API 级别 28)开始,对应用使用的非 SDK 接口实施了限制。
如果你的APP通过引用<code>非 SDK 接口</code>或尝试<code>使用反射或 JNI 来获取句柄</code>,这些限制就会起作用。官方给出的解释是为了<code>提升用户体验、降低应用崩溃风险</code>。
官方给出了一个检测工具,下载地址:veridex
veridex使用方法:
以上截图中,blacklist、greylist、greylist-max-o、greylist-max-p含义如下:
blacklist 黑名单:禁止使用的非SDK接口,运行时直接Crash(因此必须解决)
greylist 灰名单:即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口
greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被禁止使用的非SDK接口
greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被禁止使用的非SDK接口
非SDK接口限制 官方描述:
https://developer.android.google.cn/about/versions/11/non-sdk-11