Android12全面适配

2,289 阅读6分钟

一、适配背景

image.png

二、适配需要改造如下:

1、配置编译选项:

- targetSdk:31

- compileSdk:31 

  这个必须为31,因为android12开屏适配需要用到31的api,如果不适配开屏,compile sdk不使用31也是可以的。
 
- buildToolVersion:30.0.2 

2、广告google ads 升级到最新版

com.google.android.gms:play-services-ads:20.4.0 
   
   升级为
   
com.google.android.gms:play-services-ads:21.0.0

3、Firebase,Google登录,Facebook,AppsFlyer都要升级到最新版本

  • firebase升级
implementation platform('com.google.firebase:firebase-bom:29.1.0')

升级为

implementation platform('com.google.firebase:firebase-bom:30.2.0')

并去除fpm的显示指定版本,改为:      implementation 'com.google.firebase:firebase-perf'

-        classpath 'com.google.gms:google-services:4.3.10'\
-        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'\
-        classpath 'com.google.firebase:perf-plugin:1.4.0'

修改为
+        classpath 'com.google.gms:google-services:4.3.13'\
+        classpath 'com.google.firebase:perf-plugin:1.4.1'\
+        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'

  •  google登录升级
implementation platform('com.google.firebase:firebase-bom:29.1.0')

implementation 'com.google.android.gms:play-services-auth:20.1.0'

修改为

`implementation 'com.google.android.gms:play-services-auth:20.2.0'`
  • facebook升级
api 'com.facebook.android:facebook-login:8.1.0'

    修改为:

implementation 'com.facebook.android:facebook-login:14.1.0'

 

  • appsflyer升级
implementation 'com.appsflyer:af-android-sdk:6.4.0@aar'\
        implementation 'com.android.installreferrer:installreferrer:2.2'

修改为:

implementation 'com.appsflyer:af-android-sdk:6.8.0@aar'\
        implementation 'com.android.installreferrer:installreferrer:2.2'

4、确认小米SDK已经升级到适配android12的版本

5、JDK配置

本地环境的jdk版本必须配置为11AS的gradle jdk版本也要配置为11,
否则无法编译通过,因为android12的jar依赖的了jdk11

6、所有子项目的annatationProcessor都要改为kapt

请各个子项目一个一个自我检查,否则无法编译通过或者注解器功能失效

7、manifest中带有intent-fitlers的都要显示指定android:exported="true or false",

否则android12无法安装

8、PendingIntent的flag必须强制指定FLAG_MUTABLE or FLAG_IMMUTABLE

否则会出现闪退,需要全局改造,适配方案如下:

if (Build.VERSION.SDK_INT >= 31) {\
flags |= FLAG_MUTABLE;\
}

如果有第三方SDK未适配,需要推进其进行适配

9、ijkplayer会闪退,需要进行适配,具体见详细适配过程

10、TelephonyManager.getNetworkType需要权限,而且需要更换API

android12可能无法获取到网络类型

11、 精确的闹钟权限

以Android 12为目标平台的App,如果使用到了AlarmManager来设置定时任务,
并且设置的是精准的闹钟(使用了setAlarmClock()、setExact()、setExactAndAllowWhileIdle()
这几种方法),则需要确保SCHEDULE_EXACT_ALARM权限声明且打开,否则App将崩溃。

12. 通知 trampoline 限制

以Android 12为目标平台的App,如果尝试在Service或BrocastReceiver中内
调用 startActivity(),系统会阻止该Activity启动,各业务自检

13.Package可见性:“获取设备是否已安装某个app或者获取已安装列表,或者查询是否可以打开某个app”的这类功能将无法使用,若有强烈需求,需要按一下规则进行适配

当使用queryIntentActivities(), getPackageInfo()或者
getInstalledApplications()查询是其他应用信息的话会查不到
当应用target到Android11之后,Package可见性受到了限制,查询其他应用信息需要加上QUERY_ALL_PACKAGES权限或者使用queries方式获取。
**解决办法:**
1. 在AndroidManifest.xml中加入权限<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />,
这个需要谨慎使用,因为应用市场上线检查可能会需要提供使用的必要性说明,例如Google Play政策:*<https://support.google.com/googleplay/android-developer/answer/10158779>*
2. 在AndroidMainifest.xml中定义需要访问的应用信息,例如:需要访问某个应用信息,直接指定应用包名。
 <queries>
   <package android:name="com.example.store" />
 </queries>`
需要访问具有某些intent的外部组件,指定需要访问的intent。
 <queries>
   <intent>
     <action android:name="android.intent.action.SEND" />
     <data android:mimeType="image/jpeg" />
   </intent>
 </queries>`
需要访问某些外部content provider,指定authoritite。
 <queries>
   <provider android:authorities="com.example.settings.files" />
 </queries>

14、开屏适配

android12采用splashScreen一直到进入首页为止;可能UI需要设计一个更好看的图标 和底部brand

三、适配后需要测试的点如下:

分别用android12设备和android12以下的设备测试以下几点:

1、用Android12设备进行自动化测试,遍历看看是否有遗漏的crash

2、用ci包和googleplay包内测包测试一下googleplay登录和facebook登录是否正常

3、查看firebase后台crash数据,分析数据,以及fpm数据是否正常;

4、查看google ad广告是否正常;

5、测试android12是否获取得到网络类型:可能获取不到;需要同步产品和BI

6、确认小米SDK对方说已适配到android12,测试一下推送通知栏以及透传通知栏是否正常展示,展示通知栏时是否会闪退。

四、详细适配过程

1、配置编译选项:targetSdk:31,compileSdk:31,buildToolVersion:30.0.2

开始编译:出现编译失败:

Caused by: java.lang.AssertionError: annotationType(): unrecognized Attribute name MODULE (class com.sun.tools.javac.util.SharedNameTable$NameImpl)\
        at com.sun.tools.javac.util.Assert.error(Assert.java:133)

这个问题踩的坑比较深,过程如下:

  • 1、配置android studio gradle 为 jdk 11,配置app/build.gradle 为jdk 11,本地本地环境为jdk 11,编译后,出现gradle 各种预发问题,看样子需要升级gradle到7.0以上,改造太大,另想办法;

  • 2、回退为java8,尝试将所有annotationProcessor改为kapt,gradle命令行居然编译成功了(本地java环境是java8);

  • 3、Android Studio编译失败:Caused by: java.lang.AssertionError,又出现这个错误,于是配置android studio gradle 为 jdk 11,编译成功;

  • 4、将此配置同步到主工程,发现AS可以编译成功,但是gradle命令行不能成功,出现Caused by: java.lang.AssertionError,于是将本地jdk改为11,app/build.gradle保持java8配置,编译成功;

所以当时最终解决方案是(这个在最后适配12开屏的时候否定了这个最终方案):

  • Android Studio 配置修改如下:

image.png

  •  app/build.gradle里的java版本改为:
compileOptions {\
sourceCompatibility JavaVersion.VERSION_11\
targetCompatibility JavaVersion.VERSION_11\
}

//去除警告:AGPBI: {"kind":"warning","text":"An API level of 31 is not supported by this compiler。。。。\
    kotlinOptions {\
        jvmTarget = '1.8'\
    }
  •     然后从官网下载jdk11进行安装:
-     <https://www.oracle.com/java/technologies/downloads/#java11-mac>\
并将本地java环境配置为11,

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.16.jdk/Contents/Home/

配置成功后,执行java -version显示如下:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.16.jdk/Contents/Home/

$ java -version

java version "11.0.16" 2022-07-19 LTS

Java(TM) SE Runtime Environment 18.9 (build 11.0.16+11-LTS-199)

Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.16+11-LTS-199, mixed mode)

实际上的摸索过程要比以上文档描述的复杂一点,过程如下:

发现:

**4.1.0对应的buildToolVersion为29.0.2**

**4.2.0版本的jdk依然是java8**

**7.0的版本才使用的java11**

**因此要将java8升级到java11,就意味着要升级Gradle Plugin7.0,

而升级到7.0意味着要适配大量的插件工作,重新学习整个编译流程,改造风险太大,决定更换思路。

image.png

image.png

image.png

**最终,我们尝试将compleSdk降为30,就解决了编译过程中java出错的问题,只是我们无法使用31的新api了。。

后边因为要集成implementation 'androidx.core:core-splashscreen:1.0.0-alpha01' 要求minComplieSdk 为31

经过一翻折腾,发现将一下注解器改为kpat就可以了,原来的是annoProcessorAnnatation,保持java8

kapt "com.meiyou.framework:summer-compiler:2.0.9.1"\
kapt "com.meiyou:usopp:1.0.15"\
kapt "com.google.auto.service:auto-service:1.0-rc2"

也就是子模块的annotationProcessor都要改成kapt才可以

2、编译出现错误

Targeting S+ (version 10000 and above) requires that an explicit value for android:exported be defined when intent filters are present\

也就是maifest所有带有 `intent filters都要显示的声明android:exported = true或者false

于是将缺少的补上即可。`

如果是第三方无法修改的, 有两种方案:1、拷贝到主app进行复写;2、使用脚本进行全局替换:http://events.jianshu.io/p/1913b48f2dad

3、安装出现错误

The application could not be installed: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED

这个设备的问题,我们用的是小米12,将小米12的安全调试开关打开即可;

image.png

4、启动运行闪退

2022-07-28 17:16:47.551 13041-13120/com.meetyou.intl E/AndroidRuntime: FATAL EXCEPTION: pool-27-thread-1
Process: com.meetyou.intl, PID: 13041
java.lang.IllegalArgumentException: com.meetyou.intl: 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.
at android.app.PendingIntent.checkFlags(PendingIntent.java:375)
at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:645)
at android.app.PendingIntent.getBroadcast(PendingIntent.java:632)
at androidx.work.impl.utils.ForceStopRunnable.getPendingIntent(ForceStopRunnable.java:174)
at androidx.work.impl.utils.ForceStopRunnable.isForceStopped(ForceStopRunnable.java:108)
at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:86)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:75)

解决方案

这个库是com.google.android.gms:play-services-ads:20.4.0 没有适配android12,因此需要升级google ads sdk为` implementation 'com.google.android.gms:play-services-ads:21.0.0'

升级后编译出现:

  • What went wrong:
    Execution failed for task ':app:checkZroTestDebugDuplicateClasses'.\

A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class com.google.android.gms.internal.measurement.zzhx found in modules play-services-measurement-base-20.1.2-runtime (com.google.android.gms:play-services-measurement-base:20.1.2) and play-services-measurement-impl-20.1.0-runtime (com.google.android.gms:play-services-measurement-impl:20.1.0)
Duplicate class com.google.android.gms.internal.measurement.zzmt found in modules play-services-measurement-base-20.1.2-runtime (com.google.android.gms:play-services-measurement-base:20.1.2) and play-services-measurement-impl-20.1.0-runtime (com.google.android.gms:play-services-measurement-impl:20.1.0)

说明和firebase冲突了,需要升级firebase crash analytics 和firebase fpm

解决方案如下:

  •        classpath 'com.google.gms:google-services:4.3.10'\
  •        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'\
  •        classpath 'com.google.firebase:perf-plugin:1.4.0'

修改为\

  •        classpath 'com.google.gms:google-services:4.3.13'\
  •        classpath 'com.google.firebase:perf-plugin:1.4.1'\
  •        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
    \
  •        implementation platform('com.google.firebase:firebase-bom:29.1.0')

修改为\

  •        implementation platform('com.google.firebase:firebase-bom:30.2.0')
  •        implementation 'com.google.firebase:firebase-perf:19.1.1'

修改为:\

  •        implementation 'com.google.firebase:firebase-perf'

\

  •    implementation 'com.google.android.gms:play-services-ads:20.4.0' //20.5.0以上的版本需要将kotlin升级到1.4以上才可以

修改为:\

  •    implementation 'com.google.android.gms:play-services-ads:21.0.0' 
    \

5、PendingIntent适配:

Process: com.meetyou.intl, PID: 13041
java.lang.IllegalArgumentException: com.meetyou.intl: 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.

以Android 12为目标平台的App,在构建PendingIntent时,需要指定Flag为FLAG_IMMUTABLE(建议)或FLAG_MUTABLE二者之一,否则App将崩溃并出现以上警告。

适配方案:

int flags = xxx
if (Build.VERSION.SDK_INT >= 31) {
flags |= FLAG_MUTABLE;
}

需要全局检索一下进行适配

6、Summer相关方法无法找到的问题:

国际版升级到了Android12目前有发现部分模块会存在,动态代理Summer相关方法无法找到的问题。
具体处理方式是build.gradle
1.增加
apply plugin: 'kotlin-kapt'
2.dependencies里增加

//动态代理代码自动生成
kapt "com.google.auto.service:auto-service:${PREGNANCY_AUTO_SERVICE_VERSION}"

7、开屏页适配

运行成功后,我们的开屏启动页从这样xxxxx,当我们适配到这里的时候,compilesdk必须升级为31;

8、ijkplayer相关:android11+target11的设备会出现crash

在target到Android11并且在64位的安卓系统版本11及以上的手机,使用ijkplayer会产生崩溃。这里的原因是Android11对于64位的处理器中,每个指针的第一个字节将被用作标记位,用于ARM的内存标记扩展(MTE)支持。在释放内存的时候如果修改这个标记位程序就会崩溃。

那么ijkplayer在哪里会导致第一个字节被修改了呢,查看这个issues github.com/bilibili/ij… 以及提交记录 github.com/bilibili/ij… 可以看出,主要的原因是之前将指针转换成了int64_t类型导致了精度丢失,修改的地方是将指针转成String或者无符号整形,避免精度丢失导致的首位字节丢失。

解决办法:在application加上:android:allowNativeHeapPointerTagging="false"解决

9、获取网络类型相关: TelephonyManager.getNetworkType

运行时崩溃:

****** Crash INFO AT 04/01/2022 10:16 ****java.lang.SecurityException: getDataNetworkTypeForSubscriberandroid.os.Parcel.createExceptionOrNull(Parcel.java:2389)android.os.Parcel.createException(Parcel.java:2373)android.os.Parcel.readException(Parcel.java:2356)android.os.Parcel.readException(Parcel.java:2298)com.android.internal.telephony.ITelephonyStubStubProxy.getNetworkTypeForSubscriber(ITelephony.java:8762)android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3024)android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2988)

TelephonyManager.getNetworkType被标记为deprecated,需要改成使用 getDataNetworkType ,并且需要加上权限READ_PHONE_STATE 或者 READ_BASIC_PHONE_STAT。

App影响:由于我们没有读取权限,所以android12获取不到网络类型;

参考文章:

1、mp.weixin.qq.com/s/rA-1f8aa4…

2、mp.weixin.qq.com/s?__biz=MzA…

3、events.jianshu.io/p/1913b48f2…