Android 12 变更及适配攻略

在这里插入图片描述

这几个月有点忙,一年一篇的适配文章来的有点晚了。但其实也还好,因为我们项目也是下半年才适配。我这边也是提前调研踩坑,评估一下工作量。这个时间点也完全跟得上Google Play的审核要求(11月1号),对于国内这边更是超前了。。。废话不多说,开始了。

首先先说一些影响在Android 12上运行应用的行为变更。这部分会直接影响在Android 12上运行的所有应用,所以即使你不适配,也需要重点关注。

应用启动画面

从 Android 12 开始,SplashScreen API 可为所有Android 12或更高版本的设备上运行的应用启用新的应用启动动画。这包括启动时的进入应用动画、显示应用图标的启动画面,以及向应用本身的过渡。

先看一下启动画面示例:
在这里插入图片描述

启动画面的元素由Android清单中的XML资源文件定义。每个元素都有浅色模式和深色模式版本。

启动画面的可自定义元素包括应用图标、图标背景和窗口背景:

在这里插入图片描述

①应用图标应该是矢量可绘制对象(AVD XML),它可以是静态或动画形式。虽然动画的时长可以不受限制,但我们建议不超过1,000 毫秒。默认情况下,使用启动器图标。

②可以选择添加图标背景;在图标与窗口背景之间需要更高的对比度时图标背景很有用。如果您使用一个自适应图标,当该图标与窗口背景之间的对比度足够高时,就会显示其背景。

③与自适应图标一样,前景的三分之一被遮盖。

④窗口背景由不透明的单色组成。如果窗口背景已设置且为纯色,则未设置相应的属性时默认使用该背景。

其实这个实际上可以不适配。。。比如我的vivo手机其实在开发者模式可以开关这一选项,而且默认是关闭的。对于使用非原生系统的用户来说,算是个感知不到的变化。但最终适配与否还是看产品是否可以接受了。

不过需要注意:默认情况下,SplashScreen 使用主题的windowBackground(如果它是单色)和启动器图标。
所以windowBackground的颜色和logo的颜色如果对比度比较低,实际显示效果会比较糟糕,我觉得这块可以适当处理一下。

这里就不花费篇幅来说明了,有适配需求的可以参考:将现有的启动画面实现迁移到 Android 12 及更高版本

拉伸滚动效果

在搭载 Android 12 及更高版本的设备上,滚动事件的视觉行为发生了变化。

在 Android 11 及更低版本中,滑动到边界是会出现弧形的阴影遮罩。在 Android 12 及更高版本中,发生拖动事件时,视觉元素会拉伸和反弹;发生快速滑动事件时,它们会快速滑动和反弹。

在这里插入图片描述

定位权限:大概位置

如果您的应用请求ACCESS_COARSE_LOCATION但未请求ACCESS_FINE_LOCATION,则此变更不会影响您的应用。
请添加图片描述
如果您的应用请求ACCESS_FINE_LOCATION 运行时权限,您还应请求ACCESS_COARSE_LOCATION权限,以便处理用户授予应用大致位置访问权限的情形。

在这里插入图片描述

可能是出于兼容的目的。实际我在我的vivo手机测试发现,只请求ACCESS_FINE_LOCATION权限,也可以正常弹出,并选择位置精度。但是保险起见,实际适配时还是按照官方文档的要求来。

如果用户首次选择了大致位置,二次请求权限时会提示用户是否更改为精确位置。这点可以注意一下。

在这里插入图片描述

Activity生命周期

Android 12 更改了在按下“返回”按钮时系统对为其任务根部的启动器activity的默认处理方式。在以前的版本中,系统会在按下“返回”按钮时finish这些 activity。在 Android 12 中,现在系统会将 activity 及其任务移到后台,而不是finish activity。这一新行为与使用HOME按钮行为一致。

注意:系统仅会将新行为应用于为其任务根部的启动器 activity,即使用 ACTION_MAINCATEGORY_LAUNCHER 声明 intent 过滤器的 activity。对于其他 activity,在按下“返回”按钮时,系统会像以前一样finish activity。

对于大多数应用而言,此变更意味着使用“返回”按钮退出应用的用户可以更快地从温状态恢复应用,而不必从冷状态完全重启应用。

建议您针对此变更测试您的应用。如果您的应用目前重写 onBackPressed() 来处理返回导航并finish Activity,请更新您的实现来调用 super.onBackPressed() 而不是finish。调用 super.onBackPressed() 可在适当时将 activity 及其任务移至后台,并可为不同应用中的用户提供更一致的导航体验。

另请注意,通常,我们建议您使用 AndroidX Activity API 提供的自定义返回导航,而不是替换 onBackPressed()。如果没有组件拦截系统按下“返回”按钮,AndroidX Activity API 会自动遵循适当的系统行为。


还有许多的行为变更,以上我只选了几条重要的。其他变更可以参见:Android 12行为变更:所有应用

接下来是以Android 12为目标平台的应用行为变更。首先就需要我们将targetSdkVersion升至31。

自定义通知

Android 12 更改了自定义通知的外观和行为。以前,自定义通知能够使用整个通知区域并定义自己的布局和样式。因此会在不同设备上引发布局兼容性问题,使用户感到困惑。

对于以 Android 12 为目标平台的应用,自定义布局的通知将不再使用完整通知区域;相反,系统会使用标准模板。此模板可确保自定义通知在所有状态下都与其他通知相同,例如,在收起状态下的通知图标和展开功能,以及在展开状态下的通知图标、应用名称和收起功能。此行为与 Notification.DecoratedCustomViewStyle 的行为几乎完全相同。
图4
下面展示了在收起状态和展开状态下呈现的自定义通知:

图5
在这里插入图片描述
此变更会影响某些定义了 Notification.Style 子类的应用,或使用 Notification.Builder 中的 setCustomContentView(RemoteViews)setCustomBigContentView(RemoteViews)setContent(RemoteViews)setCustomHeadsUpContentView(RemoteViews) 方法的应用。

具体适配时,需要注意以下几点:

  • 自定义视图的尺寸有变更。一般来说,提供给自定义通知的高度比之前小。在收起状态下,自定义内容的最大高度已从 106dp 减少到 48dp。此外,水平空间也减小了。
  • 对于以 Android 12 为目标平台的应用,所有通知都是可展开的。通常,这意味着,如果您使用的是 setCustomContentView,则还需要使用 setBigCustomContentView,以确保收起状态和展开状态保持一致。
  • 为了确保“浮动通知”(Heads Up)状态看起来符合您的预期,请勿忘记将通知渠道的重要性(setPriority)提升至“高”(在屏幕中弹出)。

更安全的组件导出

如果您的应用以 Android 12 或更高版本为目标平台,且包含使用 intent 过滤器的 activity、服务或广播接收器,您必须为这些应用组件显式声明 android:exported 属性。否则应用无法安装。

所以我们可以在AndroidManifest.xml搜索<intent-filter>,为这些组件显式声明 android:exported 属性。

android:exported 属性表示是否能被其他应用隐式调用。所以如果应用组件包含 LAUNCHER 类别,请将 android:exported 设置为 true。在大多数其他情况下,请将 android:exported 设置为 false。例如:

<service android:name="com.example.app.backgroundService"
         android:exported="false">
    <intent-filter>
        <action android:name="com.example.app.START_BACKGROUND" />
    </intent-filter>
</service>

对于三方sdk,具体根据相应文档要求配置。如果sdk没有此适配,可以参考这篇文章:Android 12 自动适配 exported 深入解析避坑

PendingIntent可变性

如果您的应用程序以Android 12为目标平台,您必须为应用创建的每个 PendingIntent 对象指定可变性。这项额外的要求可提高应用的安全性。因为三方app可以通过劫持PendingIntent,然后改写里面的action、category、data等,造成重定向攻击。

所以适配的具体就是在创建 PendingIntent时,使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE 标志。否则运行时会报IllegalArgumentException

java.lang.IllegalArgumentException: com.dailyyoga.inc: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.

我们可以检查代码中使用PendingIntent的地方,比如PendingIntent.getActivity()PendingIntent.getService()
PendingIntent.getBroadcast()方法。指定可变性标志:

PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);

一般情况下,尽可能使用FLAG_IMMUTABLE创建不可变的 ​PendingIntent​ 对象。如果需要创建可变的​PendingIntent​,那么一定注意要使用显式 intent 并设置​ComponentName​。

我们项目中因为使用到了androidx.workgoogle-exo库,它内部在创建 PendingIntent 对象时没有指定可变性,导致有兼容性问题,所以需要升级版本至2.7.02.16.0以上。

应用休眠

Android 11中引入了权限自动重置机制。如果您的应用以 Android 12 为目标平台,并且用户有几个月未与您的应用互动,则系统会自动重置授予的所有权限并将您的应用置于休眠状态。

休眠状态的应用程序具有以下特征:

  • 应用无法从后台运行作业(jobs)或警报(alerts)。
  • 应用无法接收推送通知,包括通过Firebase Cloud Messaging发送的高优先级消息。
  • 应用程序的缓存文件都将被删除。

当用户下一次与您的应用程序交互时(包括与小组件),您的应用程序将退出休眠,它可以再次创建作业、提醒和通知。

但是,系统不会为您的应用程序执行以下操作:

  • 重新授予你的应用的运行时权限。用户必须重新授予您的应用程序这些权限。
  • 不会恢复作业、提醒和通知。你需要重新计划在应用程序进入休眠状态之前计划的任何作业、警报和通知。

此行为类似于用户手动从系统设置中强制停止您的应用程序。要更轻松地支持此工作流程,请使用WorkManager。您还可以在ACTION_BOOT_COMPLETED广播接收器中添加重新计划逻辑,当您的应用退出休眠状态并在设备启动后被调用。

对于系统级应用,会免于休眠。如果您的应用程序中的核心用例会受到休眠的影响,您可以向用户请求免除应用程序休眠。具体详见官方文档,这里就不过多介绍了。

前台服务启动限制

针对Android 12或更高版本的应用在后台运行时无法启动前台服务,除了几个特殊情况外。如果一个应用程序试图在后台运行时启动前台服务,而前台服务不满足其中一种异常情况,系统将引发ForegroundServiceStartNotAllowedException.

你可以执行以下ADB命令,检查您的应用程序是否执行后台启动:

adb shell device_config put activity_manager \
  default_fgs_starts_restriction_notification_enabled true

一旦发现后台运行时启动前台服务,就会在通知栏推送一条提醒。

谷歌建议的适配方法就是使用WorkManager,应用在后台运行时,使用 WorkManager来计划和启动加急任务(expedited job) 。从WorkManager2.7.0开始,可以调用setExpedited()来声明Worker使用加急任务。

通知 trampoline 限制

当用户与通知交互时,某些应用程序会通过启动应用程序组件来响应通知点击,该组件最终会启动用户最终看到并与之交互的Activity。 此应用程序组件称为通知中介。

也就是说,如果你点击了通知,启动了一个广播或者服务,然后在这个广播或服务中调用了startActivity()启动activity。此时系统会阻止该 activity 启动,并在 Logcat 中显示以下消息:

Indirect notification activity start (trampoline) from PACKAGE_NAME, \
this should be avoided for performance reasons.

我们可以执行下面的命令识别哪些应用组件充当通知 trampoline:

adb shell dumpsys activity service \
  com.android.systemui/.dump.SystemUIAuxiliaryDumpService

执行后,点击你的通知,如果是通知 trampoline。Log会输出的包含文本“NotifInteractionLog”的日志。此部分包含识别因点按通知而启动的组件所需的信息。

具体适配方法就是使用PendingIntent直接启动目标Activity。

蓝牙权限

如果您的应用程序面向Android 12或更高版本,使用蓝牙功能时请在应用程序的清单文件中声明以下权限:

  • BLUETOOTH_SCAN:允许蓝牙设备扫描。
  • BLUETOOTH_CONNECT:允许蓝牙设备连接。
  • BLUETOOTH_ADVERTISE:允许当前蓝牙设备可以被其他蓝牙设备发现。

在这里插入图片描述

如果不申请新的权限,扫描时会报如下错误:

 java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for android.content.AttributionSource@8825e139: GattService registerScanner

对于以前的与蓝牙相关的权限声明,设置android:maxSdkVersion到30。此应用兼容性步骤可帮助系统仅授予您的应用在运行Android 12 的设备上安装时所需的蓝牙权限。

<manifest>
    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- 只有当您的应用程序使用蓝牙扫描结果来获取物理位置时才需要 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

如果您的应用程序使用蓝牙扫描结果来获取物理位置,请声明ACCESS_FINE_LOCATION。以前版本中(6.0 ~ 11)是必须申请定位权限,才可以进行蓝牙扫描。

在Android 12上,如果你的应用程序不使用蓝牙扫描结果来获取物理位置。可以添加android:usesPermissionFlags属性到您的BLUETOOTH_SCAN权限声明,并将该属性的值设置为neverForLocation.

<manifest>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />

    <!--可以删除定位权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

其他

  • 默认toast样式发生变化,文字上限为两行,且在文字旁边会显示应用图标。

在这里插入图片描述

  • 为了保护私有应用数据,Android 12 变更了 adb backup 命令的默认行为。对于以 Android 12或更高版本为目标平台的应用,用户运行 adb backup 命令时,从设备导出的任何其他系统数据都不包含应用数据。
    如果您的测试或开发工作流程依赖于使用 adb backup 的应用数据,现在您可以选择通过在应用的清单文件中将 android:debuggable 设置为 true 来导出应用数据。

有关Android 12的适配内容主要就这么多,更多的变更还是需要我们去阅读官方文档。链接我也贴到了文末。

关于Android的适配,我从Android 6.0写到了12,其中也包含早期4.4,5.0的个别特性适配。这些内容都在我的Android 适配专栏里,希望可以帮到你。

最后,期待你的收藏点赞,给作者一个支持。好让我继续坚持每年为大家带来我的实际适配心得,感谢!

参考

猜你喜欢

转载自blog.csdn.net/qq_17766199/article/details/125376225