Gradle工作原理全面了解

一、 没有你的日子里(手动悲伤)

如果你手动的来完成一个可以执行的java程序要经历什么步骤

  1. 完成一个功能总不能都是自己写的代码吧,总会用到一些第三方的包吧, 这个时候你需要下载第三方的包放在本地

  2. java文件要 通过java自带的工具编译成 .class

  3. 生成.class可能还要顺带生成文档

  4. 最后再要用 java自带的工具将 所有的资源一起打包成 一个.jar

      最简单大概就这这些步骤。如果还要加上一些编译多个差异包之类的需要,就更复杂。仔细认真的想想,这特么的也太麻烦了。如果同样的工程要换个电脑该怎么办?这些都问题。要想办法解决这些问题,让问题变的简单起来,这就是gradle的作用了。帮助我们做了一些需要手动做的事,减轻我们的工作量,把关注点让在具体的工程代码上面。

二、 瞅一眼gradle

万一很简单,咱瞅一眼就能懂了呢?

gradle的官网是这么定义的

Gradle is an open-source build automation tool 
focused on flexibility and performance.
 Gradle build scripts are written using a Groovy or Kotlin DSL.

懵逼…字我都认识,可是 groovy是啥。DSL是啥。

再百度下groovy:

Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,
它结合了Python、Ruby和Smalltalk的许多强大的特性,
Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。
由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库。

再瞅瞅 DSL

领域特定语言(英语:domain-specific language、DSL)
指的是专注于某个应用程序领域的计算机语言。

      对于最上面那一串的翻译过来就是:有一个语言叫groovy ,咱用它制定一些规则,来完成上面所说的操蛋的构建的事。这种新的脚本语言就是gradle

      因为先不回学习自定义,先跳过groovy语法的部分,先瞅瞅gradle的一套规则是怎么运行的。

三、 纯粹的gralde

       gradle的是一个单独的东东,是可以脱离IDE 脱离具体的某个类型的项目(java,android)存在的。我们先瞅瞅它的原始模样。

准备工作

gradle是一个单独的工具,可以单独执行。为了方便我们把它配置到环境变量中。如果之前有使用过gradle,一般就是找到C:\Users\xxxxxx\.gradle\wrapper\dists\gradle-4.4-all这样的目录就有了,配置其bin目录到环境变量中即可。如果之前没有使用过,可以单独下载。

目录结构

在某个你要使用的目录下调用gradle.bat init就可以完成初始化。

如图1。

这个就最基本的结构了。

     为了更加的丰富测试,我加了两个工程, 创建了文件夹projectA projectB里面分别创建一个空的build.gradle文件,在文件中加入了一句打印的话。然后在setting.gralde中加上include projcetA, projectB,在

再来查看工程中的project gradlew.bat projects

       添加一个简单的任务默认来执行,我擦,居然还需要下载,好吧我本地其实已经下载了的。那我把它拷贝本地wraper中,gralde.wrapper 中的链接指向一个本地链接就好了。在 //gradle.wrapper中修改对应的url地址。

修改后就 快了很多,这个时候就可以大大的节省时间,再也不用担心玩命的等待了。

      本来想顺着官网的文档来挨着讲。可是回想起我自己琢磨gradle的时候,我特么最关心就是它是怎么运行的,以及怎么自定义。那就先说这个事。

总的执行顺序 初始化->配置->执行阶段

初始化阶段:扫描setting.gradle创建gradle 等对象 官网初始化的解释

配置阶段:扫描 各个 build.gradle 创建对应的project ,获取项目中有多少个task,因为task的执行有顺序,这里就为task构建一个DFA(有向无环图)

执行阶段:运行所有的task

       因为是脚本语言,其实每次执行都会把脚本让进解释器读一遍,所以每次执行某个task都会把以上的三个阶段走一次。

        这里有几个概念很关键,gradle, project, task。gradle全局可见,相当于这个gradle工具本身。project是一个模块,与android studio中的module是对应的关系,也对应了一个build.gradle的文件, task是最小的执行块。为了方便,我们来有两个project的工程来模拟, 他们的具体执行顺序进一步细分为下面的这个样子。

     看到这幅图,我们就能知道这个工具的能力边界在哪里。通过一步步的配置操作,形成一个任务的DFA来继续执行,如果我们需要在某些节点插入一些操作,就可以有两种方式:

使用系统的提供的回调监听 #800a00
调整task 或者 创建新的task修改task的执行图 #802300

3.1 设置监听插入自定义操作到整个运行过程

      所有gradle中能操作的动作,有对应的类文件俩定义它,我们搜索可查看Gradle.java的文件可以查看到这些监听的事件。

   

/**
     * Adds the given listener to this build. The listener may implement any of the given listener interfaces:
     *
     * <ul>
     * <li>{@link org.gradle.BuildListener}
     * <li>{@link org.gradle.api.execution.TaskExecutionGraphListener}
     * <li>{@link org.gradle.api.ProjectEvaluationListener}
     * <li>{@link org.gradle.api.execution.TaskExecutionListener}
     * <li>{@link org.gradle.api.execution.TaskActionListener}
     * <li>{@link org.gradle.api.logging.StandardOutputListener}
     * <li>{@link org.gradle.api.tasks.testing.TestListener}
     * <li>{@link org.gradle.api.tasks.testing.TestOutputListener}
     * <li>{@link org.gradle.api.artifacts.DependencyResolutionListener}
     * </ul>
     *
     * @param listener The listener to add. Does nothing if this listener has already been added.
     */
    void addListener(Object listener);

      一般的我们我们比较关心 projectEvaluation这个对应中扫描build.gradle文件的动作,我们可以知道扫描时候扫描开始,也可以知道什么时候扫描结束,taskGraph就对应上面的念念叨叨的任务所形成的有向无环图。

      在工程projectA 和 projectB的build.gradle中加入扫描的监听,将其输出来,还是向最上面一样,执行taskB

针对taskB来做监听

println( "I am Project root ")




task testA {
  doLast {
    println( "testA doLast")
  }
}




task testB {
    doFirst {
        println( "testB doFirst")
    }
  
  doLast {
    println( "testB doLast")
  }
}
testB.dependsOn(testA)








project.gradle.taskGraph.beforeTask {
    task ->
        if("testB".equals(task.name)) {
            println(" before  testB ")
        }
}




project.gradle.taskGraph.whenReady {
    graph ->
       println("task graph ready")
}




project.gradle.taskGraph.afterTask {
    task ->
        if("testB".equals(task.name)) {
            println(" afterTask  testB ")
        }
}




afterEvaluate {
    println(" afterEvaluate")
}




beforeEvaluate {
    println(" beforeEvaluate")
}

最后执行下testB 看下结果

如图所示的。看到监听在不同的阶段产生了效果

3.2 修改或者增加task来插入自定义操作到整个运行过程

1、修改task

      dofirt dolast可以不断往上附加,多次设置dolast,都可以执行,

如果原来的task有一个dolast ,可以在后面追求一个dolast的闭包。两者是累积的关系,不会替换。这样的话就可以执行自己想要个的操作。

这里以一个实际有效的例子来说明,在编译android项目的过程中经常需要打包成一个jar,又需要将操作嵌入到正常的编译链之后,build会生成一系列的文件,这个文件在assemble task执行后一定会执行,我们可以这样进行操作

assemble.doLast {
String cmd = "jar -cMf ${buildDir}/aaa.jar ${buildDir}/intermediates/classes/a360/debug"
def cmdResult = cmd.execute().text.trim()
println "cmdResult: " + cmdResult}

这样做了之后 每次build都可以自动生成一个jar

2、增加task

task的顺序有两种,一个依赖,一个定义顺序,这两个有啥区别?

举个例子

规定task A 依赖于 taskB 当执行taskA的时候一定会执行 taskB,再执行taskA

//这样可以定义依赖关系

testA.dependsOn(testB)

   规定taskB 在taskA的前面执行,单独执行taskA时,taskB不会执行,除非由于其他的任务导致taskB执行了,在执行的时候才会让taskB运行在taskA的前面

testA.mustRunAfter(testB)

有了上面的依赖方式,则可以增加一个task,通过依赖的方式嵌入到前面的提到的任务执行的有向无环图中,任务自然就可以执行了。

android中的gradle(我已经不单纯了)

1.android编译过程

如果不理解android的编译过程,你说想写好自动化构建的脚步,,那就有点幽默了。这个最底层的,我们需要完全的理解之后才能各种操作变化。不想自己画图,在网上扣了个图

2.对应的工具

3. 对应的gradle过程

      创建了一个最基本的android项目 随便执行了一句gradlew.bat build 。emmm… 58个task。我都没法截图了。

执行gradlew.bat tasks –all可以查看所有的任务。这里就跳过了,如果有特殊的需求再来找这些。同时

也可以通过 gradlew.bat sourceSets看到所有默认的资源路径配置

//部分的结果

main
----
Compile configuration: compile
build.gradle name: android.sourceSets.main
Java sources: [app\src\main\java]
Manifest file: app\src\main\AndroidManifest.xml
Android resources: [app\src\main\res]
Assets: [app\src\main\assets]
AIDL sources: [app\src\main\aidl]
RenderScript sources: [app\src\main\rs]
JNI sources: [app\src\main\jni]
JNI libraries: [app\src\main\jniLibs]
Java-style resources: [app\src\main\resources]

4. android特有的一些类,查看技巧和使用

    每次在写android的buid.gralde我都苦恼于 这些标签一定不能瞎写,肯定有地方规定了什么能够写,什么不能够写。那么这些规定在哪里可以看到?

最上层的android标签里面能够写那些你知道?

一般的写法如下

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.a01377123.myapplication"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

      但是还有哪些呢?这个时候就可以查看 AppExtension.java ,它以及他的父类的里面出来的函数都可以写在

android这个括号里面的。因为这个类不好直接搜索,我们可以在android studio中的 build.gradle点击一个标签buildTypes可以跳转到BaseExtension.java截个图

/**
 * Base extension for all Android plugins.
 *
 * <p>You don't use this plugin directly. Instead, use one of the following:
 *
 * <ul>
 *   <li>{@link AppExtension}: outputs the {@code com.android.application} plugin you use to create
 *       an Android app module.
 *   <li>{@link LibraryExtension}: outputs the {@code com.android.library} plugin you use to <a
 *       href="https://developer.android.com/studio/projects/android-library.html">create an Android
 *       library module</a>.
 *   <li>{@link TestExtension}: outputs the {@code com.android.test} plugin you use to create an
 *       Android test module.
 *   <li>{@link FeatureExtension}: outputs the {@code com.android.feature} plugin you use to create
 *       a feature module for your <a href="https://d.android.com/instant-apps">Android Instant
 *       Apps</a>.
 * </ul>

最上面的注释这些话很重要。可以看到 com.android.applicaiton对应 AppExtension

其函数为,emm…还是太长了,基本上可以囊括编译过程中的各种工具的配置,比如aapt,javac,jarsign等。跳过,如果感兴趣就可以看到,知道这个标签下面哪些能够写,哪些不能够写。

收集了一些常用的做整理。可以方便使用android下的gradle

…我又犯懒了 ,后面还有很多都是类似的道理,查看源文件就知道它的功能边界在哪里

实例一

sourceSets {
        main {
            java.srcDirs('src/test1/java')  //定义java 源代码
            res.srcDirs('src/test1/res')    //定义资源目录(layout , drawable,values)
        }
    }

最后安利一个用来处理打包过程密码的问题

https://github.com/etiennestuder/gradle-credentials-plugin
//配置
configurations {
    baiduDebugImplementation
    baiduReleaseImplementation
    a360DebugImplementation
    a360ReleaseImplementation
}
baiduDebugImplementation 'com.facebook.stetho:stetho:1.3.1'

//可视化查看task依赖的过程

https://github.com/dorongold/gradle-task-tree

//通盘了解gradle

https://docs.gradle.org/current/userguide/userguide.html

//关于编写过程中各种语言语法的api的介绍

https://docs.gradle.org/current/dsl/

//旧的android pulgin用户指导

http://tools.android.com/tech-docs/new-build-system/user-guide

————————————————

原文链接:https://blog.csdn.net/xjz729827161/article/details/82872076

                        喜欢 就关注吧,欢迎投稿!

本网站文章均为原创内容,并可随意转载,但请标明本文链接
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: http://leetcode.jp/gradle工作原理全面了解/

此条目发表在Android分类目录。将固定链接加入收藏夹。

发表评论

您的电子邮箱地址不会被公开。