Android Gradle学习(九):有用的小技巧

现在的Android工程都是采用 gradle 来构建的,从早期的单一工程架构(一个项目只有一个主 module),到现在的组件化架构(一个项目包含有多个module),项目结构越来越复杂,在这里陆续记录一些挺有用的小技巧。

1. resolutionStrategy 统一全局第三方库版本

resolutionStrategy 顾名思义“解析策略”,也就是说可以在 gradle 解析各种依赖库时,配置一些解析策略。

1.1 resolutionStrategy.force

我们在开发的过程当中,会依赖使用各种第三方库,比如说最常见的 appcompat-v7 库,不仅我们自己的代码里会依赖该库,而且有可能我们依赖的第三方库也会依赖该库,但是不同的地方依赖该库的版本号都有可能不一致,这样在编译的时候,整个项目当中会出现各种不同版本号的 appcompat-v7 库,编译的时候可能会报错。我们不可能修改第三方库里对 appcompat-v7 库依赖的版本号,那么我们可以通过 resolutionStrategy.force 来强制编译时统一库的版本号。

android {
    configurations.all {
        resolutionStrategy {
            force 'com.android.support:appcompat-v7:28.0.0'
            force 'com.android.support:support-v4:28.0.0'
        }
    }
}

1.2 resolutionStrategy.dependencySubstitution

还有一种这样的情形,假设一个第三方工具库叫 org.gradle:util:3.0,除了我们自己的项目以及部分第三方库也有依赖它,但是在实际使用的过程当中,发现该库有些功能不满足或者有bug,那这个时候该怎么办呢?一是提 issue 让该库的作者去修改,但是时间上来不及;二是源码拿过来本地进行修改,直接本地集成,但是要修改依赖方式,那么这个时候可以这样做:

假设我们本地 module 名称叫 :util

android {
    configurations.all {
        resolutionStrategy {
            //远程依赖替换成本地依赖
            substitute module('org.gradle:util:3.0') with project(':util')
            //也可以将远程依赖换成另外的远程依赖,假设我们修改过的代码发布到自己的 maven 中央仓库后叫:com.xxx.xxx:util:3.0
            //substitute module('org.gradle:util:3.0') with module('com.xxx.xxx:util:3.0') 
        }
    }
}

2. gradle.settingsEvaluated 在编译之前做一些初始化工作

之前接手过一个项目,是采用 react-native 框架进行开发的,熟悉前端以及 React 的同学都知道,项目 build 的第一步是先进行 npm install 或者 yarn 操作,这样所有依赖的第三方库都会下载到 node_modules 目录下面。在 react-native 当中,Android 工程会依赖 node_modules 目录下的原生 Android 代码,但是因为库的版本关系,每次我编译的时候,都会发现某个 module 中的 build.gradle 配置与主工程冲突,需要手动修改才能编译。需要注意的是,node_modules 目录是动态下载的第三方库,并不会被提交,也就是说每次执行 yarn 操作之后再编译 Android,都需要手动更改下这个目录下的库,非常繁琐。那么有没方法来自动帮我们解决这个问题,不用每次都要手动修改呢?答案是可以。gradle 有个生命周期钩子方法 gradle.settingsEvaluated,在 gradle configure 阶段之前让我们做一些处理。

在我们项目的 settings.gradle 文件的最开头加上该方法的监听,注意该方法必须加在 settings.gradle 文件中,在 build.gradle 里没用。以下是个范例:

gradle.settingsEvaluated {
    println "-------------settingsEvaluated start------------"
    //假设我们要修改的是 react-native-fs 这个库当中的 build.gradle 文件
    //先删除该文件
    delete("${rootDir}/../node_modules/react-native-fs/android/build.gradle")
    //我们将修改好的文件放到 ./replace-files/react-native-fs 这个目录当中
    //前面已经删除了目标文件,现在只需将我们需要的文件复制过去即可
    copy {
        from("./replace-files/react-native-fs/build.gradle")
        into("${rootDir}/../node_modules/react-native-fs/android")
    }
    println "-------------settingsEvaluated end------------"
}

执行打包命令 ./gradlew assembleRelease 等的时候,一开始就会执行以上代码,自动帮我们做好这些事情。

除此之外,还可以做很多事情,比如打包前先删除某个缓存目录的文件,每次打包时自动递增修改 versionCodeversionName ,检查 local.properties 配置文件是否存在已经配置是否正确等等。

3. 修改包的输出路径

android {
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def date = releaseTime()
            //这里只改变 release 包的输出路径
            if (variant.buildType.name.contains('release')) {
                //我们定义包的输出路径为 android_outputs/Android_APK
                //这里可以根据情况定义任意路径
                variant.packageApplicationProvider.get().outputDirectory = new File(project.rootDir.absolutePath + File.separator + "android_outputs"+ File.separator + "Android_APK")
                //包名范例:Android-pro-release-v2.6.6-c266-d202108181428.apk
                def fileName = "Android-${variant.productFlavors[0].name}-${buildType.name}-v" + versionName + "-c" + versionCode + "-d" + date + ".apk"
                output.outputFileName = fileName
            }

            //拓展一下,也可以根据 productFlavors 来做配置
            /*
            if (variant.getName() == "qaRelease") {
                //这里还可以动态修改 productFlavors 里的东西,例如:
                variant.buildConfigField "String", "APP_CHANNEL", '"xiaomi"'
            } else if (variant.getName() == "proRelease") {                

            }
            */
        }
    }
}

def releaseTime() {
    return new Date().format("yyyyMMddHHmm")
}

4. 找到版本冲突的库

当项目越来越大,所使用的第三方库越来越多的时候,最让人头疼的是针对同一个库引入了多个不同的版本,编译的时候导致库冲突直接编译失败,问题找起来也很麻烦。还是利用resolutionStrategy,我们可以快速找到冲突的库以及各版本号,配置如下:

configurations.all {
    resolutionStrategy {
        failOnVersionConflict()
    }
}

下面是一个范例,在我的项目当中,om.android.support:multidex这个库引入了2个不同的版本:1.0.3 和 1.0.2

Conflict(s) found for the following module(s):
  - com.android.support:multidex between versions 1.0.3 and 1.0.2

找到冲突的库及版本后,我们可以用前面的方法,强制指定统一的版本号以解决冲突。

持续更新中......

系列文章

Android Gradle学习(一):Gradle基础入门
Android Gradle学习(二):如何创建Task
Android Gradle学习(三):Task进阶学习
Android Gradle学习(四):Project详解
Android Gradle学习(五):Extension详解
Android Gradle学习(六):NamedDomainObjectContainer详解
Android Gradle学习(七):Gradle构建生命周期
Android Gradle学习(八):统计Task执行时长
Android Gradle学习(九):一些有用的小技巧

你可能感兴趣的:(Android Gradle学习(九):有用的小技巧)