本文字数:9048字
预计阅读时间:40分钟
在狐友项目的编译优化中,我们发现在 BuildAnalyzer 中有明确的 Warnings 提示,告知项目可以进行 Jetifier 优化。
Jetifier 是之前项目进行 AndroidX 迁移时引入的插件,它能辅助迁移三方库到 AndroidX。
Jetpack 套件能够帮助你更轻松的搭建高质量的 APP,它包括了依赖库、工具和指导。它通过最佳实践、模板代码、以及简化复杂的任务来使得编码更轻松。让你更专注于你所关心的(业务)。 而 AndroidX 是所有 Jetpack 库的包名,你可以把它理解成是开发、测试、发版和发布 Jetpack 库时用到的开源项目。 在2018年的 I/O 大会上,Google 宣布 Support 库会以 AndroidX 重新命名,Support 28后不再更新,发布AndroidX 1.0。 Jetifier 工具,辅助迁移三方库到 AndroidX。它通过修改依赖库的字节码来适配 AndroidX 的工程。
01
引入Jetifier插件
引入 Jetifier 插件辅助迁移 AndroidX 非常简单,只需要在配置文件中开启 enableJetifier 功能:
// gradle.properties 中添加
# AndroidX,Android插件会对源码启用Support库的使用检测和转换。默认false,必须配置打开。
android.useAndroidX=true
# AndroidX,Android插件会通过重写其二进制文件来自动迁移现有的第三方库(aar包或者jar包等依赖库)。默认false,必须配置打开。
android.enableJetifier=true
当你在项目中启动用 Jetifier 时(android.enableJetifier = true),Gradle 插件会在构建时将三方库里的 Support 引用转换成对应的 AndroidX 引用,包括如下部分:
Class 类里引入的 Support 库;
layout 里用到的 Support 库;
Manifest 指向 Support 库的类。
这个 Jetifier 插件每次编译运行,都会对构建速度产生影响。因此,关闭 Jetifier 有助于加快构建速度。
02
移除Jetifier插件
Jetifier 在 AndroidX 刚出现时是一个非常实用的工具,可以帮助我们快速迁移到 AndroidX。但是现在绝大多数库都已经迁移到了 AndroidX,是时候移除 Jetifier 插件,提高我们的编译速度了。
AGP7.0 已经提供了一个工具,帮忙我们检查各个模块能否移除 Jetifier。
直接运行 gradle checkJetifier,或点击 gradle 任务列表中的 checkJetifier 任务,检查所有模块的 Support 库使用情况:
运行 task 会输出各模块的检测结果,可以通过 log 查看到所有模块的 Support 使用情况,和是否可以移除 Jetifier 插件。
//运行 checkJetifier task
gradle checkJetifier
// 输出log
......
> Task :comm_lib:checkJetifier
The following libraries used by project ':comm_lib' depend on legacy support libraries. To disable Jetifier, you will need to use AndroidX-supported versions of these libraries.
com.github.bumptech.glide:okhttp3-integration:4.8.0 (Project ':comm_lib', configuration 'debugAndroidTestCompileClasspath' -> com.github.bumptech.glide:okhttp3-integration:4.8.0 -> com.android.support:support-annotations:27.1.1)
com.meituan.android.walle:library:1.1.6 (Project ':comm_lib', configuration 'debugAndroidTestCompileClasspath' -> com.meituan.android.walle:library:1.1.6 -> com.android.support:support-annotations:24.1.1)
> Task :share_module:checkJetifier
Project ':share_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :report_module:checkJetifier
Project ':report_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :photoedit_module:checkJetifier
Project ':photoedit_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :local_repository:mmkv_sdk:checkJetifier
Project ':local_repository:mmkv_sdk' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :app:checkJetifier
The following libraries used by project ':app' depend on legacy support libraries. To disable Jetifier, you will need to use AndroidX-supported versions of these libraries.
com.alibaba.android:vlayout:1.2.32 (Project ':app', configuration 'flavorsDevDebugAndroidTestCompileClasspath' -> com.alibaba.android:vlayout:1.2.32 -> com.android.support:recyclerview-v7:25.2.0)
com.sothree.slidinguppanel:library:3.3.0 (Project ':app', configuration 'flavorsDevDebugAndroidTestCompileClasspath' -> com.sothree.slidinguppanel:library:3.3.0 -> com.android.support:support-v4:23.1.1)
com.android.support:palette-v7:28.0.0 (Project ':app', configuration 'flavorsDevDebugAndroidTestCompileClasspath' -> com.android.support:palette-v7:28.0.0 -> com.android.support:support-v4:23.1.1)
根据 log,我们可以总结梳理出,需要 Jetifier 进行 Androidx 迁移的库有:
com.github.bumptech.glide:okhttp3-integration:4.8.0 ;
com.meituan.android.walle:library:1.1.6 ;
com.alibaba.android:vlayout:1.2.32 ;
com.sothree.slidinguppanel:library:3.3.0 ;
com.android.support:palette-v7:28.0.0 ;
得到还需要使用 Jetifier 的依赖库列表后,我们就可以开始手动迁移这些库到 AndroidX 了。根据不同情况,尽量使用简单的方法处理。
最新版本已经支持了 AndroidX 的,直接升级到新版本即可;
如果有源码,可以使用源码,添加 android.useAndroidX = true,编译出支持AndroidX 的新版本,将所有依赖更新到这个新版本;
没有源码,则使用 jetifier-standalone (https://developer.android.google.cn/tools/jetifier?hl=en) 命令行工具,把 aar/jar 转成支持 AndroidX 的新版本。
// jetifier-standalone 这个命令行的转换效果,
// 和在代码里开启android.enableJetifier的效果是一样的
// 如果aar中有Support库,会提示[TypeRewriter]
jetifier-standalone -i source.aar -o output.aar -l info
//输出log
INFO: [Archive] Extracting nested: annotations.zip
INFO: [Archive] Extracting nested: classes.jar
INFO: [Processor] Started new transformation
INFO: [Processor] - Input file: source.aar
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/IntDef -> androidx/annotation/IntDef
INFO: [TypeRewriter] Map: android/support/annotation/IntDef -> androidx/annotation/IntDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/Keep -> androidx/annotation/Keep
INFO: [TypeRewriter] Map: android/support/annotation/RequiresApi -> androidx/annotation/RequiresApi
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [TypeRewriter] Map: android/support/annotation/ColorInt -> androidx/annotation/ColorInt
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [Archive] Deleting old output file
INFO: [Archive] Writing archive: file:output.aar
根据梳理出的方法方法,我们对项目中需要进行 Androidx 迁移的库进行了相应处理:
com.github.bumptech.glide:okhttp3-integration:4.8.0,项目引入的 glide 库使用了 Support 库,其新版本已经支持 AndroidX,升级版本即可;
// 在app的build.gradle 中修改:
Implementation("com.github.bumptech.glide:okhttp3-integration:4.14.2")
com.meituan.android.walle:library:1.1.6,最新版本也不支持 AndroidX,能找到其源码,且源码已经有几年没更新过了。我们可以下载源码,添加 enableJetifier 配置后,编译出新库,并修改依赖为新库;
// 从https://github.com/Meituan-Dianping/walle 下载源码,修改配置文件,编译新库
// gradle.properties 中添加
android.useAndroidX=true
android.enableJetifier=true
// 编译完后,在comm_lib的build.gradle.kts中修改依赖新库:
Implementation(files("libs/hy_walle.aar"))
com.alibaba.android:vlayout:1.2.32,最新版本也不支持AndroidX,也没有源码,可以使用 jetifier-standalone 工具将转换为支持 AndroidX的aar ;
// 将~/.gradle/caches/modules-2/files-2.1/com.alibaba.android/vlayout/1.2.32/7ffbe82e19f764be17415c0e639b2ba690f04f2a/vlayout-1.2.32.aar
// 转换成支持AndroidX的aar。命令如下
jetifier-standalone -i vlayout-1.2.32.aar -o hy_vlayout-1.2.32.aar
// 转换完后,在app的build.gradle.kts中修改依赖:
Implementation(files("hy_vlayout-1.2.32.aar"))
com.sothree.slidinguppanel:library:3.3.0 ;
// 将~/.gradle/caches/modules-2/files-2.1/com.sothree.slidinguppanel/library/3.4.0/a46c103238d666c097f6fefcffb479ebb450d365/library-3.4.0.aar
// 转换成支持AndroidX的aar。命令如下
jetifier-standalone -i library-3.4.0.aar -o hy_slidinguppanel_library-3.4.0.aar
// 转换完后,在app的build.gradle.kts中修改依赖:
Implementation(files("hy_slidinguppanel_library-3.4.0.aar"))
com.android.support:palette-v7:28.0.0,项目直接引入的 Support 库的,直接替换成 AndroidX 版本;
// 在app的build.gradle 中修改:
Implementation("androidx.palette:palette:1.0.0")
完成三方库的 AndroidX 迁移后,我们就可以移除 Jetifier 插件了。
// gradle.properties 中添加
# AndroidX,Android插件会对源码启用Support库的使用检测和转换。默认false,必须配置打开。
android.useAndroidX=true
# AndroidX,Android插件会通过重写其二进制文件来自动迁移现有的第三方库(aar包或者jar包等依赖库)。
# 已经优化处理了所有含support库的代码和第三方库,可以不引入Jetifier插件了
android.enableJetifier=false
移除 Jetifier 插件后,进行对比,发现编译时间平均减少了23s,对编译耗时优化有一些效果。
03
新问题
前节中,移除 Jetifier 插件后,项目编译速度优化有了一些效果,但是却引入了一个 bug:人脸识别库的接口调用失败,导致产品中的人脸识别验身功能不 work 了!!!
通过分析 log,发现 hy-wbface-1.1.0.aar 库中的一个类,因为找不到 Android Support 相关的类,导致调用返回错误。
那为什么我们之前通过 checkJetifier 检测,并没有提示 hy-wbface-1.1.0.aar 使用了Android Support 库呢?我们可以再次使用 checkJetifier 验证一下,是我们之前看漏了吗?
点击 Gradle task 列表中的 checkJetifier,运行 task:
checkJetifier task 任务的输出 Log 如下, 可以看到,并没有提示 hy-wbface-1.1.0.aar 使用了 Android Support 库:
//优化后,运行 checkJetifier task
gradle checkJetifier
> Task :comm_lib:checkJetifier
Project ':comm_lib' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :share_module:checkJetifier
Project ':share_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :report_module:checkJetifier
Project ':report_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :photoedit_module:checkJetifier
Project ':photoedit_module' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :local_repository:mmkv_sdk:checkJetifier
Project ':local_repository:mmkv_sdk' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
> Task :app:checkJetifier
Project ':app' does not use any legacy support libraries. If this is the case for all other projects, you can disable Jetifier by setting android.enableJetifier=false in gradle.properties.
所以,我们并没有看漏,而是 checkJetifier 任务确实存在一定的遗漏,需要我们进一步分析,定位问题。
04
原因
那么,Google 提供的 checkJetifier task 任务,为什么没有检测出 hy-wbface-1.1.0.aar 对 Support 库的使用呢?
通过分析,我们发现 checkJetifier task 任务的原理,是通过检测 maven 库 pom 文件的 dependency 信息,来递归检测所有三方库是否依赖了 com.android.support 这个 Android Support 库。
比如,com.github.bumptech.glide:okhttp3-integration:4.8.0 这个库的 pom 文件,就明确表明了:它依赖 Support 库。
但是,因为我们使用了很多第三方库,除了正常的 maven 库引用,还有另外2种情况。而这2种情况,checkJetifier task 任务是无法检测出它们对 Support 库的使用情况:
直接使用第三方提供的 jar/aar 文件,没有 maven,所有也没有 pom 文件。这些 jar/aar 如果依赖了 Support 库,自然无法被检测出来;
我们封装过的 maven 库,包含了第三方的 jar/aar 文件,但是没有完整配置 pom 文件的 dependency 信息,导致无法检测出 Support 库的使用情况。hy-wbface-1.1.0.aar 库出现的 bug,就属于这类。
所以,当我们关闭 Jetifier 插件后,由于 APP 没有替换掉 hy-wbface-1.1.0.aar 中对 Support 库的使用,也没有把 Support 库编译进 APK 中,导致 hy-wbface-1.1.0.aar 调用 Support 库时,找不到对应的类,自然就会返回失败!
05
方案
为了解决上面的 bug,同时也为了编译优化的防恶化管控,我们需要设计一个更为合理的解决方案。方案不但需要要检测出所有三方库对 Support 库的依赖情况,还需要避免后续引入三方库,导致 Jetifier 优化被再次破坏,同时也需要方案不增加团队其它开发人员的工作量。方案目标有:
首先,方案需要把应用依赖的所有三方库找出来;
其次,方案需要判断出哪些第三方库需要依赖 Support 库;
接着,方案能自动把依赖 Support 库的三方库迁移到支持 Androidx 库;
最后,方案能不增加开发人员的工作量,能自动发出检测报告给相关开发。
1、依赖库检测
首先,我们需要先把应用依赖的所有三方库梳理出来。Gradle 提供了2个 task 检测项目的依赖库,如下图红色框的 task:
androidDependencies,该 task 会平铺展示依赖树,且只展示几个主要configuration 的依赖,较为清爽,但是不能像方式二那样指定 configuration。这个方式除了 maven 依赖的库,也会显示本地 libs 中放的依赖库;
dependencies,该 task 的输出列表展示了所有 configuration 下的依赖树,依赖关系明显,层次清晰。输出的结果包含几十个 configuration 的依赖,较为冗余,可以通过指定 configuration 来显示特定的依赖树。如,gradle :app:dependencies --configuration debugCompileClasspath,该命令只会显示 debug 模式下编译过程中的依赖树。此方式只会显示 maven 依赖的库;
除了上述 Gradle 提供的2个依赖库检测 task,我们还可以通过自定义脚本的方式来获取依赖库。
因为上述2个 gradle 自带 task 的输出,对我们后续进行 SUPPORT 依赖库判断不是很方便,而通过自定义脚本来检测依赖,可以更好更灵活的控制整理第三方依赖库的输出。
我们通过 configuration.resolutionStrategy 的方式,获得所有依赖库。包括嵌套依赖的库,但是不包括本地依赖库。但是 resolutionStrategy 中的依赖遍历,是在一次次回调中输出的,不太适合自定义 task 对依赖做统一处理;
// 在buildsrc的 config.kts中
this.project.configurations.all {
resolutionStrategy.eachDependency {
println("${this.requested.group}:${this.requested.name}:${this.requested.version}")
// 收集第三方依赖库信息
}
}
我们还可以通过 configuration.allDependencies 的方式,获取所有依赖库。这种方式不包括嵌套依赖的库,和本地依赖库。但这种方式获得的依赖库是调用时直接输出的,非常适合后续统一处理。
// 在buildsrc的 config.kts中
// dependenciesMaps:key是依赖库jar/aar的名称, value是jar/aar的absolutePath
// 遍历获取所有maven依赖库的名称,存入dependenciesMaps中的key字段
this.project.configurations.forEach{ configuration ->
configuration.allDependencies.forEach {
dependenciesKey ="${it.name}-${it.version}"
if(!dependenciesMaps.contains(dependenciesKey)) {
// 遍历补全依赖库的文件路径 dependenciesValue,存入dependenciesMaps中的value字段
dependenciesMaps[dependenciesKey] = dependenciesValue
}
}
}
上面2种方式都没有包含本地的依赖库,所以我们继续通过文件查找的方式,将项目中所有 libs 下的 jar/aar 都找出来。
// 在buildsrc的 config.kts中
fun findLibsFiles(rootPath:String, dependenciesMaps:HashMap):HashMap {
val files = File(rootPath).listFiles()
for (f in files.indices) {
if (files[f].isDirectory) {
var pathSeg = files[f].absolutePath.split("/")
if (!pathSeg[pathSeg.lastIndex].startsWith(".")
&& !pathSeg[pathSeg.lastIndex].startsWith("build")
&& !pathSeg[pathSeg.lastIndex].startsWith("src")
&& !files[f].absolutePath.contains("/info")
&& !files[f].absolutePath.contains("/gradle")) {
//循环遍历
findLibsFiles(files[f].absolutePath,dependenciesMaps)
}
} else if(!files[f].isDirectory
&& (files[f].name.endsWith("jar")
|| files[f].name.endsWith("aar"))) {
//保存libs下jar/aar文件的路径到dependenciesMaps中
dependenciesMaps[files[f].name] = files[f].absolutePath
}
}
return dependenciesMaps;
}
2、检测Support库,并迁移到AndroidX库
有了上一步得到的所有依赖库 dependenciesMaps 后,我们就可以开始 Support 库的使用检测,和 AndroidX 的迁移了。
根据 google 提供的 jetifier-standalone (https://developer.android.google.cn/tools/jetifier?hl=en)工具的介绍,我们可以通过命令行的方式进行 Support 检测和 AndroidX 迁移,以 com.heytap.msp_3.1.0.aar 为例:
// 工具使用
jetifier-standalone -o Hy_com.heytap.msp_3.1.0.aar -l info -i com.heytap.msp_3.1.0.aar
INFO: [Archive] Extracting: com.heytap.msp_3.1.0.aar
INFO: [Archive] Extracting nested: annotations.zip
INFO: [Archive] Extracting nested: classes.jar
INFO: [Processor] Started new transformation
INFO: [Processor] - Input file: com.heytap.msp_3.1.0.aar
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/IntDef -> androidx/annotation/IntDef
INFO: [TypeRewriter] Map: android/support/annotation/IntDef -> androidx/annotation/IntDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/StringDef -> androidx/annotation/StringDef
INFO: [TypeRewriter] Map: android/support/annotation/Keep -> androidx/annotation/Keep
INFO: [TypeRewriter] Map: android/support/annotation/Keep -> androidx/annotation/Keep
INFO: [TypeRewriter] Map: android/support/annotation/RequiresApi -> androidx/annotation/RequiresApi
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [TypeRewriter] Map: android/support/annotation/ColorInt -> androidx/annotation/ColorInt
INFO: [TypeRewriter] Map: android/support/annotation/ColorInt -> androidx/annotation/ColorInt
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [TypeRewriter] Map: android/support/annotation/DrawableRes -> androidx/annotation/DrawableRes
INFO: [Archive] Deleting old output file
INFO: [Archive] Writing archive: Hy_com.heytap.msp_3.1.0.aar
通过输出 log 我们可以看到 com.heytap.msp_3.1.0.aar 库,使用了 Support 库,并且成功迁移到 AndroidX,生成了新的 Hy_com.heytap.msp_3.1.0.aar 库。
由此,我们可以通过 jetifier-standalone 遍历处理我们的三方依赖库,如果有 Support 依赖则自动进行迁移,根据 log 中是否包含 TypeRewriter 记录,整理出迁移报告发送给开发人员:
// 在buildsrc的 config.kts中
println("cjf-checkjetify : ================================")
println("cjf-checkjetify : Please use jetifier-standalone to remove android.support from below jar/aar:")
println("cjf-checkjetify : ================================")
dependenciesMaps.forEach{
var cmd = "/jetifier-standalone -o build/Hy_${it.key} -l info"
cmd = "$cmd -i ${it.value}"
val proc = Runtime.getRuntime().exec(cmd)
val bufferedReader = BufferedReader(InputStreamReader(proc.getInputStream(),"UTF-8"))
log.clear()
var line: String? = bufferedReader.readLine()
while (line != null) {
log.append(line)
log.append("\n")
line = bufferedReader.readLine()
}
if(log.contains("TypeRewriter")) {
println("cjf-checkjetify : ${it.value}")
// add report info here ......
}
}
// send report info to developer here ......
3、添加自定义task
接着,我们通过自定义 task 的方式,将每次 Support 库的检测,和 AndroidX 的迁移整合在一起。最后,在 CI 编译时,自动运行此 task,并将问题报告发送给代码提交者。
首先,在 buildsrc 中添加 hyCheckjetifier 扩展函数,封装检测和迁移相关代码:
// 在buildsrc的 config.kts中
fun Project.hyCheckjetifier() {
var dependenciesMaps = HashMap()
var dependenciesKey:String
var dependenciesValue = ""
//添加CompileClasspath中的所有maven依赖库(不包括pom文件的二级依赖库)
//正规的第三方maven库,pom文件是会完整写上support支持库的依赖的,通过gradel的“checkJetifier”task是可以正确输出support情况的。
//但是我们封装的第三直接提供的jar/aar的maven库,是没有正确的pom文件的,所以需要工具直接代码检测。
this.project.rootProject.allprojects.forEach{
println("cjf-checkjetify : ${it.name}")
it.configurations.forEach{ configuration ->
configuration.allDependencies.forEach {
//收集maven依赖 ......
}
}
}
// 收集本地libs下的所有aar/jar依赖
findLibsFiles(this.project.rootProject.projectDir.absolutePath,dependenciesMaps);
//检测jar/aar, 是否包含Support库代码
val log = StringBuilder()
var line: String?
var cmd = ""
dependenciesMaps.forEach{
cmd = "~/sns/info/jetifier-standalone/bin/jetifier-standalone -o build/Hy_${it.key} -l info"
cmd = "$cmd -i ${it.value}"
// 运行shell命令,获得输出Log
val proc = Runtime.getRuntime().exec(cmd)
val bufferedReader = BufferedReader(InputStreamReader(proc.getInputStream(),"UTF-8"))
log.clear()
line = bufferedReader.readLine()
while (line != null) {
log.append(line)
log.append("\n")
line = bufferedReader.readLine()
}
if(log.contains("TypeRewriter")) {
println("cjf-checkjetify : ${it.key}---${it.value}")
// 输出问题第三方库,并发送报告给Commit Owner
}
}
}
然后,在 APP 的 build.gradle.kts 中创建 hyCheckjetifier task:
// 在app的 build.gradle.kts中, 创建task,并调用hyCheckjetifier方法
task("hyCheckjetifier") {
group = "android"
dependsOn("checkJetifier") //使用gradle自带task,同步检测嵌套库的情况。
doLast {
this.project.hyCheckjetifier() //调用buildsrc中的方法,进行Support库检测和替换
}
}
同步项目后,我们就可以在 Gradle 任务列表的 android 类型中找到对应的 hyCheckjetifier task 了。
通过命令行,或 Gradle 任务面板,运行 task,就会输出相应的检测和迁移报告。如果在 CI 上编译,报告可以发送给对应代码的提交者。
06
问题处理
根据第五节的解决方案,开发人员运行 hyCheckjetifier task 得到的检测报告如下:
cjf-checkjetify : ================================
cjf-checkjetify : Please use jetifier-standalone to remove android.support from below jar/aar:
cjf-checkjetify : ================================
cjf-checkjetify : ***/.gradle/caches/modules-2/files-2.1/com.facebook.stetho/stetho/1.5.0/9ed0e0a9ab9134f34a5bce11ea543f432f9b08b3/stetho-1.5.0.aar
cjf-checkjetify : ***/.gradle/caches/modules-2/files-2.1/io.github.yidun/captcha/3.4.8/af7b441ccbcbbc491b12ff4d3f0024f627188534/captcha-3.4.8.aar
cjf-checkjetify : ***/.gradle/caches/modules-2/files-2.1/com.sohu.hy/hy-wbface/1.1.0/1c7f588a511497c22c567d11fda3041af6cd43d1/hy-wbface-1.1.0.aar
cjf-checkjetify : ***/.gradle/caches/modules-2/files-2.1/com.sohu.hy/hy-amap/1.1.0/2eea2505735530e0165698c5e09fcaf778fa5c89/hy-amap-1.1.0.aar
cjf-checkjetify : ***/.gradle/caches/modules-2/files-2.1/com.sohu.hy/hy-oppopush/1.1.0/fb8344a804fa86538793dcae1d03c7c34f9afb29/hy-oppopush-1.1.0.aar
cjf-checkjetify : ***/sns/share_module/libs/open_sdk_3.5.12.2_r97423a8_lite.jar
根据报告中的6个问题,我们可以归类出4种迁移情况:
stetho-1.5.0.aar 是第三方 maven 库,app通过 debugImplementation 引入。其 pom 文件写了依赖 Support 库,但是 checkJetifier task 没有检测出来;
captcha-3.4.8.aar 是第三方 maven 库,comm_lib 通过 api 引入。其 pom 文件没有写依赖 Support 库。最新版3.5.7仍然依赖 Support 库,但是 pom 文件中没有声明 Support 依赖;
hy-wbface-1.1.0.aar,hy-amap-1.1.0.aar,hy-oppopush-1.1.0.aar 是我们封装过的 maven 库,其 pom 文件的依赖不完整,缺失 Support 库的依赖信息;
open_sdk_3.5.12.2_r97423a8_lite.jar 是我们本地 libs 目录下直接应用的 jar 库。
我们需要对这4种情况分别做处理:
1、升级第三方maven库版本
针对第三方 maven 库 stetho,其最新版本1.6.0不再依赖 Support 库,可以将1.5.0版本升级到1.6.0版本,快速解决问题。
// 在app的build.gradle 中修改:
debugImplementation("com.facebook.stetho:stetho:1.6.0")
2、封装第三方maven库到狐友maven库中
针对第三方 maven 库 captcha,其最新版本3.5.7仍然依赖 Support 库,所以我们需要先用工具将其 Support 库依赖迁移到 AndroidX 库,然后再封装,上传到狐友的 maven 库中。
// 在local_repository中新建captcha_sdk模块,captcha_sdk/build.gradle.kts 文件中添加相关配置:
plugins {
id("com.android.library")
hypublish
}
......
dependencies {
// 此aar是重新打包过的aar。checkJetifier提示用了support库,需要转换一下:
// jetifier-standalone
// -i ~/.gradle/caches/modules-2/files-2.1/io.github.yidun/captcha/3.4.8/af7b441ccbcbbc491b12ff4d3f0024f627188534/captcha-3.4.8.aar
// -o Hy_captcha-3.4.8.aar
// -l info
// 将装换后的Hy_captcha-3.4.8.aar,放在captcha_sdk/build.gradle.kts
api(fileTree("./") {
include("*.aar")
})
// 将captcha-3.4.8的pom文件中的依赖信息加入到配置中,publish时自动封装到Hy_captcha maven库中。
// ~/.gradle/caches/modules-2/files-2.1/io.github.yidun/captcha/3.4.8/d3e43ff4d08e711e11aee3f55b14d7086050fadd/captcha-3.4.8.pom
implementation("io.github.yidun:base-core:1.1.3.1")
implementation("com.github.bumptech.glide:glide:4.9.0")
}
ext {
extra[MavenInfo.KEY_ID] = LocalRepository.captchaSdkName
extra[MavenInfo.KEY_VERSION] = LocalRepository.captchaSdkVersion
extra[MavenInfo.KEY_STRATEGY] = Strategy.publicMaven3Artifact
extra[MavenInfo.KEY_FILE] = "Hy_captcha-3.4.8.aar" //待封装的aar文件
extra[MavenInfo.KEY_FLAVORBUILD] = ""
}
封装后的库,上传到狐友 maven 库,其 pom 文件中丢失了 captcha-3.4.8 对应的其它依赖配置,需要我们的 hypublish 插件添加 pom 依赖自动添加的能力。
// 在hypublish.gradle.kts插件文件中添加,pom文件自动添加dependency的支持:
fun Project.publicMaven(groupValue: String, artifactIdValue: String, versionValue: String, maven_url: String,strategy: String) {
publishing {
publications {
when (strategy) {
......
Strategy.publicMaven3Artifact -> {
val artifactSrc = project.extra.get(MavenInfo.KEY_FILE) as Any
create("release") {
// 封装aar库只能使用artifact,artifactSrc具体值是extra[MavenInfo.KEY_FILE] = "Hy_captcha-3.4.8.aar"
artifact(artifactSrc)
groupId = groupValue
artifactId = artifactIdValue
version = versionValue
pom.withXml {
//artifact的方式,生成的pom文件没有dependency信息,
//需要找到pom文件, 将captcha-3.4.8的所有依赖插入到captcha_sdk库的dependency中
var dependencies = asNode().appendNode("dependencies")
configurations.findByName("releaseCompileClasspath")
?.getResolvedConfiguration()
?.getFirstLevelModuleDependencies()?.forEach {
var dependency = dependencies.appendNode("dependency")
dependency.appendNode("groupId", it.moduleGroup)
dependency.appendNode("artifactId", it.moduleName)
dependency.appendNode("version", it.moduleVersion)
}
}
}
}
......
}
}
}
}
上传到狐友 maven 库后,我们可以在项目中把 captcha 依赖库,修改为狐友的 maven 库 hy-captcha-1.0.0 了。
3、更新狐友maven库
针对我们封装过的 maven 库,hy-wbface-1.1.0.aar,hy-amap-1.1.0.aar,hy-oppopush-1.1.0.aar,先找到 Support 依赖的代码和 aar/jar,进行 AndroidX 迁移,然后再重新打包上传到狐友 maven 库即可。如 hy-oppopush-1.1.0.aar 的build.gradle.kts 文件:
plugins {
hypublish
}
configurations.maybeCreate("default")
artifacts{
// 上传的aar,是通过下面命令重新打包过的aar。
// jetifier-standalone -i com.heytap.msp_3.1.0.aar -o Hy_com.heytap.msp_3.1.0.aar -l info
add("default",file("Hy_com.heytap.msp_3.1.0.aar"))
}
ext {
extra[MavenInfo.KEY_ID] = LocalRepository.oppopushSdkName
extra[MavenInfo.KEY_VERSION] = LocalRepository.oppopushSdkVersion
extra[MavenInfo.KEY_STRATEGY] = Strategy.publicMaven3Artifact
extra[MavenInfo.KEY_FILE] = "Hy_com.heytap.msp_3.1.0.aar"
extra[MavenInfo.KEY_FLAVORBUILD] = ""
}
4、更新模块中的aar/jar库
针对我们模块中的本地库,如 open_sdk_3.5.12.2_r97423a8_lite.jar,我们可以直接用工具迁移到 AndroidX 即可,然后替换掉本地的 jar 库。
// 在项目的***/share_module/目录下:
// 将libs/open_sdk_3.5.12.2_r97423a8_lite.jar
// 通过工具重新打包成 libs/Hy_open_sdk_3.5.12.2_r97423a8_lite.jar。
jetifier-standalone -i libs/open_sdk_3.5.12.2_r97423a8_lite.jar -o libs/Hy_open_sdk_3.5.12.2_r97423a8_lite.jar -l info
07
小结
随着我们项目不断地增大,开发调试的编译时间也在不断地增加,这对团队的开发效率有了明显的降低。
在这个背景下,我们开始了一系列的编译速度优化的改造,遇到了很多问题,其中就包括了 Jetifier 优化导致产品功能不 work 的现象。
这就要求我们必须要了解优化过程的原理,方案和效果的验证,还要考虑防止恶化的管控措施。
Jetifier 优化的整个流程,可以总结如下:
使用 Google checkJetifier task 检测所有三方 maven 库的 Support 库使用情况;
根据 checkJetifier 输出提示,进行三方库的 AndroidX 迁移:
升级到支持 AndroidX 的新版本;
使用源码和 Jetifier 插件,编译出支持 AndroidX 的新版本;
使用 jetifier-standalone 命令行工具,转成支持 AndroidX 的新版本。
使用狐友内部 hyCheckJetifier task 检测所有三方库的 Support 库使用情况,包括第三方 maven 库,狐友 maven 库,和本地 libs 库。根据输出提示,进行三方库的 AndroidX 迁移;
将 hyCheckJetifier task 部署到 CI 中,每次代码提交,进行三方库的使用检测,将问题报告发送给 Commit Owner,防止优化被恶化。