本文目录
- 背景
- 技术分析
- 实战
- 总结与展望
背景
2021 年 10 月 5 日 Google 发布 Android12 操作系统,安全性和隐私性大幅提升,各手机厂家陆续更新 Android12 操作系统。
2022 年随着各大 APP 应用市场推动 Android12 适配工作,开发者积极响应,一股技术升级浪潮来袭。
适配 Android 12
流利说旗下三大 APP “流利说英语”,“流利说阅读”,“流利少儿英语” 在 2019 年适配了Android X (API28),时隔 3 年全面适配 Android12 工作启动。
技术方案选型
原则
流利说 APP 工程较为庞大,适配工作原则应优先考虑减少变动量,低成本过渡到 Android12,后续迭代中再依序升级各项组件。
在此原则基础上思考两条主线 1. 工程环境适配。2. 系统特性适配。
线路
工程环境适配目标是让 Android12 工程可以顺利完成项目的编译、调试、打包等各项链路。开发环境涉及 Android studio、JDK、Gradle 升级,CI/CD 主要是 ci-runner JDK 环境升级。
系统特性适配目标是解决各类编译和运行错误。 涉及第一方和第三方 SDK 升级,APP 权限调整,针对系统新特性调整业务代码,适配 UI 等工作。
按照这个两条主线我们开始实战升级。
实战
开发环境
AS升级
开发工具 Android studio 升级到 Chipmunk 版本, 升级全家桶, Android API,PlatformTools, BuildTools。
流利说工程 Gradle 版本比较老 AGP-3.4.3, Gradle-5.5.1 ,在 JDK 8 编译时报错。这是因为 Android12 中使用了JDK8 中没有的一个注解 - MODULE,进而导致的出错。
升级 JDK1.8 到 JDK11(Perferences→ Build Tools→ Gradle → GradleJDK 切换版本)。
java.lang.AssertionError: annotationType(): unrecognized Attribute name MODULE
配置 Gradle 文件
compileSdkVersion = 31buildToolsVersion = '31.0.0'minSdkVersion = 21targetSdkVersion = 31
CI/CD 环境
配置 Gitlab-Runner 环境
流利说使用 ansible 管理多台 runner 机器,将其中一台机器 runner 的 dockerfile 升级到 JDK11
FROM ubuntu:16.04LABEL de.mindrunner.android-docker.flavour="built-in"COPY --chown=root:root ./source.list /etc/apt/sources.listRUN apt-get updateRUN apt-get install -y openjdk-11-jdk git curl unzip lib32stdc++6
RUN apt-get install -y python3-pip python3-dev \ && pip3 install --upgrade pipRUN apt-get install -y wget expect vim python \ && apt-get install -y python-pip python-dev build-essential \ && pip install pathlib \ && pip install configparser
RUN apt-get clean
启用 Runner
在 gitlab 中的 setting→ Runners → Enable for this project 启用上述 CI Runner。
Android 工程 gitlab.yml 中 指定 tag 和 image
完成以上操作后 Android 12 项目环境搭建基本完成,本地编译和 CI 编译可以顺利通过。
API
这一小节需要适配 Android12 API , 解决工程各种 crash 和编译异常问题。
Exported
使用 的 activity、服务或广播接收器,需要显式的申明 exported 属性,如果组件需要在应用外启动就设置为‘true’,否则尽可能设置为’false’进而增加安全性。(不适配情况下APP安装时会提示:解析软件包出现问题)。可以跟着 Lint 报错(也可以手动搜索)在 AndroidManifest.xml 中一个个改动。比较隐形的是一些第三方库可能没有设置 exported 属性。可以将 APK 文件解压缩,然后用 AXMLPrinter2.jar 反编译明文输出 AndroidManifest.xml ,找到那些包含 但没有申明 exported 的 Activity。
java -jar AXMLPrinter2.jar AndroidManifest.xml > test.xml
再利用 menifest 可合并的方式来解决, 比如添加下面这行。
<activity android:name="com.github.moduth.blockcanary.ui.DisplayActivity" android:exported="false" />
PendingIntent
PendingIntent 必须声明可变性来增加应用安全性,Android12 上每一个PendingIntent必须设置可变性 FLAG,可以是 FLAG_MUTABLE 或者 FLAG_IMMUTABLE。例:
val pi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE)} else { PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT )}
Kotlin 判空
Kotlin 和 JDK 升级之后,判空检测升级,原代码中有相当一部分未判空的代码都添加了 ?.let{}、?.apply{} 等相关判空检测。这个工作会比较繁琐,改动地方比较多,编译时 AS 提供很好的错误定位,逐一修复,是个体力活。
SingleInstance 模式 和 taskAffinity
举例说明:A-Activity 是 standard 模式,由他启动的 B-Activity 是 singleInstance 模式。当进入 B 后,退到桌面再次点击 Icon,又重新启动了主 Activity。原因:B-Activity 是位于新的 task 栈内,然后当回到桌面后,Android11 会清理不活跃的后台栈,所以之前的 activity 都被干掉了。经测试,当在 B-Activity 页面直接回到桌面,确实 A 走了onDestroy。但是如果给 B-Activity 设置了不同于 application 的 taskAffinity,当退到后台时,系统就不会杀死 A-Acitivty 所在的栈。
权限和限制
SCHEDULE_EXACT_ALARM:Android12 上对精确闹钟的使用添加了最新限制,必须申明 SCHEDULE_EXACT_ALARM 权限才可以使用,例如提醒通知要在每天的八点触发,就需要声明此权限。
SENSOR_DELAY:限制加速度传感器、陀螺仪、磁力传感器等运动或位置传感器的采样率,一般为200HZ。可以根据业务需要设置(比如 SENSOR_DELAY_NORMAL) 如需 SENSOR_DELAY_FASTEST 需要声明权限HIGH_SAMPLING_RATE_SENSORS
trampoline:响应通知点按操作的服务或广播接收器这种组件,被称为 trampoline,12 之前 trampoline 中可以调用 startActivity 来启动最终的Activity,Android 12 上限制了此行为,如果要达到同样的效果,需要通过设置通知或通知操作按钮的 PendingIntent 的来达到同样的目的,不要忘了设置 PendingIntent 的可变性。
更多问题
项目不同差异较大,解决方案可以网上搜索 都有比较明确的答案
getSystemService(Context.WINDOW_SERVICE): Tried to access visual service WindowManager from a non-visual Context
JAX-DataBinding: 从 jdk9 开始 JAX-B 就从 jdk 中移除了
getLifecycle(): returned null in ComponentActivity's constructor
WebView: Binary XML file line... Error inflating class android.webkit.WebView
FOREGROUND_SERVICE: Permission Denial: ... requires android.permission.FOREGROUND_SERVICE
org.apache.http.legacy: Didn't find class "org.apache.http.impl.client.DefaultHttpClient"
android.graphics.FontFamily: androidx.core.graphics.TypefaceCompatApi26Impl.obtainFontFamilyCtor
network security policy: CLEARTEXT communication to dev-account.thellsapi.com not permitted by network security policy...
升级第一方和第三方
这部分也是项目差别较大,最近一年配合应用市场规范整改,已经有一部分 SDK 更新到了新版本,但适配工作依旧是黑盒,需要QA测试阶段才能发现。 注意:升级 SDK 混淆配置可能有差异。
UI
Notification 样式修改
自定义内容视图的通知将不再使用完整通知区域,系统会应用标准模板来确保自定义通知在所有状态下都与其他通知相同。我们一般使用的是 setCustomContentView,12 上还需要使用 setBigCustomContentView,以确保收起状态和展开状态保持一致。大致样式如下:
SplashScreen
App在每次启动时,系统都会为我们加上一个默认的启动画面。
1.应用图标。2 背景图标。3 前景遮罩。4windows 窗口背景。找设计师要素材,在 style.xml 设定:
<item name="android:windowSplashScreenBackground">@color/bg_gray</item><item name="android:windowSplashScreenAnimatedIcon">@drawable/icon_splash_screen_animated</item><item name="android:windowSplashScreenAnimationDuration">1000</item>
总结与展望
以上内容为流利说各 APP 适配 Android12 改动范围。Android12 本身改变内容还有很多,比如定位精度、应用休眠、前台服务、蓝牙权限等等。2022年8月 Android13 正式版发布,更细化的权限和更多后台运行限制, 总体上感觉 Google 花了很大精力在提升 Android 安全性(当然还有稳定性),作为开发者很高兴看到 Android 生态越来越健康, 流利说作为保护用户隐私典范企业一直积极响应政策走在规范行业前沿,为我们的用户提供安全可靠的服务。