Android编译优化之Jetifier优化

Android编译优化之Jetifier优化_第1张图片

Android编译优化之Jetifier优化_第2张图片

本文字数:9048

预计阅读时间:40分钟

a7d3f6b22a548b5cefd2d846f23df4b1.png

在狐友项目的编译优化中,我们发现在 BuildAnalyzer 中有明确的 Warnings 提示,告知项目可以进行 Jetifier 优化。

Android编译优化之Jetifier优化_第3张图片

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 库使用情况:

Android编译优化之Jetifier优化_第4张图片

运行 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:

Android编译优化之Jetifier优化_第5张图片

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 库。

Android编译优化之Jetifier优化_第6张图片

但是,因为我们使用了很多第三方库,除了正常的 maven 库引用,还有另外2种情况。而这2种情况,checkJetifier task 任务是无法检测出它们对 Support 库的使用情况:

  1. 直接使用第三方提供的 jar/aar 文件,没有 maven,所有也没有 pom 文件。这些 jar/aar 如果依赖了 Support 库,自然无法被检测出来;

  2. 我们封装过的 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:

Android编译优化之Jetifier优化_第7张图片

  • androidDependencies,该 task 会平铺展示依赖树,且只展示几个主要configuration 的依赖,较为清爽,但是不能像方式二那样指定 configuration。这个方式除了 maven 依赖的库,也会显示本地 libs 中放的依赖库;

  • dependencies,该 task 的输出列表展示了所有 configuration 下的依赖树,依赖关系明显,层次清晰。输出的结果包含几十个 configuration 的依赖,较为冗余,可以通过指定 configuration 来显示特定的依赖树。如,gradle :app:dependencies --configuration debugCompileClasspath,该命令只会显示 debug 模式下编译过程中的依赖树。此方式只会显示 maven 依赖的库;      

Android编译优化之Jetifier优化_第8张图片

除了上述 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 了。

Android编译优化之Jetifier优化_第9张图片

通过命令行,或 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,防止优化被恶化。

你可能感兴趣的:(android)