个推技术分享 | 您有一份安卓12适配攻略,请注意查收!
10 月 4 日,谷歌将Android12源代码推送至 Android 开源项目 (AOSP)。自从2021年2月发布Android12第一个预览版以来,历经9个月时间测试和优化,正式版本的Android12终于来了!不仅在UI方面做了不少升级,Android12对个人隐私安全的保护也得到了进一步增强。整体来讲,Android12更加智能、高效和安全,感兴趣的开发者可以登录官网下载源码测试学习。
个推服务开发者多年,打磨SDK产品的同时,一直密切关注和跟进行业发展趋势。Android12稳定版发布后,我们使用模拟器进行了研究和适配测试。本文将从安全变更、权限变化、性能更新等方面来谈谈 Android12 新特性,以帮助开发者更快速、更便捷地上手适配Android新系统。
安全变更
01 更安全的组件导出
从事Android开发的同学都知道,Android有四大组件,分别是活动(Activity)、服务(Service)、广播接收器(Broadcast Receive)和内容提供器(Content Provider)。Activity组件为用户提供可视化操作界面;服务组件在后台运行,支撑各类功能的实现;广播接收器顾名思义主要用于接受各种广播;内容提供器主要支持多个应用中存储和读取数据,相当于一个数据库。
这四大组件赋予了App各种各样丰富的功能,因此无论是对App还是用户来讲,它们的安全性都至关重要。在App开发过程中,会有一些特定需求使用到第三方SDK,如支付、消息推送等,这些都会涉及到组件导出的问题。为保护隐私以及改善整体用户体验,Android12对组件的导出有了更严格的要求。
使用Android12的开发者需要关注,如果您对四个组件配置了intent 过滤器,则务必要在代码中显式声明android:exported 属性。如果未设置该属性,那应用将无法安装在 Android12 上。
android:exported 属性声明代码示例:
<service android:name="com.example.app.backgroundService" android:exported="false"> <intent-filter> <action android:name="com.example.app.START_BACKGROUND" /> </intent-filter> </service>
02 PendingIntent
PendingIntent是一种特殊的Intent,和Intent的区别在于Intent是立刻执行的,而PendingIntent不是,可以被理解为一种异步处理机制。PendingIntent执行的操作实质上是参数传进来的Intent操作,像通知栏消息的发送就是使用PendingIntent实现。
为了提升应用的安全性,Android12新特性要求应用创建的每个PendingIntent对象都要指定可变性, 使用PendingIntent.FLAG_MUTABLE 或PendingIntent.FLAG_IMMUTABLE标志。
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
如果不设置任一可变性标志, 系统将会抛出IllegalArgumentException异常,报错内容如下:
java.lang.IllegalArgumentException: XXX: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
03 不安全的intent启动
而对于一般的intent而言,开发者如何确保其安全性呢?Android12为开发者们提供了一种调试功能,如果应用以不安全的方式启动intent,此功能将会发出警告。具体实现也比较简单,开发者在application中添加以下代码即可:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch().penaltyLog() // Consider also adding penaltyDeath() .build()); }
这里要非常注意嵌套intent。嵌套intent是在其他intent中作为extra传递的intent。如果有以下行为,系统将发生StrictMode违规警告:
①从intent的extra中解析提取嵌套intent。
②使用该嵌套intent启动应用组件,例如将intent传递给startActivity()。
关于嵌套 intent 安全问题, 这篇文章写得比较详细,感兴趣的开发者可以深入阅读:https://blog.csdn.net/m0_57546986/article/details/116078533
隐私保护
为了更好地保障用户的个人信息,Android12引入了大致位置选项、应用休眠、ADB备份限制等新的隐私功能。
01 大致位置选项
Android12之前,用户在授予位置信息访问权限时只能允许系统层面的设置,如果想要更改某一特定应用的位置权限,则需要找到相应的设置界面进行手动处理。为了更好地保护用户隐私,Android12引入了“大致位置”选项。当应用需要访问位置权限时,弹窗将会出现“确切位置”和“大致位置”两个选项供用户进行授权:
- 确切位置,通常精确到几米之内。
- 大致(粗略)位置,一般为几百米。
位置信息一共有三种授权方式:仅在使用该应用时允许、仅限这一次和不允许。个推对这些授权方式进行了实测:
①如果Manifest 配置了
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED { requestPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION }, 1); } }
在前端界面将会出现弹框如下:
②Android12要求,必须同时申请大致(粗略)位置和确切位置的权限,才能完成ACCESS_FINE_LOCATION 确切位置权限的授权。所以,如果Manifest没有配置<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" />但是请求Manifest.permission.ACCESS_FINE_LOCATION 弹框, 则前端界面会弹出和 ① 情况一致的弹框,即“大致(粗略)位置弹框”。
③如果Manifest配置了
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1); }} // 和以下代码调用 , 弹框一致requestPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, 1); @Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) }}
在前端界面将会出现弹框如下:
④ 在第③种情况下,根据用户的选择,会出现以下几种情况:
如果用户选择了“确切位置”, 同时选择了“仅在使用该应用时允许”或者“仅限这⼀次”,onRequestPermissionsResult 参数int[] grantResults 两个返回值均为0,表示已授权,应用重启后权限将仍保持授权状态。
★ 也就是说,“仅限这一次”并非严格意义上的仅限这一次授权。对当前应用完成授权后,下次重启该应用将仍是已授权状态。
如果位置选择了“大致位置”, 同时选择了“仅限这⼀次”, onRequestPermissionsResult参数int[] grantResults返回值为0 和-1,意味着此次“大致位置”已经授权, 但“确切位置”仍未授权。下次启动再次调用requestPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1), 则弹框如下: 从“大致位置”改为“确切位置”弹框询问,此时“大致位置”已默认授权。
如果用户选择 “继续使用大致位置”, 则int[] grantResults 返回值仍为0和-1 , 下次请求还会出现同样弹框内容。
如果选择“仅限这一次”, 则int[] grantResults返回值为0和0 ,下次启动应用时,两个位置权限将仍保持授权状态。
以上测试总结如下:
关于权限申请这块,Google给我们提供了很好的示例, 以下代码, 感兴趣的开发者可以根据“位置”选择和“选项列表”选择分别调试看看效果。更多关于Android12的位置权限变更内容,可参考:https://developer.android.com/about/versions/12/approximate-location。
ActivityResultLauncher<String[]> locationPermissionRequest = registerForActivityResult(new ActivityResultContracts .RequestMultiplePermissions(), result -> { Boolean fineLocationGranted = result.getOrDefault( Manifest.permission.ACCESS_FINE_LOCATION, false); Boolean coarseLocationGranted = result.getOrDefault( Manifest.permission.ACCESS_COARSE_LOCATION,false); if (fineLocationGranted != null && fineLocationGranted) { // Precise location access granted. } else if (coarseLocationGranted != null && coarseLocationGranted) { // Only approximate location access granted. } else { // No location access granted. } } ); locationPermissionRequest.launch(new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION});
★ Android12的位置权限变更对旅游出行、地图导航、酒店预订等App影响较大。尤其是在“个保法”正式实施的背景下,App开发者更要关注系统和相关法律法规对位置信息授权方式的要求,做好个人信息安全的有效保护。比如,App需通过弹窗等显著的方式向用户申请个人信息的授权,在用户隐私条款中明确相关信息的用途、保存方式、处理方式等。
02 应用休眠
此外,Android12还对Android11 “自动撤销权限”功能进行了升级,引入了“应用休眠”功能。App自动休眠功能代码由谷歌于今年一月份在AOSP系统项目中提交。这项功能可以让用户自行标记出一个应用列表,受标记的应用如果几个月未被使用,则系统会自动取消其权限、停止各种后台通知,将该应用置于休眠状态,以省电并移除其占有的应用空间。Android12的该功能不仅有助于释放手机存储空间,还将极大提升用户体验。
★对于用户交互频率较低的特定类型应用而言,开发者可以调用包含 Intent.ACTION_APPLICATION_DETAILS_SETTINGS intent 操作的intent,向用户发送请求,让其准许应用免于休眠和自动重置权限限制。
03 DB 备份限制
同时,为了帮助保护私有应用数据,Android12还更改了adb backup命令的默认行为。对于以Android12为目标平台的应用,当运行adb backup命令时,从设备导出的其他任何系统数据都不会包含应用的数据。
如果测试或开发工作流程依赖于使用adb backup的应用数据,则可以选择在AndroidManifest文件中将android:debuggable设置为true来导出应用数据。注意,release线上版本务必将android:debuggable设置为false。
性能更新
性能优化是Android系统每次版本更新的重点内容。Android12改进了应用程序启动时间并优化了I/O,以加快应用程序加载速度。同时,为改进用户体验,Android12对通知trampoline、前台服务启动以及闹钟权限等均进行了限制。
01 通知trampoline限制
当用户点击通知后,会启动一个组件来响应用户的点击操作, 一般最终会打开一个界面。这个界面组件就是通知trampoline。
考虑⼀种情形, 如果通知构建的PendingIntent使用了PendingIntent.getService或者 PendingIntent.getBroadcast, 那么,当打开通知时后台可能需要做一些耗时操作或者请求网络等, 等处理完成之后再跳转到目标页面。但有时候网络、耗时操作等各种原因会造成等候时间较长, 进而导致界面弹出较晚, 以致几秒后才跳转出⼀个页面, 这种体验相对而言比较差。
为了改进用户体验,以Android12为目标平台的应用无法从用作通知 trampoline的服务或者广播中启动activity,也就是说应用构建的通知 setContentIntent()参数必须是 PendingIntent.getActivity。
个推对该功能进行了测试:
创建通知, setContentIntent() PendingIntent参数使用getService或者PendingIntent.getBroadcast构建 PendingIntent;
通知触发的目标组件, 也就是点击通知后启动的Service或者Broadcast, 启动某个Activity;
弹出通知后, 应用切换到后台, 此时再点击通知, 会报以下错误, 且最终页面无法被启动。
system_process E/NotificationService: Indirect notification activity start (trampoline) from com.gt.brand.push.tst1 blocked...system_process E/ActivityTaskManager: Abort background activity starts from 10146
对于大部分App开发者来讲,实现通知trampoline的兼容,只需要将应用构建的通知 setContentIntent()参数修改为 PendingIntent.getActivity即可。
但是对于有特殊功能需求的开发者来讲,如果之前的业务逻辑是点击通知以后需启动服务/广播,并在服务/广播中完成⼀些动作(比如打点或者发送回执)后才启动目标 Activity,那么直接将setContentIntent()参数改为 PendingIntent.getActivity,同时这个目标Activity正好是第三方页面的话,点击通知后,直接启动的就是第三方页面,而开发者自身的业务需求则将无法得到处理。
针对此情况,个推提出了两个可参考的方案:
①在SDK中新增⼀个透明中转 Activity, 通知点击启动这个透明Activity 之后, 在透明Activity onCreate 方法中再启动目标服务或者广播, 后续逻辑保持⼀致。theme务必配置成android:style/Theme.Translucent.NoTitleBar, 透明activity务必记得要在onCreate最后调用finish方法销毁这个页面。
<activity android:name="透明中转 activity" android:excludeFromRecents="true" android:exported="false" android:taskAffinity="xxx" android:theme="@android:style/Theme.Translucent.NoTitleBar" > </activity>
②新增一个BaseActivity, 在BaseActivity完成业务逻辑,目标Activity继承这个BaseActivity, 并让目标Activity调用super方法。
相比第二种方案,第一种方案对客户来讲无需做额外处理,集成起来更加方便。因此,个推SDK产品使用的就是第一种方案进行Android12
适配。
02 前台服务启动限制
除了一些特殊情况外,以 Android12 为目标平台的应用将无法在后台运行时启动前台服务。如果应用尝试在后台运行时启动前台服务,将会引发异常。针对此情况,Android12官网也给出了前台服务的推荐替代方案:WorkManager。感兴趣的开发者可进入官网了解详情:https://developer.android.google.cn/about/versions/12/foreground-services#cases-fgs-background-starts-allowed
03 精确的闹钟权限
闹钟是应用安排定时工作的重要方式。在大多数情况下,应用应该使用非精确闹钟(inexact alarms),这样可以减少电池消耗。然而对于提供时间管理、日程安排等服务的App而言,必须使用精确的闹钟权限才能实现相关功能。精确闹钟功能非常方便可靠,但也会加大电量消耗。为增加用户的自主权,提升用户体验,Android12 对精确的闹钟权限进行了限制。
在Android12系统下,开发者如果想要使用精确闹钟,则需要在 AndroidManifest.xml 文件中添加该权限的申请:
- setAlarmClock()
- setExact()
- setExactAndAllowWhileIdle()
如下图,用户在 设置>应用>特殊应用权限>闹铃和提醒 可以看到需要精确闹钟权限的应用列表:
并且,用户可以为某一特定应用手动关闭或打开该权限:
总结
Android12的新特性还有很多,比如添加了 SplashScreen API,让App开发者可以自主设置启动应用的画面主题和外观;提供通知的丰富图片支持,使开发者可以通过在 MessagingStyle() 和 BigPictureStyle() 通知中提供动画图片来丰富应用的消息通知体验。总的来讲,Android12带来了很多酷炫的功能,也为用户提供了更多的自主权,尤其注重用户隐私的保护和个人信息的安全。更多Android12更新要点,开发者可进入Android12官网进一步了解:https://developer.android.google.cn/about/versions/12
针对Android12的新特性,个推消息推送SDK进行了适配测试。开发者用户可联系@个推技术支持了解个推消息推送SDK最新版本,完成对Android12的兼容。
扫码添加@个推技术支持
后续,个推还将持续关注安卓系统和行业发展动态,与开发者们交流相关开发知识和技术原理,共同推进移动互联网的快速发展。