过滤变体
Gradle
会为我们配置的 所有变体 和 build类型 每一种可能组合都创建一个 build变种
。当然有些变种,我们并不需要,所以我们可以在相应模块的 build.gradle
中创建 变体过滤器 ,以便移除某些不需要的变体配置。
android{ ... variantFilter { variant -> def names = variant.flavors*.name if (names.contains("demo2")) { setIgnore(true) } } ... }
效果如下:
针对变体配置依赖项
我们也可以针对上面这些变体,进行不同的依赖。比如:
demo1Implementation xxx minApi23Implementation xxxx
常见技巧
关于依赖管理
对于一些环境下,我们并不想在线上依赖某些库或者 model ,如果是三方库,一般都会有 relase 下依赖的版本。
如果是本地model,目前已经引用到了,所以就需要对于线上环境做null包处理,只留有相应的包名与入口,具体的实现都为null.
限制依赖条件为build类型
debugImplementation project(":dev") releaseImplementation project(":dev_noop")
有一点需要注意,当我们使用默认的 debugImplementation 和 releaseImplementation 进行依赖时,最终打包时是否会依赖其中,取决于我们 使用的build命令中build类型是不是debug或者relase ,如果使用的是自定义的 dev ,那么上述的两个 model 也都不会依赖,很好理解。
限制依赖条件为变体
相应的,如果我们希望当前的依赖的库或者model 不受 build类型 限制,仅受 变体 限制,我们也可以使用我们的 变体-Implementation
进行依赖,如下所示:
demo1Implementation project(":dev")
这个意思是,如果我们打包时使用demo1相应的gradle命令,比如assembleDemo1Debug,那么无论当前build类型是debug还是release或者其他,其都会参与依赖。
排除传递的依赖项
开发中,我们经常会遇见依赖冲突,对于第三方库导致的依赖冲突,比较好解决,我们只需要使用 exclude
解决即可,如下所示:
dependencies { implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") { exclude group: 'androidx.lifecycle', module: 'lifecycle-process' } }
统一全局的依赖版本
有时候,某些库会存在好多个版本,虽然 Gradle 会默认选用最高的版本,但是依然不免有时候还是会提示报错,此时我们就可以通过配置全局统一的版本限制:
android{ defaultConfig { configurations.all { resolutionStrategy { force AndroidX.Core force AndroidX.Ktx.Core force AndroidX.Work_Runtime } } } }
简化你的BuildConfig配置
开发中,我们常见的都会将一些配置信息,写入到 BuildConfig
中,以便我们在开发中使用,这也是最常用的手段之一了。
配置方式1
最简单的方式就是,我们可以在执行 applicationVariants task任务时,将我们的 config
写入配置中,示例如下:
app/ build.gradle
android.applicationVariants.all { variant -> if ("release" == variant.buildType.getName()) { variant.buildConfigField "String", "baseUrl", "\"xxx\"" } else if ("preReleaseDebug" == variant.buildType.getName()) { variant.buildConfigField "String", "baseUrl", "\"xxx\"" } else { variant.buildConfigField "String", "baseUrl", "\"xxx\"" } variant.buildConfigField "String", "buglyAppId", "\"xx\"" variant.buildConfigField "String", "xiaomiAppId", "\"xx\"" ... }
在写入时,我们也可以通过判断当前的 build类型 从而决定到底写入哪些。
优化配置
如果配置很少的话,上述方式写还可以接收,那如果配置参数很多,成百呢?此时就需要我们将其抽离出来了。
所以我们可以新建一个 build_config.gradle
,将上述代码复制到其中。
然后在需要的 模块 里,依赖一下即可。
apply from: "build_config.gradle"
这样做的好处就是,可以减少我们 app-build.gradle
里的逻辑,通过增加统一的入口,来提高效率和可读性。
配置方式2
当然也有另一种方式,相当于我们自己定义两个方法,在 buildType
里自行调用,相应的我们将 config配置 按照规则写入一个文件中去管理。
示例代码:
app/ build.gradle
buildTypes { // 读取 ./build_extras 下的所有配置 def configBuildExtras = { com.android.build.gradle.internal.dsl.BuildType type -> // This closure reads lines from "build_extras" file and feeds its content to BuildConfig // Nothing but a better way of storing magic numbers def buildExtras = new FileInputStream(file("./build_extras")) buildExtras.eachLine { def keyValue = it == null ? null : it.split(" -> ") if (keyValue != null && keyValue.length == 2) { type.buildConfigField("String", keyValue[0].toUpperCase(), "\"${keyValue[1]}\"") } } } release { ... configBuildExtras(delegate) ... } debug{ ... configBuildExtras(delegate) ... } }
build_extras
... baseUrl -> xxx buglyId -> xxx ...
上述两种配置方式,我们可以根据需要自行决定,我个人是比较喜欢方式1,毕竟看着更简单,但其实两者的实现方式也是大差不大,具体看个人习惯吧。
管理全局插件的依赖
某些时候,我们所有的model,可能都需要集成一个插件,此时我们就可以通过在 项目build.gradle
里全局统一管理,而避免到每一个Gradle 下去集成:
// 管理全局插件的依赖 subprojects { subproject -> // 默认应用所有子项目中 apply plugin: xxx // 如果想应用到某个子项目中,可以通过 subproject.name 来判断应用在哪个子项目中 // subproject.name 是你子项目的名字,示例如下 // 官方文档地址:https://guides.gradle.org/creating-multi-project-builds/#add_documentation // if (subproject.name == "app") { // apply plugin: 'com.android.application' // apply plugin: 'kotlin-android' // apply plugin: 'kotlin-android-extensions' // } }
动态调整你的组件开关
对于一些组件,在 debug
开发时如果依赖,对我们的编译时间可能会有影响,那么此时,如果我们增加相应的开关控制,就会比较好:
buildscript { ext.enableBooster = flase ext.enableBugly = flase if (enableBooster) classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version" }
如果每次都是静态控制,那么当我们使用 CI
来打包时,就会没法操作。所以相应的,我们可以更改一下逻辑:
我们创建一个文件夹,里面放的是相应的忽略文件,如下所示:
然后我们更改一下相应的 buildscript
逻辑:
buildscript { ext.enableBooster = !file("ignore/.boosterignore").exists() ext.enableBugly = !file("ignore/.buglyignore").exists() if (enableBooster) classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version" }
通过判断相应的插件对应的文件是否存在,来决定插件在CI打包时的启用状态。在CI打包时,我们只需要通过shell删除相应的配置ignore文件或者通过gradle执行相应命令即可。因为本篇是讲gradle的一些操作,所以我们就主要演示一下gradle的命令示例。
定义自己的gradle插件
我们先简单写一个最入门的插件,用来移除相应的文件,来达到开关插件的目的。
task checkIgnore { println "-------checkIgnore--------开始->" removeIgnore("enableBugly", ".buglyignore") removeIgnore("enableGms", ".gmsignore") removeIgnore("enableByteTrack", ".bytedancetrackerignore") removeIgnore("enableSatrack", ".satrackerignore") removeIgnore("enableBooster", ".boosterignore") removeIgnore("enableHms", ".hmsignore") removeIgnore("enablePrivacy", ".privacyignore") println "-------checkIgnore--------结束->" } def removeIgnore(String name, ignoreName) { if (project.hasProperty(name)) { delete "../ignore/$ignoreName" def sdkName = name.replaceAll("enable", "") println "--------已打开$sdkName" + "组件" } }
这个插件的作用很简单,就是通过我们 Gradle 命令 携带的参数 来移除相应的插件文件。
gradlew app:assembleRoyalFinalDebug -PenableBugly=true
具体如图所示:在 CI-build 时,我们就可以通过传递相应的值,来动态决定是否启用某插件。
优化版
上述方式虽然方便,但是看着依然很麻烦,那么有没有更简单,单纯利用 Gradle
即可。其实如果稍微懂一点 Gradle 生命周期,这个问题就能轻松解决。
我们可以在 settings.gradle
里监听一下 Gradle 的 生命周期 ,然后在项目结构加载完成时,也就是 projectsLoaded
执行时,去判断一下,如果存在某个参数,那么就打开相应的组件,否则关闭。
示例:
settings.gradle
gradle.projectsLoaded { proj -> println 'projectsLoaded()->项目结构加载完成(初始化阶段结束)' def rootProject = proj.gradle.rootProject rootProject.ext.enableBugly = rootProject.findProperty("enableBugly") ?: false rootProject.ext.enableBooster = rootProject.findProperty("enableBooster") ?: false rootProject.ext.enableGms = rootProject.findProperty("enableGms") ?: false rootProject.ext.enableBytedance = rootProject.findProperty("enableBytedance") ?: false rootProject.ext.enableSadance = rootProject.findProperty("enableSadance") ?: false rootProject.ext.enableHms = rootProject.findProperty("enableHms") ?: false rootProject.ext.enablePrivacy = rootProject.findProperty("enablePrivacy") ?: false }
执行build命令时携带相应参数即可:
gradlew assembleDebug -PenablePrivacy=true