Android 12 适配升级小结

    Android 12 发布已经有 4 个月了,而且随着各大市场对应用适配的要求逐渐提高,小菜也尝试将一个历史的应用简单升级适配 Android 12

Android 12

    Android 12 对应 Build.VERSION_CODES.S,使用 Material You 打造的全新系统界面,富有表现力、活力和个性。使用重新设计的微件、AppSearch、游戏模式和新的编解码器扩展您的应用。支持隐私信息中心和大致位置等新的保护功能。使用富媒体内容插入功能、更简便的模糊处理功能、经过改进的原生调试功能等提高工作效率。

    Android 12 相对我们的历史项目来说属于较大版本的更新,在适配过程中遇到一系列问题,小菜简单记录整理一下。

SDK 版本号升级

    小菜首先对 SDK 版本号进行升级,之后对升级后的应用逐步进行适配更新;

当前版本

minSdkVersion = 17
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'

升级后版本

minSdkVersion = 17
targetSdkVersion = 31
compileSdkVersion = 31
buildToolsVersion = '31.0.0'

Q1: Gradle 不匹配

    升级 SDKsync 后遇到第一个 Gradle 不匹配问题;

Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager.

A1: 升级 Android 12 对应 Gradle 版本

    历史版本 Gradle 对应版本是 3.3.3,升级到最新的 7.0.4

classpath 'com.android.tools.build:gradle:7.0.4'

Q2: distributionUrl 不匹配

Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin [id 'com.android.internal.version-check']
    at com.android.build.gradle.BasePlugin.apply(BasePlugin.kt:33)
    at com.android.build.gradle.LibraryPlugin.apply(LibraryPlugin.kt:26)
    at build_21d4k8dpcp55f408j9ar3yifm.run(/Users/user/Documents/workspace/App/adlibrary/build.gradle:1)
Caused by: java.lang.RuntimeException: Minimum supported Gradle version is 7.0.2. Current version is 6.1.1. If using the gradle wrapper, try editing the distributionUrl in /Users/user/Documents/workspace/App/gradle/wrapper/gradle-wrapper.properties to gradle-7.0.2-all.zip
    at com.android.build.gradle.internal.plugins.VersionCheckPlugin.apply(VersionCheckPlugin.kt:59)
    at com.android.build.gradle.internal.plugins.VersionCheckPlugin.apply(VersionCheckPlugin.kt:33)
    ...

A2: 升级 Android 12 对应 distributionUrl Gradle 版本

    小菜将本地 gradle-wrapper.properties 中升级到与 classpath 一致的 7.0.2-all 即可;

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

Q3: Marven 仓库不支持 Http

    小菜历史项目中引入了很多公司内部仓库和部分非 Https 的线上库,在 Gradle 升级后,Marven 仓库建议使用 Https 安全的仓库;

Could not resolve all dependencies for configuration ':classpath'.
   > Using insecure protocols with repositories, without explicit opt-in, is unsupported. Switch Maven repository 'maven(http://0.0.0.0:80/xxx/App)' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. 
   See https://docs.gradle.org/7.0.2/dsl/org.gradle.api.artifacts.repositories.UrlArtifactRepository.html#org.gradle.api.artifacts.repositories.UrlArtifactRepository:allowInsecureProtocol for more details. 

A3: 升级 Https 线上库或解决安全警告

    对于部分线上 Marven 仓库可以更新至 Https,对于不可更新的库可以通过添加 allowInsecureProtocol 属性解决 Gradle 仓库地址的不安全警告;

repositories {
    maven {
      url "http://0.0.0.0:80/xxx/App"
      allowInsecureProtocol = true
    }
}

Q4: compile 弃用

    小菜的历史项目中有个别 Module 中未及时修改 compile(),而 Gradle 升级之后已完全弃用 compile()

A problem occurred evaluating project ':lib'.
> Could not find method compile() for arguments [directory 'libs'] on object of type
    org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

A4: 更新为 api 或 implementation

    根据具体的业务需求将 compile() 更新为 api / implementation 即可;

api fileTree(dir: 'libs', include: ['*.jar'])

Q5: Android 工程依赖的 Java 版本过低

A problem occurred evaluating project ':lib'.
> Failed to apply plugin 'com.android.internal.library'.
   > Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
     You can try some of the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing `org.gradle.java.home` in `gradle.properties`.

A5: 更新 Android 依赖版本为 jdk 11.0.13

    通过 AndroidStudio -> Preferences... -> Gradle 更新 jdk 版本即可;

Q6: AGCPluginTask 中 randomEncryptComponent 属性不应使用 @Optional 进行注释

    小菜的历史项目中使用了 Huawei HMS 推送等,使用的 Marven 库版本较低,与升级后的 Gradle 不兼容;

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':push:processDebugManifest'.
> A failure occurred while executing com.android.build.gradle.tasks.ProcessLibraryManifest$ProcessLibWorkAction
   > Manifest merger failed with multiple errors, see logs

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
A problem was found with the configuration of task ':app:processDebugAGCPlugin' (type 'AGCPluginTask').
  - Type 'com.huawei.agconnect.agcp.AGCPluginTask' property 'randomEncryptComponent' of type boolean shouldn't be annotated with @Optional.
    
    Reason: Properties of primitive type cannot be optional.
    
    Possible solutions:
      1. Remove the @Optional annotation.
      2. Use the java.lang.Boolean type instead.
    
    Please refer to https://docs.gradle.org/7.0.2/userguide/validation_problems.html#cannot_use_optional_on_primitive_types for more details about this problem.

A6: 升级华为 HMS 库版本

classpath 'com.huawei.agconnect:agcp:1.5.2.300'

Q7: 使用 Intent 过滤器的 Service 需设置 exported 属性

    此元素设置 Activity 是否可由其他应用的组件启动 —“true”表示可以,“false”表示不可以。若为“false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。
    如果您使用的是 Intent 过滤器,则不应将此元素设置为“false”。否则,在应用尝试调用 Activity 时,系统会抛出 ActivityNotFoundException 异常。相反,您不应为其设置 Intent 过滤器,以免其他应用调用 Activity。

    如果没有 Intent 过滤器,则此元素的默认值为“false”。如果您将元素设置为“true”,则任何知道其确切类名的应用均可访问 Activity,但在系统尝试匹配隐式 Intent 时,该 Activity 无法解析。

    此属性并非是限制 Activity 向其他应用公开的唯一方式。您还可使用权限来限制哪些外部实体能够调用 Activity

/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <service>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. 
    See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <service>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. 
    See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A7: 在所有 Module 中找到使用 Intent 过滤器的 Service 并按业务需求添加对应的 exported 属性

<service
    android:name="com.xxx.app.push.OPushMessageService"
    android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE"
    android:exported="true">
  <intent-filter>
    <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
  </intent-filter>
</service>

Q8: 使用 Intent 过滤器的 Receiver 需设置 exported 属性

/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <receiver>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <receiver>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A8: 在所有 Module 中找到使用 Intent 过滤器的 Receiver 并按业务需求添加对应的 exported 属性

<receiver android:name="com.xxx.app.SystemReceiver"
    android:exported="false">
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    <action android:name="android.intent.action.USER_PRESENT" />
  </intent-filter>
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_REMOVED" />

    <data android:scheme="package" />
  </intent-filter>
</receiver>

Q9: 使用 Intent 过滤器的 Activity 需设置 exported 属性

/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <activity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <activity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A9: 在所有 Module 中找到使用 Intent 过滤器的 Activity 并按业务需求添加对应的 exported 属性

<activity
    android:name=".xxx.app.TestActivity"
    android:exported="false"
    android:theme="@style/Theme.notAnimation">
  <intent-filter>
    <action android:name="com.sogou.novel.reader.setting.clean" />

    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

Q10: PendingIntent 需声明可变性

    在 Android 12 中创建 PendingIntent 的时候,需要显示的声明是否可变,请分别使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE 标志,如果您的应用试图在不设置任何可变标志的情况下创建 PendingIntent 对象,系统会抛出 IllegalArgumentException 异常;

PACKAGE_NAME: Targeting S+ (version 10000 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.

A10: 根据业务设置 PendingIntent 可变性

    PendingIntent 是一个可以给另一个应用程序使用的 IntentPendingIntent 接收待处理意图的应用程序可以使用与产生待处理意图的应用程序相同的权限和身份执行待处理意图中指定的操作;

    在 Adnroid 12 之前,默认创建一个 PendingIntent 它是可变的,因此其他恶意应用程序可能会拦截,重定向或修改此 Intent

PendingIntent pendingIntent = PendingIntent.getBroadcast(getContext().getApplicationContext(), type, intent, PendingIntent.FLAG_IMMUTABLE);
            

W11: 避免使用 flatDirs 提醒

    Gradle 升级之后,提示避免使用 flatDir 提醒,因该方式不支持任何元数据方式;

Using flatDir should be avoided because it doesn't support any meta-data formats.
Using flatDir2 should be avoided because it doesn't support any meta-data formats.

A11: 使用 jniLibs.srcDirs 方式引入 libs 库

    Gradle 升级之后使用 jniLibs.srcDirs 方式替代 flatDirlibs 库引入,并更新 aar 引入方式;

当前版本

repositories {
    flatDir {
        dirs 'libs'
    }
}

implementation(name: 'test_name', ext: 'aar')

升级后版本

android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

implementation (files("libs/test_name.aar"))

W12: dexOptions 弃用提醒

DSL element 'dexOptions' is obsolete and should be removed.
It will be removed in version 8.0 of the Android Gradle plugin.
Using it has no effect, and the AndroidGradle plugin optimizes dexing automatically.

A12: Gradle 升级后 dexOptions 已弃用,删除即可

dexOptions {
    preDexLibraries = true
}

Tips:

    小菜在测试过程中,明明代码中所有涉及 intent-filter 过滤器的 Activity / Service / Receiver 都已经设置了 exported 属性,但依旧提示使用 Intent 过滤器的 XX 需设置 exported 属性;其原因在于引入了各类三方 SDK,在引入的各类三方库中可以存在对应的未设置 exported 属性的 Activity / Service / Receiver,单独设置处理一下即可;


    Android 12 的初步升级到此位置,还有很多特有的属性,小菜会在后续的适配中进行完善;如有错误,请多多指导!

阿策小和尚

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容