Android编译优化之Jetifier优化

0eefbac836d07e60b39a7ca8e71c95ea.jpeg

ce5dd4e98c5b0034ab2da66679ef9550.gif

本文字数:9048

预计阅读时间:40分钟

a7d3f6b22a548b5cefd2d846f23df4b1.png

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

25f928bae976b4f0c62315e9d18eb748.png

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

685c780d66b6c9d9a5558cdd5175f909.png

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

8c4dba8b1a46bcce47b0cd9f321c0a54.png

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

244e7262c72e8bae28b9337d6393601b.png

但是,因为我们使用了很多第三方库,除了正常的 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:

7ab5b0c91ada415bd42d0a18adac29e7.png

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

a298730be4b2d1b3db2d5a8f3d09c52a.png

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

c6427a88c3fd3ca461474b5d6c8218ca.png

除了上述 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<String,String>):HashMap<String,String> {
 
    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<String,String>()
    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 了。

af5955862572a7168ce3f7529da742e0.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/252888.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Azure Machine Learning - 提示工程简介

OpenAI的GPT-3、GPT-3.5和GPT-4模型基于用户输入的文本提示工作。有效的提示构造是使用这些模型的关键技能&#xff0c;涉及到配置模型权重以执行特定任务。这不仅是技术操作&#xff0c;更像是一种艺术&#xff0c;需要经验和直觉。本文旨在介绍适用于所有GPT模型的提示概念和…

diffuser为pipeline设置不用的scheduler

查看默认的schedulers&#xff1a; 使用默认的schedulers生成数据 查看默认scheduler的默认配置&#xff0c;定义了采样器中的相关参数&#xff0c;网上关于DDPM和DDIM的文章较多&#xff0c;可以先去看看这两种schedulers&#xff1a; 修改scheduler&#xff0c;可以用于…

13.二进制枚举练习题

文章目录 二进制枚举练习题[78. 子集](https://leetcode.cn/problems/subsets/)[77. 组合](https://leetcode.cn/problems/combinations/)[1286. 字母组合迭代器](https://leetcode.cn/problems/iterator-for-combination/)[2397. 被列覆盖的最多行数](https://leetcode.cn/pro…

CSS3 2D变形 过渡 动画

​​​​​ transform(2D变形)概述translate()平移scale()缩放skew()倾斜rotate()旋转transform-origin中心原点 CSS3 2D变形 3D变形 过渡 动画 在CSS3中&#xff0c;动画效果包括4个部分&#xff1a;变形&#xff08;transform&#xff09;、3D变形、过渡&#xff08;transit…

【Linux】cp问题,生产者消费者问题代码实现

文章目录 前言一、 BlockQueue.hpp&#xff08;阻塞队列&#xff09;二、main.cpp 前言 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用…

计算机网络:网络层(无分类编址CIDR、计算题讲解)

带你快速通关期末 文章目录 前言一、无分类编址CIDR简介二、构成超网三、最长前缀匹配总结 前言 我们在前面知道了分类地址&#xff0c;但是分类地址又有很多缺陷&#xff1a; B类地址很快将分配完毕!路由表中的项目急剧增长! 一、无分类编址CIDR简介 无分类域间路由选择CI…

计算机网络:数据链路层(VLAN)

今天又学到一个知识&#xff0c;加油&#xff01; 目录 一、传统局域网的局限&#xff08;促进VLAN的诞生&#xff09; 二、VLAN简介 三、VLAN的实现 总结 一、传统局域网的局限&#xff08;促进VLAN的诞生&#xff09; 缺乏流量隔离:即使把组流量局域化道一个单一交换机中…

【Linux】初识命令行

为什么使用命令行&#xff1f; 大多数的计算机用户只是熟悉图形用户界面(GUI)&#xff0c;采用图形方式显示的用户操作界面。命令行界面(CLI)是一种通过文本输入来与计算机进行交互的方式&#xff0c;用来和计算机进行交流沟通的非常有效的方式&#xff0c;正像人类社会使用文…

【Windows】windows11右键默认显示更多选项的办法

Windows11系统的右键菜单显示&#xff0c;需要多点一次“显示更多选项”才能看到所有菜单内容&#xff0c;按下面步骤简单设置一下就能恢复成Windows经典的右键菜单显示。 1. 2.输入命令【reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a…

熬了一个通宵,把国内外的大模型都梳理完了!

大家好&#xff0c;大模型越来越多了&#xff0c;真的有点让人眼花缭乱。 为了让大家清晰地了解大模型&#xff0c;我熬了一个通宵把国内和国外的大模型进行了全面梳理&#xff0c;国内有189个&#xff0c;国外有20&#xff0c;同时包括大模型的来源机构、来源信息和分类等。 …

vue3+leaflet天地图开发

<script setup> import { onMounted, onUnmounted, ref } from "vue"; // todo 项目使用请放开 leaflet 引入 // import L from leaflet;const emit defineEmits(["mapLoad"]);var markers ref([]); const mapRef ref(); const marker ref(); co…

linux环境安装可操作图库语言Gremlin的图框架HugeGraph

原创/朱季谦 若你还没接触过图数据库&#xff0c;可能看到这个概念时&#xff0c;会比较蒙蔽。 图是什么&#xff1f;图数据库又是什么&#xff1f; 首先&#xff0c;在数据结构中&#xff0c;图是一种由顶点&#xff08;vertex&#xff09;集合及顶点间关系集合组成的一种非…

FRP内网映射家用服务器至公网访问

兄弟们&#xff0c;服务器到货了&#xff0c;后续与大家分享内容就用它了。我预装的操作系统是Centos8,首先要解决的是远程访问的问题。 【特别注意】下述的端口&#xff0c;记得在阿里云安全组配置中放开端口入规则&#xff01;&#xff01; 1. FRP服务器配置 之前我有购买…

如何将数据库导入MySQL的办法

在电脑cmd终端进行导入 首先找到MySQL中bin的位置 第一步&#xff1a;找到MySQL 第二步&#xff1a;进入MySQL 第三步&#xff1a;打开bin 第四步&#xff1a;输入cmd进入终端 第五步&#xff1a; 输入mysql -uroot -p 然后会弹出enter password&#xff1a; 输入你的密码…

Leetcode的AC指南 —— 链表:24. 两两交换链表中的节点

摘要&#xff1a; Leetcode的AC指南 —— 链表&#xff1a;24. 两两交换链表中的节点。题目介绍&#xff1a;给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能…

[计网01] 物理层 详细解析笔记,特性

计算机网络的物理层是网络协议栈中的第一层&#xff0c;负责传输原始的比特流&#xff08;bitstream&#xff09;通过物理媒介进行通信。物理层主要关注传输介质、信号的编码和调制、数据传输速率以及数据传输的物理连接等方面。 相关特性 机械特性&#xff08;Mechanical Ch…

Java已死!

许多开发者仍然认为 Java 与当今时代息息相关&#xff0c;看完本文&#xff0c;你会发现 Java 的影响力已经大幅减弱。实际上&#xff0c;Java 是一种濒临灭绝的编程语言。尽管 Java 一直是世界上使用最广泛、最受欢迎的编程语言之一&#xff0c;但它很快就会面临消亡的危险。 …

【C语言加油站】qsort函数的模拟实现

qsort函数的模拟实现 导言一、回调函数二、冒泡排序2.1 冒泡排序实现升序 三、qsort函数3.1 qsort函数的使用3.2 比较函数 四、通过冒泡排序模拟实现qsort函数4.1 任务需求4.2 函数参数4.3 函数定义与声明4.4 函数实现4.4.1 函数主体4.4.2 比较函数4.4.3 元素交换 4.5 my_qsort…

单片机控制步进电机

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、步进电机的工作原理是什么&#xff1f;二、连线图三、程序1.参考程序2.实际测试 四、开发板1.原理图2.实际连接图3.参考程序4.测试5. 思考 五、步距角总结 …

面向对象三大特征——继承

目录 1. 概述 2. 继承的限制 2.1 单继承 2.2 访问修饰符 2.3 . final 3. 重写 4. super 4.1super的作用 4.2访问父类的成员和被重写方法 4.3调用父类的构造器 1. 概述 多个类中存在相同属性和行为时&#xff0c;将这些内容抽取到单独一个类中&#xff0c;那么就无需在…