一、 没有你的日子里(手动悲伤)
如果你手动的来完成一个可以执行的java程序要经历什么步骤
完成一个功能总不能都是自己写的代码吧,总会用到一些第三方的包吧, 这个时候你需要下载第三方的包放在本地
java文件要 通过java自带的工具编译成 .class
生成.class可能还要顺带生成文档
最后再要用 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工作原理全面了解/