gradle项目的结构
首先将android项目中的gradle文件系统列举如下:
- settings.gradle
- project路径下的build.gradle
- subproject(也可以理解为module)路径下的build.gradle
接下来我们依次分析他们的组成结构,并且总结各个gradle文件的作用
gradle文件使用groovy编写。但是大量使用DSL,一定程度上可以使代码的功能容易看懂,但是加大了读懂具体实现的难度。
settings.gradle
一般这个文件下只包含一句话,比如
include ':app', ':module_a', ':module_b', ':base'
这里调用了一个include方法,传入了一个字符串数组,表示在这个gradle文件系统下,包含以下这么多个subproject。
project路径下的build.gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
代码块整体是一个构建脚本,里面又是由两部分构成:
- 配置了代码仓库(与下面的仓库不同点在于这里是提供最外层project构建用的仓库)。
- 配置了依赖,具体区别同上。
所以整体上可以这么理解,如果有project(最外层项目的构建需求),我们就从这里配置的仓库中取出这里配置的依赖中的代码库。
allprojects {
repositories {
google()
jcenter()
}
}
这里的配置是给所有的subproject用的,比如这里给所有的subproject配置了google和jcenter仓库。
task clean(type: Delete) {
delete rootProject.buildDir
}
这里在build.gradle中创建了一个clean任务,用来删除最外层project的构建目录。
subproject路径下的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
如果此时构建的是一个android项目,那么subproject的build.gradle则需要引用上面两个插件(都是android官方提供给我们来构建android项目的)中的一个,前者用来配置一个application类型的module,后者用来配置一个library类型的module。
这里特别需要特别注意一下apply plugin和apply from两个概念。
apply from(应用脚本插件)
可以直接调用插件中的脚本,就好像脚本插件就是当前gradle文件的一部分一样。
apply plugin(应用二进制插件)
调用二进制插件中的apply方法,把当前project对象当作参数传入
android {
compileSdkVersion 28
buildToolsVersion "29.0.0"
defaultConfig {
applicationId "com.example.neteasemusic"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
接下来是这一段,方法名android,很明显是用来配置各种android属性的,比如编译sdk的版本,构建工具的版本,各种默认配置,构建类型,编译的jdk版本,等等。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
}
这里就更好理解了,当前这个module依赖的第三方库,就在这里配置。
如何理解gradle文件
我们知道setting.gradle,可以对应到一个Settings.java文件。build.gradle,可以对应到一个Project.java文件。那他们之间是什么关系呢?
我现在的理解是,build.gradle文件,用groovy语言编写,在内部会持有一个Project的对象,在build.gradle中写的代码,都是用持有的这个Project类的对象在调用。
如何理解gradle版本和gradle插件版本
android项目使用gradle作为构建工具,gradle版本就是当前项目使用的gradle sdk的版本
android官方提供了几个用于构建module的插件(上面提到过的com.android.application和com.android.library),gradle插件版本就是指这几个插件的版本。
我在这里用一个可能不恰当的比方。gradle版本可以类比成jdk的版本,gradle插件版本则是android sdk的版本。
自定义gradle插件
我们一起分析gradle官网给出的例子:
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('hello') {
doLast {
println 'Hello from the GreetingPlugin'
}
}
}
}
// Apply the plugin
apply plugin: GreetingPlugin
上面是第一种方法,首先创建了一个GreetingPlugin实现了Plugin接口,重写了apply方法,在project中创建了一个名为hello的task,在task执行完后打印了一句话"Hello from the GreetingPlugin".
然后在当前gradle文件中应用了这个plugin。
所以当项目构建完成后,会多出来一个名为hello的task,运行这个task,会在控制台打出上面那句话
class GreetingPluginExtension {
String message = 'Hello from GreetingPlugin'
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingPluginExtension)
// Add a task that uses configuration from the extension object
project.task('hello') {
doLast {
println extension.message
}
}
}
}
apply plugin: GreetingPlugin
// Configure the extension
greeting.message = 'Hi from Gradle'
上面是第二种方法,首先创建了一个GreetingPluginExtension类,可以理解为一个实体类,这个类里面有一个字段message,我们待会就会用到。
接下来我们创建了一个GreetingPlugin实现了Plugin接口,重写了apply方法,拿到project对象中的extensions,创建了一个名为greeting,类型为GreetingPluginExtension的对象,然后project创建了一个名为hello的task,在任务完成后打印出了extension中的message。
然后在当前gradle文件中应用了这个插件,所以project中创建了一个extension,然后创建了一个task
最后给extension中的字段赋值。
打包一个plugin
前面我们讲了如何写gradle插件,那接下来自然就是如何打包使用了,一般来说,我们自己写的gradle plugin的source code可以放在以下几个地方:
-
构建脚本
你可以把插件的代码直接写在构建脚本中。好处是插件是自动编译和包含在构建脚本的类路径中的,也就意味着你不需要额外做任何事。然后,在构建脚本意外插件是不可见的,所以你不能在其他地方使用你的插件。 -
buildSrc project中
你可以把插件的源码放在rootProjectDir/buildSrc/src/main/groovy目录下。Gradle会自动进行编译,测试,并且使这个插件在构建脚本的类路径上可用。 -
独立的project中
你可以为你的plugin创建一个单独的项目。这个项目产生和发布一个jar包,供其他人使用。一般来说,这个jar包会包含一些插件,
gradle构建android项目的过程
我们直接观察项目构建时IDE的「build」窗口的输出。可以直接看出,一次完整的构建包含如下四个部分:
- load build(加载构建)
- configure build(配置构建)
- calculate task graph(计算task的图)
- run tasks(运行用于构建项目的tasks)
我们依次分析这四个部分:
第一部分,load build,截图中包含了两个部分,第一部分可以忽略(是我用来存放自定义plugin的代码相关的)。第二部分解析了根目录下面的settings.gradle文件。
第二,三部分比较清晰,我们可以放在一起讲,第二部分主要是加载了所有subprojects,第三部分则是计算了整个gradle项目所有task的有向无环图。
第四部分,在上一部分所有task的有向无环图产生之后,就可以自由的运行我们想运行的task了。所以这里运行了所有构建需要的task。