Android Gradle 入门到精通(一)

1.背景和意义

先后经历过多个Android开发团队,每个团队都有这样的一个人。他能在gradle中配置一些代码。导致最终的apk 根据品牌+渠道 命名。甚至还能根据不同品牌,配置不同的变量。根据不同变量展示不同界面。gradle除了能帮我们做上述的事之外,还能做哪些呢?

  • hook Android 编译过程
  • 配置和改善 Gradle 编译速度

学习完gradle,对android 项目的构建理解会更加深入;更加熟练的使用task,帮助我们构建项目

2.概念

gralde它就是一个构建工具,帮助我们编译,打包,发布,检查代码格式,下载jar包等等。它是基于groovy语言编写的。

问题1: android如何自动下载gradle
android 项目创建的时候,gradle文件中自带gradle-wrapper.jar 这个jar包会根据gradle-wrapper.properties的配置 下载对应的gradle 版本。

问题2: ./gradlewgradle的区别

gradlew就是对gradle的包装和配置,gradlew是gradle Wrapper,Wrapper的意思就是包装。 因为不是每个人的电脑中都安装了gradle,也不一定安装的版本是要编译项目需要的版本,那么gradlew里面就配置要需要的gradle版本

问题3:什么是Project, 什么是Task,什么是Extension
android studio 每个module都有一个build.gradle文件,对于这个build.gradle而言,project 就是我们的module。可以通过 gradle projects查看Project。

Extension 对我而言就是配置,初始化变量,这个变量的作用就是为Task提供参数。

Task 任务,也是Action,表示这个任务是行为。例如assembleRelease是一个Task,用来执行打包操作。打完的apk存放路径这个路径可以人为的配置。这个配置,在我看来就是拓展(Extension )

问题4:什么是Gradle Daemon
Gradle Daemon是gralde的守护进程。是一个长时间运行在后台的进程,避免频繁的初始化。并且 Gradle 会将 Project data 缓存在内存中,加快构建速度。使用Gradle Daemon 是没有任何副作用的,并且我们可以非常简单的使用它,不需要任何成本,Gradle Daomen 对我们用户是完全透明的。可以通过gradle --stop停止

问题4:什么是闭包Closure
Groovy中的闭包是一个开放,匿名的代码块,可以接受参数,返回值并分配给变量。如:

//定义一个代码块
def innerClosure = {
    printf("hello world")
}
//执行方式1
innerClosure()
//执行方式2
innerClosure.call()

闭包也能传递参数:

def innerClosure = {String s,Student student ->
    printf(s+student.toString())
}
innerClosure("hello world",new Student("chips","26"))

以上就是gralde的基本知识和概念

3.生命周期

gradle 的生命周期分为三个阶段:

  • 初始化阶段
  • 配置阶段
  • 执行阶段

3.1 初始化阶段

在初始化阶段,Gradle的主要任务是确定有哪些工程参与构建,会解析settings.gradle文件,为文件中参与构建的的项目都创建project对象。可以通过gradle init完成

3.2 配置阶段

在这个阶段,Gradle会分别在每个Project对象上执行对应的build.gradle脚本,对Project进行配置。gradle提供了俩个方法

void beforeEvaluate(Closure closure)  //Project 配置之前调用

void afterEvaluate(Closure closure)    //在 Project 配置结束后调用

其中需要注意的是:beforeEvaluate 必须在父模块的 build.gradle 对子模块进行配置才能生效,因为在当前模块的 build.gradle 中配置,它自己本身都没配置好,所以不会监听到。

3.3 执行阶段

在配置时,Gradle 决定了在执行阶段要运行的 task 的顺序,他们的依赖关系的内部结构被建模为一个有向无环图,我们可以称之为 taks 执行图,它可以用 TaskExecutionGraph 来表示。可以通过 gradle.taskGraph 来获取。

在 TaskExecutionGraph 中也可以设置一些 Task 生命周期的回调:

  • addTaskExecutionGraphListener(TaskExecutionGraphListener listener)
  • addTaskExecutionListener(TaskExecutionListener listener)
  • afterTask(Action action),afterTask(Closure closure)
  • beforeTask(Action action),beforeTask(Closure closure)
  • whenReady(Action action),whenReady(Closure closure)

4. 基础使用

4.1 Task的基础使用

task使用的官方文档:官方文档
build.gradle中task的定义:

task taskName(){
    //任务执行前
    doFirst {
        println "doFirst"
    }
    //任务执行完毕的打印
    doLast {
        println "doLast"
    }
}

dependsOn使用
task任务经常存在相互依赖关系。A task 依赖于 Btask 可以借助dependsOn。在hook的时候用的比较多

task B {
    println "this is a task B"
}
task A(dependsOn: B) {
    println "this is a task A"
}

type使用
type用来表示创建的task 的类型

class MyTask extends DefaultTask {
    public String message
    public void test(String s) {
        println("this is MyTask " + message)
    }
}

task testDe(type: MyTask) {
    message = "chips"
    println test()
}

task之间存在顺序性,使用到的api:mustRunAfter()shouldRunAfter()

task B {
    println "this is a task B"
}
task A(dependsOn: B) {
    println "this is a task A"
}

A.mustRunAfter(B)

这样运行A任务的话,也会顺带执行B任务


自定义插件注册Task的方式:

class MyPlugin implements Plugin {

    @Override
    void apply(Project project) {
        MyTask t = project.tasks.register("MyTask", MyTask).get()
        t.message="chips"
        println  t.toString()
    }
}

ps:千万别用project.getByName("MyTask")直接获取task,gradle编译出错

以上就是Task的基本用法,下面说说Extension的用法.

4.2 Extension 用法

什么是Extentsion?

就是 Gradle 的 Extension,翻译成中文意思就叫扩展。它的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 android 这样命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。
一般我们通过ExtensionContainer来创建Extension,这个类跟TaskContainer命名有点类似。TaskContainer是用来创建并管理Task的,而ExtensionContainer则是用来创建并管理Extension的,通过Project的以下API可以获取到ExtensionContainer对象

在开始学习前,我们先看一个问题:
android app的build.gradle中 有这样的一段配置:

android {
    compileSdkVersion 31
    def versionCodeValue = 1
    def versionNameValue = "1.0"
}

这个android{} 就是一个名为android的Extension 。

我们来看看android这个Extension是如何定义的,系统源码连接:AppPlugin

AppPlugin插件:

  @Override
    void apply(Project project) {
        super.apply(project)
     ....
        extension = project.extensions.create('android', AppExtension,
                this, (ProjectInternal) project, instantiator,
                buildTypeContainer, productFlavorContainer, signingConfigContainer)
}

注册了一个名为android拓展,类型是AppExtension.

AppExtension:

public class AppExtension extends BaseExtension {
    final NamedDomainObjectContainer productFlavors
    final NamedDomainObjectContainer buildTypes
    final NamedDomainObjectContainer signingConfigs
    private final DefaultDomainObjectSet applicationVariantList =
        new DefaultDomainObjectSet(ApplicationVariant.class)
}

productFlavors/signingConfigs/buildTypes 分别是我们在build.gradle android下面配置的内容。他们都是一个又一个的拓展。用来配置参数,给Task的执行提供参数。

AppExtension 还有哪些拓展,可以访问AppExtension官网 看看还有哪些拓展可以写


使用Extension 分为三步:

  • 定义Extension 的参数类型
  • 在build.gradle中注册这个Extension
  • 实例化拓展,且给每个元素赋值
  1. 定义一个Extension 类型
class MyExtension{
    String name
    String age
}

2.注册Extension

getExtensions().create("MyExtension_拓展名称", MyInfoExtension)

3.在build.gradle 使用:

myExtension {
    name = "wxy"
    age = "123"
}

如何访问myExtension 呢?

project.afterEvaluate {
    MyInfoExtension mMyInfoExtension = getExtensions().getByName("myExtension")
    println "Extension 使用:  "+mMyInfoExtension.name+"   "+mMyInfoExtension.age
}

以上就是Extension 基本应用.

如何定义像android{}这样的Extension呢?

class Address{
    public String address
}
class MyExtension {
    public String name
    public String age
    public Address addressInfor=new Address();
    // 配置方法
    void addressConfig(Action
action) { action.execute(addressInfor) // 直接执行 action 参数的 execute 方法,并传入扩展对象 } }

其中addressConfig 是必填项.
这样在build.gradle中就可以这样定义了 :

getExtensions().create("myExtension", MyExtension)
myExtension {
    name = "chips"
    age = "123"
    addressConfig{
        address="湖北"
    }
}

我们还可以在.groovy文件中,调用:

class MyPlugin implements Plugin {

    @Override
    void apply(Project project) {
        project.getExtensions().create("MyInfoExtension",MyInfoExtension)
    }
}

这一步做完之后,相当于我们注册了这样一个拓展。那么直接在buid.gradle中 进行声明就行.


4.3 属性的访问

属性的访问我遇到的有三种方式:

  • 根 build.gradle 中定义变量
  • 导入其他的.gradle,携带的变量
  • 访问gradle.properties
  • 访问local.properties
  1. 根build.gradle 定义的变量,其他的子build.gradle中都能访问。如:
project.ext {
    versionCode2=2021
}

project.ext 传入一个闭包,返回一个Defaultextrapropertiesextension 拓展,此类实际上是用一个Map保存变量的。

  1. 导入其他的.gradle
    类似定义constants.gradle
project.ext {
    min_sdk_version     = 24
    compile_sdk_version = 'android-31'
    target_sdk_version  = 31
    build_tools_version = "30.0.3"
    java_Compatibility  = JavaVersion.VERSION_1_8 // 支持lambda
}

然后在build.gradle中
apply from: "${rootProject.projectDir}/constants.gradle"

3.gradle.properties 访问
在gradle.properties中定义各种变量 如:

versionCode5=2022

在build.gradle中就可以这样使用:

defaultConfig {
     versionCode versionCode5  as int
}

必须得用as 转换成对应的类型,不然会报错

4.local.properties 访问
local元素的访问稍微麻烦点.得new 一个Properties 对象,指定加载的文件

Properties properties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
}

---
properties.getProperty("versionCode3") as int

之后通过properties.getProperty("versionCode3") as int 得到值。响应的也一定要加上as int,默认是string类型

以上就是gradle的基础知识。下一节讲Gradle的自定义插件

你可能感兴趣的:(Android Gradle 入门到精通(一))