谷歌于10月4日将Android 12的源代码推送至Android开源项目(AOSP)。经过9个月的测试和优化,自2021年2月发布第一个预览版以来,正式版本的Android 12终于面世!除了在用户界面方面进行了多项升级外,Android 12还进一步增强了个人隐私安全保护。总体而言,Android 12更加智能、高效和安全。对于感兴趣的开发者来说,他们可以登录官网下载源码进行测试和学习。
技术分享 | Android 12适配指南,为您呈现个推的专业解读
作为个推服务开发者多年的经验积累,我们不仅致力于打磨SDK产品,还一直紧密关注和跟踪行业发展趋势。在Android12稳定版发布后,我们立即进行了模拟器研究和适配测试。本文将从安全变更、权限变化、性能更新等方面详细介绍Android12的新特性,旨在帮助开发者更快速、更便捷地适配最新的Android系统。
安全变更
01 更安全的组件导出
熟悉Android开发的同学都了解,Android平台拥有四大核心组件,它们分别是活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供器(Content Provider)。活动组件为用户提供可视化的操作界面,使用户能够与应用程序进行交互;服务组件在后台运行,支持各种功能的实现;广播接收器主要用于接收各种广播信息;而内容提供器则主要用于多个应用程序之间存储和读取数据,类似于一个数据库。
这四大组件赋予了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实现。
为了增强应用的安全性,Android 12 引入了新特性,要求应用在创建每个 PendingIntent 对象时都必须指定可变性。可以使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志来满足要求。
如果未设置任何可变性标志,系统将抛出IllegalArgumentException异常。以下是报错内容:抛出异常:java.lang.IllegalArgumentException: XXX: 创建PendingIntent时,针对S+(版本31及以上),需要指定FLAG_IMMUTABLE或FLAG_MUTABLE中的一个参数。危险的意图启动
对于一般的intent而言,开发者如何确保其安全性呢?Android12为开发者们提供了一种调试功能。如果应用以不安全的方式启动intent,此功能将会发出警告。具体实现也比较简单,开发者只需在application中添加以下代码即可:
如果 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() // 其他之前添加的StrictMode检查。 // ... }①从intent的extra中解析提取嵌套intent。
使用这个嵌套的intent来启动应用组件,比如将intent传递给startActivity()方法。
关于嵌套 intent 安全问题, 这篇文章写得比较详细,感兴趣的开发者可以深入阅读:https://blog.csdn.net/m0_57546986/article/details/116078533
隐私保护
为了更好地保护用户的个人信息,Android12推出了一系列新的隐私功能,包括大致位置选项、应用休眠和ADB备份限制。
01 位置选项的改进
在Android12之前,用户只能在系统级别设置中授予位置信息访问权限。如果想要更改特定应用的位置权限,需要手动找到相应的设置界面进行处理。为了更好地保护用户隐私,Android12引入了“大致位置”选项。现在,在应用请求访问位置权限时,会出现一个弹窗,供用户选择“确切位置”或“大致位置”来进行授权:
-
通常情况下,我们能够精确到几米以内的位置。
-
大致(粗略)位置,一般为几百米。
个推对位置信息的授权方式进行了实测,总共有三种方式:仅在使用该应用时允许、仅限这一次和不允许。
如果在Manifest中配置了<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>并且请求了Manifest.permission.ACCESS_COARSE_LOCATION位置弹框,那么①。
如果(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ 如果(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文件中同时配置了<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>和<uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"/>,并且需要同时请求这两个位置权限的弹框:
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 应用休眠
除此之外,Android 12对Android 11的“自动撤销权限”功能进行了升级,并引入了全新的“应用休眠”功能。这项功能最初由谷歌于今年一月份在AOSP系统项目中提交。用户可以自行标记一个应用列表,如果这些被标记的应用几个月没有被使用,系统将自动取消其权限、停止各种后台通知,并将该应用置于休眠状态,以节省电量并释放占有的存储空间。Android 12的这一功能不仅能够释放手机存储空间,还将极大地提升用户体验。
对于用户交互频率较低的特定类型应用而言,开发者可以使用包含 Intent.ACTION_APPLICATION_DETAILS_SETTINGS 操作的意图来向用户发送请求,以便让用户允许应用在休眠时保持活动状态并自动重置权限限制。
03 ADB备份限制
为了加强私有应用数据的保护,Android 12对adb backup命令的默认行为进行了修改。对于目标平台为Android 12的应用程序而言,在运行adb backup命令时,导出的系统数据将不再包含该应用程序的数据。
如果测试或开发工作流程依赖于使用adb backup的应用数据,您可以选择在AndroidManifest文件中将android:debuggable设置为true来导出应用数据。请注意,在发布线上版本时务必将android:debuggable设置为false。
性能更新
Android系统每次版本更新都注重性能优化。在Android12中,我们对应用程序启动时间进行了改进,并通过优化I/O来加快应用程序的加载速度。此外,为了提升用户体验,我们对通知trampoline、前台服务启动以及闹钟权限等方面进行了限制。
01 通知限制
当用户点击通知时,会启动一个组件来响应用户的操作,一般会打开一个界面。这个界面组件被称为通知限制。
考虑一种情况,如果通知所关联的PendingIntent使用了PendingIntent.getService或者PendingIntent.getBroadcast方法,那么当用户打开通知时,后台可能需要执行一些耗时操作或者发起网络请求等待处理完成后再跳转到目标页面。然而,由于各种原因(如网络延迟、耗时操作等),可能会导致等待时间较长,进而使得界面弹出较晚,在几秒钟之后才能跳转到目标页面。相对而言,这种体验是比较差的。
为了提升用户体验,Android 12 平台上的应用无法从作为通知跳板的服务或广播中启动活动。换句话说,应用构建通知时,必须使用 PendingIntent.getActivity() 作为 setContentIntent() 的参数。
个推已对该功能进行了测试:
-
在创建通知时,可以使用setContentIntent()方法来设置PendingIntent参数。你可以使用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,并在其onCreate方法中再次启动目标服务或广播,保持后续逻辑一致。请务必将theme配置为android:style/Theme.Translucent.NoTitleBar,并确保在onCreate的最后调用finish方法以销毁该页面。
还木有评论哦,快来抢沙发吧~