一. 事情布景
若是有和我相同, 要上线Google Play Store的同学, 那你们也应该知道, Google现在要求: “在8月31号之前, targetSDK要升为33”
政策改动
曾经的政策是:
- 新app, 要求8月31号之前, targetSDK晋级到X
- 老app, 要求11月1号之前, targetSDK晋级到X
现在的政策则变了, 对于新或老app, 全都一视同仁, 要求在8月31号之前, targetSDK要升到某一版别号. 并且时刻便是每年的8月31号了.
因而, 我原本计划是9月进行的晋级targetSDK作业, 提早到了7月底开端进行. 也因而碰到了多个坑, 血淋淋的坑, 填这些坑花了两三天时刻. 下面分享出来, 究竟离8月底的deadline已经很近了, 期望本文对有需求的同学有所协助.
二. 晋级targetSDK 33
晋级targetSDK蛮简单的, 便是去 app/build.gradle
中把compileSDK, targetSDK
全升成33就行了.
但其实的作业却要多一些, 要去Android 13 features and chagnes list官网上看各种新加的东西. 比方新加了POST_NOTIFICATION, 咱们app就要动态处理, 否则notification底子不会出来 (当然也不会crash啦); …
网上关于晋级targetSDK到33, 要注意哪些 (如新权限, 新功用, 现有功用的改变), 文章有不少的. 我就不写重复写了. 我当时晋级自己工程的代码库主要是参阅了这几个文章:
- 文章1
- 文章2
- 文章3
- 官网阐明1
- 官网阐明2
有了上面5篇文章, 基本上代码库的晋级就没问题了. 但其实晋级并没有完成, 这种晋级targetSDK可能还会引发各种连锁反应. 这些连锁反应是网上少有文章触及的, 所以才是我这文章的重点
三. 晋级targetSDK引发的开发东西呈现问题
3.1 Android Studio (下文简称AS)
布景
由于讨厌新的logcat格局, 所以我一向呆在android studio bumblebee, 一向都没有晋级Android Studio.
问题
晋级了targetSDK后, 我发现许多功用没有了. 比方说我新建个xml, 然后在里面输入”id”, 一般来说, 是有这样的提示的:
或是输入layout_width后, 有可选值的提示:
现在这些智能提示通通没了. 找了一些资料, 才发现, 是说targetSDK晋级了的原因.
所以我把targetSDK从刚晋级到的33, 回滚到原本的31. 公然, 上面这些智能提示全有了.
处理
这下破案了: targetSDK晋级导致我的Android Studio BumbleBee不再支撑一些功用.
所以若想有更好的开发体验, 我要么降级targetSDK (这一点必定不行, 由于google不让), 要么便是晋级Android Studio.
查看了下AS官网, 发现现在的稳定版已经出到了’长颈鹿’了, 也便是AS Giraffe. 本着’不想每年都折腾这么一下’的想法, 就直接升到最新的稳定版吧. 所以我就下载了AS Giraffe, 并装置成功.
后续
所以潘多拉魔盒翻开了, 各种问题接踵而来.
3.2 Android Gradle Plugin (下文简称AGP)
AS的官网上其实已经写明: 晋级了AS, 那AGP也要晋级! 它们二者的对应联系如下:
我原本的AGP版别是4.1, 现在也本着干脆就升到最新稳定版的思维, 我就想升到Giraffe支撑的最高版别, 也便是AGP 8.1.
手动晋级AGP是比较累的, 由于要改的东西许多, 还触及到从4.1到8.1这么多个版别, 所以手动晋级是不可能手动晋级的, 这辈子都不可能手动晋级的. AS里东西这么多, 说话又好听, 必定要用AS的东西啊.
东西就在这儿: Tools -> AGP Upgrade Assistant…
点开来后, 你能够挑选, 从当时AGP版别, 晋级到哪个版别. 然后点击”Run selected steps”即能主动完成晋级了:
晋级成功后样子如下:
再看下Git改变记录, 你就知道, 有manifest文件被改动了, 还有多个gradle文件改了. 这要是自己手动来晋级AGP怕要累死, 所以还是东西好
现在的根目录下的build.gradle已经是这样的了:
plugins {
// AGP版别
id 'com.android.application' version '8.1.0' apply false
id 'com.android.library' version '8.1.0' apply false
// Kotlin版别 (没变)
id 'org.jetbrains.kotlin.android' version '1.5.31' apply false
}
备注: 若有同学对AGP晋级了, kotin版别是否要晋级有疑问, 请放心, AGP与kotlin二者不相关, 互相独立. 所以晋级了AGP, 你的kotlin仍能够呆在1.5 或 1.6 .
— 说这一点, 是由于应该有不少同学还在用kotlin-android-extensions
吧, 这个在kotlin 1.8被remove了. 所以你要是晋级到了kotlin 1.8, 那就得对项目进行大幅修正, 全面用findViewById
或是ViewBinding
了. 这个kotlin的版别问题, 后面会再讲, 不用担心
JDK改变
要是你碰到什么android LintModelSeveirty requires Java 61, the current is 55
的问题, 那阐明你的JDK不行.
JDK的内部版别如下:
49 = Java 5 ; 50 = Java 6 ; 51 = Java 7 ; 52 = Java 8
53 = Java 9 ; 54 = Java 10
55 = Java 11 ; 56 = Java 12 ; 57 = Java 13 ; 58 = Java 14; 59 = Java 15 ; 60 = Java 16
61 = Java 17 ; 62 = Java 18 ; 63 = Java 19 ; 64 = Java 20
所以这个过错便是说, 你用的是JDK 11, 但需求运用JDK 17才行.
原因
原因是曾经用JDK 11就行, 但自从AGP 8.1开端, 就需求运用JDK 17 (如下图所示).
处理
要是你下载了新AS, 那直接运用AS内置的JDK就行. 你能够在AS里这样设置, 在Settings -> Build, Execution, Deployment -> Buiild Tools -> Gradle -> 右侧 Gradke JDK
里选中jbr-17
即可 (图中的jbr是指jetbrains runtime的意思啦)
3.3 修正各种AGP晋级引发的问题
晋级了AGP后, 就引发各种牛鬼蛇神, 下面我就逐个简略地说一下怎么处理它们.
java.util.Set过错
按Stackoverflow上的阐明, 晋级google play service即可
Apple Chip问题
要是你用的是Mac的M1, M2芯片, 那你的ROOM可能会在晋级后build失利
解法有多种:
1). 要么晋级ROOM到2.4.0以上
2). 要么不用AS自带的JDK 17, 而去Open JDK中下载一个JDK 17, gradle去用这个Open JDK 17
3). 要么就在gradle的依赖里加一句kapt "org.xerial:sqlite-jdbc:3.34.0"
这三种办法都能够. 榜首种办法便是还要改变一些代码, 由于ROOM的api有点改变. 别的两种办法更简单些.
3.4 晋级Kotlin与Androidx包
晋级Kotlin
应该有不少同学还在运用kotlin-android-extensions
这个插件吧, 只需用id就能得到一个view, 很方便的
可是这个插件在Kotlin 1.8中被彻底删除了. 所以现在还不想大规模修正这个问题, 就得保证你的kotlin在1.8版别之下. 比方说: 在1.8之下最高的版别, 即1.7.22
改kotlin版别还是蛮简单的, 去根目录下的build.gradle里修正
plugins {
// AGP版别
id 'com.android.application' version '8.1.0' apply false
id 'com.android.library' version '8.1.0' apply false
// Kotlin版别 (晋级了)
id 'org.jetbrains.kotlin.android' version '1.7.22' apply false //原本是1.5.31
}
晋级后会有一些kt文件有些问题, 但这些都是null与non-null的小问题, 修正起来不难.
晋级androidx
androidx的几个要害包, 如core-ktx
, appcompat
, activity
, fragment
, lifecycle
都是和targetSDK或kotlin版别绑定得很死的. 你要是用了不对的版别, 那就会由于和kotlin不匹配而编译失利.
通过我逐个试错, 发现下面的版别是最适合targetSDK = 33的, 再高就要编译失利了:
// core-ktx 1.9.0与Android 13 (API 33)更兼容; 版别再高如1.10.0就编译出错, 由于它需求kotlin 1.8版别
implementation 'androidx.core:core-ktx:1.9.0'
//targetSDK = 33后, 能够从1.4.1升到最新的1.6.1
implementation 'androidx.appcompat:appcompat:1.6.1'
// targetSDK = 33后, 从1.6.0可升到最新的1.9.0. // 再也没这个编译过错了: "Can't determine type for tag '<macro name="m3_comp_assist_chip_container_shape">?attr/shapeAppearanceCornerSmall</macro>'"
implementation 'com.google.android.material:material:1.9.0'
// targetSDK = 33后, 版别不能是最新的1.7.1与1.6.0, 否则会由于kotlin-stdlib要运用1.8而编译不能
implementation "androidx.activity:activity-ktx:1.5.0"
implementation "androidx.fragment:fragment-ktx:1.5.0"
// targetSDK = 33后, 不能高到2.6去, 否则会由于kotlin-stdlib要运用1.8而编译不能
def lifecycle_version = "2.5.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
小细节
别的请注意, 若你曾经用了lifecycle-extensions
包, 那现在这个包没了
这个lifecycle-extensions
包里的内容分散到了独立的更小的包里了, 如曾经在lifecycle-extensions
包里的ProcessLifecycleOwner类, 现在已经在 implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
包里了.
3.5 CircleCI新问题
打debug包失利
通过上面几步, 我在本地打debug包都没问题了. 但到了CI/CD上 (咱们公司用的是CircleCI), 成果呈现打包失利
查了良久, 发现网上说的
1). 设置executor
executors:
java17:
docker:
- image: 'cimg/openjdk:17.0'
2). 自己下载jdk 17
steps:
- checkout
- run:
name: Install OpenJDK 17
command: |
sudo apt-get update && sudo apt-get install openjdk-17-jdk
sudo update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java
sudo update-alternatives --set javac /usr/lib/jvm/java-17-openjdk-amd64/bin/javac
java -version
这两个办法全都没用, 不能处理”不能打包”的问题
接着查, 才知道原本CircleCI的docker镜像是有设置JAVA_HOME的, 如android:2023.07
中就运用了JDK17的JAVA_HOME环境变量的.
所以处理办法便是便是晋级android image:
成果便是通过了上面的修正后, build debug apk成功!
打release包失利
但后来又发现, 打release包竟然又失利了. 好在这次的失利, AGP帮咱们搞出了原因:
> Task :app:minifyStagingReleaseWithR8
> ERROR: Missing classes detected while running R8.
Please add the missing classes or apply additional keep rules
that are generated in /home/circleci/repo/xxx/build/xxx/mapping/stagingRelease/missing_rules.txt.
> ERROR: R8: Missing class com.abc.camera.hardware.VersionInterval
(referenced from: com.abc.hardware.Version com.abc.internal.DeviceInfo.mEglVersions and 4 other contexts)
这下AGP帮咱们指出了哪些新规则需求增加, 咱们把这个missing_rules.txt
文件中的内容, 增加到自己项目里的proguard-rules.pro
文件中就行了.
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.abc.hardware.VersionInterval
-dontwarn com.abc.internal.DeviceInfo.mEglVersions
-dontwarn com.abc.internal.client.ClientCallback
公然, 现在打release包也成功了
四. release包的crash
上面通过修正后, CircleCI打一个release包是没问题了, 可是打出来的包一翻开来就crash.
4.1 Androidx Navigation库的问题
由于debug包运转没有crash, 所以必定不是navigation库自己的问题, 应该是minify这儿出了问题.
公然, 找了下网络, 虽然有许多说要自己加这个@Navigator.Name
注解到Navigator子类里, 但要害在于咱们代码里底子没有重写Navigator啊, 那这个crash到底是什么?
其实便是minify这儿, 让一些navigation的东西不要混杂即可. 所以去proguard文件中增加
# java.lang.IllegalArgumentException: No @Navigator.Name annotation found for m
-keepattributes RuntimeVisibleAnnotations
-keep class * extends androidx.navigation.Navigator
那app就能翻开了
4.2 网络恳求后crash
app虽然翻开了, 但只需有网络恳求就会再次crash. 相同也是debug中没这问题, 所以必定也是相似proguard的问题.
以及:
我查了下, 我用的是Retrofit 2.9, 已经是最新版别, 所以更新版原本处理问题的思路是不行了.
那我就去Retrofit官网查了下, 公然Square早就给出了答案, 说R8的编译会有点特别, 所以要加入以下proguard rule即可:
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
# Keep inherited services.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface * extends <1>
# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
4.3 本节小总结
由于debug包运转起来没问题, 只要release包运转起来才crash, 所以必定是release包里有的, debug包里没有的东西, 导致了这次crash, 比方说: shrinkResource, 或是 proguard.
公然, 这几次满是在proguard这儿出问题了.
五. 总结
原本以为简单的晋级targetSDK, 成果发现连带着还要把AS, AGP, JDK, Kotlin, Androidx, Google Play Service, …这些东东逐个晋级一下. 并且还碰到了很多稀奇古怪的过错, 跟咱们代码无关的过错, 整个的修正过程还是很累人的.
期望我踩过并填了的坑的经验, 能帮到更多的人吧, 究竟8月31号就要到了, 要晋级的怕是时刻也不多了.