赞
踩
这几个月有点忙,一年一篇的适配文章来的有点晚了。但其实也还好,因为我们项目也是下半年才适配。我这边也是提前调研踩坑,评估一下工作量。这个时间点也完全跟得上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 及更高版本
2023.08.09 补充:前一阵做了相关的适配,发现一个问题。如果windowSplashScreenAnimatedIcon
设置的是静态图,即使你用的是矢量图,开屏是图片还是会模糊。最后发现使用动态矢量图就清晰了。。。可以用<animated-vector />
标签包裹原先的静态图<vector/>
,示例如下:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector .../>
</aapt:attr>
</animated-vector>
在搭载 Android 12 及更高版本的设备上,滚动事件的视觉行为发生了变化。
在 Android 11 及更低版本中,滑动到边界是会出现弧形的阴影遮罩。在 Android 12 及更高版本中,发生拖动事件时,视觉元素会拉伸和反弹;发生快速滑动事件时,它们会快速滑动和反弹。
如果您的应用请求ACCESS_COARSE_LOCATION
但未请求ACCESS_FINE_LOCATION
,则此变更不会影响您的应用。
如果您的应用请求ACCESS_FINE_LOCATION
运行时权限,您还应请求ACCESS_COARSE_LOCATION
权限,以便处理用户授予应用大致位置访问权限的情形。
可能是出于兼容的目的。实际我在我的vivo手机测试发现,只请求ACCESS_FINE_LOCATION
权限,也可以正常弹出,并选择位置精度。但是保险起见,实际适配时还是按照官方文档的要求来。
如果用户首次选择了大致位置,二次请求权限时会提示用户是否更改为精确位置。这点可以注意一下。
Android 12 更改了在按下“返回”按钮时系统对为其任务根部的启动器activity的默认处理方式。在以前的版本中,系统会在按下“返回”按钮时finish
这些 activity。在 Android 12 中,现在系统会将 activity 及其任务移到后台,而不是finish activity。这一新行为与使用HOME按钮行为一致。
注意:系统仅会将新行为应用于为其任务根部的启动器 activity,即使用 ACTION_MAIN
和 CATEGORY_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
的行为几乎完全相同。
下面展示了在收起状态和展开状态下呈现的自定义通知:
此变更会影响某些定义了 Notification.Style
子类的应用,或使用 Notification.Builder
中的 setCustomContentView(RemoteViews)
、setCustomBigContentView(RemoteViews)
、setContent(RemoteViews)
和 setCustomHeadsUpContentView(RemoteViews)
方法的应用。
具体适配时,需要注意以下几点:
setCustomContentView
,则还需要使用 setBigCustomContentView
,以确保收起状态和展开状态保持一致。如果您的应用以 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 深入解析避坑
如果您的应用程序以Android 12为目标平台,您必须为应用创建的每个 PendingIntent
对象指定可变性。这项额外的要求可提高应用的安全性。因为三方app可以通过劫持PendingIntent
,然后改写里面的action、category、data等,造成重定向攻击。
所以适配的具体就是在创建 PendingIntent
时,使用 PendingIntent.FLAG_MUTABLE
或 PendingIntent.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.work
、google-exo
库,它内部在创建 PendingIntent
对象时没有指定可变性,导致有兼容性问题,所以需要升级版本至2.7.0
和2.16.0
以上。
Android 11中引入了权限自动重置机制。如果您的应用以 Android 12 为目标平台,并且用户有几个月未与您的应用互动,则系统会自动重置授予的所有权限并将您的应用置于休眠状态。
休眠状态的应用程序具有以下特征:
当用户下一次与您的应用程序交互时(包括与小组件),您的应用程序将退出休眠,它可以再次创建作业、提醒和通知。
但是,系统不会为您的应用程序执行以下操作:
此行为类似于用户手动从系统设置中强制停止您的应用程序。要更轻松地支持此工作流程,请使用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) 。从WorkManager
2.7.0开始,可以调用setExpedited()
来声明Worker使用加急任务。
当用户与通知交互时,某些应用程序会通过启动应用程序组件来响应通知点击,该组件最终会启动用户最终看到并与之交互的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>
adb backup
命令的默认行为。对于以 Android 12或更高版本为目标平台的应用,用户运行 adb backup
命令时,从设备导出的任何其他系统数据都不包含应用数据。adb backup
的应用数据,现在您可以选择通过在应用的清单文件中将 android:debuggable
设置为 true 来导出应用数据。有关Android 12的适配内容主要就这么多,更多的变更还是需要我们去阅读官方文档。链接我也贴到了文末。
关于Android的适配,我从Android 6.0写到了12,其中也包含早期4.4,5.0的个别特性适配。这些内容都在我的Android 适配专栏里,希望可以帮到你。
最后,期待你的收藏点赞,给作者一个支持。好让我继续坚持每年为大家带来我的实际适配心得,感谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。