哪怕不学Gradle,这些开发中的常见操作,你也值得掌握(下)

简介: Gradle 是每个 Android 同学都逃不开的一个话题。

过滤变体

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 

目录
相关文章
|
存储 Java Android开发
Android 开发 - 充分利用Gradle
Android 开发 - 充分利用Gradle
169 2
|
5月前
|
Java API 项目管理
Java一分钟之-Gradle插件开发:自定义构建逻辑
【6月更文挑战第5天】Gradle插件开发详解:从入门到发布。文章介绍如何创建自定义插件,强调依赖管理、任务命名和配置阶段的理解。示例代码展示插件实现及避免常见问题的方法。最后,讨论插件的发布与共享,助你提升构建效率并贡献于开发者社区。动手实践,打造强大Gradle插件!
121 3
|
5月前
|
Android开发
Android Gradle开发—脚本实现自动打包后复制一份APK文件,并修改APK名称,到指定目录作备份
Android Gradle开发—脚本实现自动打包后复制一份APK文件,并修改APK名称,到指定目录作备份
266 0
|
数据安全/隐私保护 Android开发
浅谈build.gradle写法,促使我们更高效开发
浅谈build.gradle写法,促使我们更高效开发
169 0
|
缓存 开发者 Windows
开发这么久,gradle 和 gradlew 啥区别、怎么选?
开发这么久,gradle 和 gradlew 啥区别、怎么选?
开发这么久,gradle 和 gradlew 啥区别、怎么选?
|
XML Java Maven
安卓开发必备——build.gradle文件初探(一)
虽然已经在安卓开发岗位工作了大半年了,但因为作者并没有系统的学习过安卓开发,故基础这块一直都是作者本人的薄弱点,这也是安卓开发必备的由来,希望能填补自己在这方面的缺漏
220 0
|
测试技术 开发工具 Android开发
哪怕不学Gradle,这些开发中的常见操作,你也值得掌握(上)
Gradle 是每个 Android 同学都逃不开的一个话题。
134 0
哪怕不学Gradle,这些开发中的常见操作,你也值得掌握(上)
|
Java 开发工具 Android开发
安卓开发必备——build.gradle文件初探(二)
上一篇(安卓开发必备——build.gradle文件初探(一) - 掘金 (juejin.cn))我们探讨了关于安卓项目中最外层目录下的build.gradle,这一篇我们将继续说build.gradle文件
302 0
|
人工智能 移动开发 Java
Android Studio插件版本与Gradle 版本对应关系
Android Studio插件版本与Gradle 版本对应关系
2337 0
Android Studio插件版本与Gradle 版本对应关系
|
1月前
|
Android开发
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况