最近一周都在搞这玩意了,写点文档给后人参考吧。
一、Gradle 对模块的描述
这里使用2个例子,只有一个模块,例如使用 Intellij 创建的新工程;只有两个模块(父子关系),例如使用 AndroidStudio 创建的默认 App。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | ➜  HelloGradle lsatotal 40
 drwxr-xr-x  12 leadroyal  staff   384B  7 19 18:04 .
 drwxr-xr-x   7 leadroyal  staff   224B  7 21 21:09 ..
 drwxr-xr-x   4 leadroyal  staff   128B  7 12 14:13 .gradle
 drwxr-xr-x   7 leadroyal  staff   224B  7 19 18:08 .idea
 -rw-r--r--   1 leadroyal  staff   492B  7 19 18:04 build.gradle
 drwxr-xr-x   3 leadroyal  staff    96B  7 12 14:13 gradle
 -rwxr-xr-x   1 leadroyal  staff   5.2K  7 12 14:13 gradlew
 -rw-r--r--   1 leadroyal  staff   2.2K  7 12 14:13 gradlew.bat
 -rw-r--r--   1 leadroyal  staff    34B  7 12 14:13 settings.gradle
 drwxr-xr-x   4 leadroyal  staff   128B  7 18 15:55 src
 
 | 
- .gradle 存放一些临时的东西,要 gitignore 掉
- .idea intellij全家桶的临时文件,要 gitignore 掉
- build.gradle 必备,描述当前模块的主要信息,经常需要编辑(例如sourceSets、plugin、dependencies等)
- gradle 必备,存放描述当前 gradle 版本的文件和一个 wrapper
- gradlew、gradlew.bat 必备,是启动脚本
- settings.gradle 非必备,但经常有,描述模块的名称、目录
- src,非必须,只是一般这么命名了,用来存放代码
再看看build.gradle 的内容
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | plugins {id 'java'
 }
 
 
 group 'com.leadroyal'
 version '1.0-SNAPSHOT'
 
 sourceCompatibility = 1.8
 
 repositories {
 mavenCentral()
 }
 
 jar {
 from {
 configurations.runtime.collect { zipTree(it) }
 }
 manifest {
 attributes 'Main-Class': "Hello"
 }
 }
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 compile 'com.qcloud:cos\_api:5.4.4'
 compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
 
 }
 
 | 
plugins是使用的插件,常见的有java、scala、shadow、springboot、maven,这种。
repositories是仓库,表示寻找依赖的地方,例如mavenCentrol、jCenter、mavenLocal这种。
jar 是指执行名为 "jar"的这个task 时需要做什么,这里写法不优雅,不要模仿,就是打包。
dependencies 是依赖,compile 是老一点的写法了,新写法是 implement,但还没有被废弃,compile 有两种格式。这个地方可能会 exclude 一些依赖中的依赖,或者 force 指定依赖版本。如果是依赖另一个本地 gradle 模块的话,使用compile project(":sub")这种格式。
再看看Android App的目录结构
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | ➜  ctfposed lsatotal 88
 drwxr-xr-x  18 leadroyal  staff   576B  6  7 14:12 .
 drwxr-xr-x  12 leadroyal  staff   384B  6  1 16:00 ..
 drwxr-xr-x   4 leadroyal  staff   128B  4 20  2017 .gradle
 drwxr-xr-x  12 leadroyal  staff   384B  6  7 16:03 .idea
 drwxr-xr-x  12 leadroyal  staff   384B  5 28 10:55 app
 -rw-r--r--   1 leadroyal  staff   498B  4 20  2017 build.gradle
 -rw-r--r--   1 leadroyal  staff   862B  2 25  2017 ctfposed.iml
 drwxr-xr-x   3 leadroyal  staff    96B  2 25  2017 gradle
 -rw-r--r--   1 leadroyal  staff   932B 11 28  2017 gradle.properties
 -rwxr--r--   1 leadroyal  staff   4.9K  2 25  2017 gradlew
 -rw-r--r--   1 leadroyal  staff   2.3K  2 25  2017 gradlew.bat
 drwxr-xr-x   3 leadroyal  staff    96B  2  3 17:35 lib
 -rw-r--r--   1 leadroyal  staff   523B  2 25  2017 local.properties
 -rw-r--r--   1 leadroyal  staff    15B  2 25  2017 settings.gradle
 
 | 
文件内容
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | ➜  ctfposed cat build.gradle// Top-level build file where you can add configuration options common to all sub-projects/modules.
 
 buildscript {
 repositories {
 jcenter()
 }
 dependencies {
 classpath 'com.android.tools.build:gradle:2.3.0'
 
 // NOTE: Do not place your application dependencies here; they belong
 // in the individual module build.gradle files
 }
 }
 
 allprojects {
 repositories {
 jcenter()
 }
 }
 
 task clean(type: Delete) {
 delete rootProject.buildDir
 }
 
 | 
这个说了等于没说,里面啥也没。
再看看 settings.gradle,原来引用了名叫":app"的子模块,所以要到"app"这个目录下去找。
| 12
 
 | ➜  ctfposed cat settings.gradleinclude ':app'
 
 | 
看看子模块的目录,刚好和之前的一样,但是没有 settings.gradle。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | ➜  ctfposed lsa apptotal 72
 drwxr-xr-x  12 leadroyal  staff   384B  5 28 10:55 .
 drwxr-xr-x  18 leadroyal  staff   576B  6  7 14:12 ..
 drwxr-xr-x   3 leadroyal  staff    96B  4 20  2017 .externalNativeBuild
 -rw-r--r--   1 leadroyal  staff     7B  2 25  2017 .gitignore
 -rw-r--r--   1 leadroyal  staff   343B  5 30  2017 CMakeLists.txt
 -rw-r--r--   1 leadroyal  staff    11K  5 28 10:55 app.iml
 drwxr-xr-x   6 leadroyal  staff   192B  2  3 17:38 build
 -rw-r--r--   1 leadroyal  staff   1.0K  4 14 23:06 build.gradle
 drwxr-xr-x   5 leadroyal  staff   160B  4 14 23:06 libs
 -rw-r--r--   1 leadroyal  staff   667B  2 25  2017 proguard-rules.pro
 drwxr-xr-x   5 leadroyal  staff   160B  2 25  2017 src
 
 | 
看看里面的内容
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 
 | ➜  ctfposed cat app/build.gradleapply plugin: 'com.android.application'
 
 android {
 compileSdkVersion 25
 buildToolsVersion "25.0.2"
 defaultConfig {
 applicationId "com.leadroyal.ctfposed"
 minSdkVersion 16
 targetSdkVersion 25
 versionCode 1
 versionName "1.0"
 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 ndk {
 // Specifies the ABI configurations of your native
 // libraries Gradle should build and package with your APK.
 abiFilters 'armeabi-v7a'
 }
 
 }
 buildTypes {
 release {
 minifyEnabled false
 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 }
 }
 externalNativeBuild {
 cmake {
 path "CMakeLists.txt"
 }
 }
 }
 
 dependencies {
 provided files('libs/XposedBridgeApi-54.jar')
 provided files('libs/hotposed.jar')
 compile 'com.android.support:appcompat-v7:25.1.0'
 compile files('libs/core-1.58.0.0.jar')
 }
 
 | 
嗯,使用了 com.android.application 插件,写了 android 这个东西,写了dependencies这个依赖。很清晰!
二、子模块使用独立的 build.gradle 的目录结构
最直观的就是 android 这个例子了,只有3个gradle文件。
| 12
 3
 4
 5
 6
 7
 
 | ➜  HelloWidget find . | grep \\w\\+\\.gradle./app/build.gradle
 ./build.gradle
 ./settings.gradle
 
 ➜  HelloWidget cat ./settings.gradle
 include ':app'
 
 | 
父模块里,settings.gradle 里写了子模块,叫 :app ,没有特别说明路径的话,就在app这个路径下。
子模块没有settings.gradle,这个无所谓,可有可无,也只是写几个名字而已。这也是我们最常见的格式,一父一子,父模块里写一些通用的设置,大部分代码都在子模块里。
再看另一个例子,我们手动创建Intellij项目,再多创建几个子模块。它们之间暂时没有依赖关系,相互虽然可能在 intellij 里会有提示,其实是相互不可见的那种。
这个 demo,在intellij里面操作非常方便,先创建空的 gradle 项目,会帮我们生成build.gradle 和 setting.gradle。
之后在里面创建新的gradle模块,选择Java类型,帮助我们自动生成 java 模板的 build.gradle。依法炮制,多创建几个java类型的 module,它们并没有什么联系,是相互独立的子项目。
| 12
 3
 4
 5
 6
 
 | ➜  simple-gradle find . | grep \\w\\+\\.gradle./part3/build.gradle
 ./part2/build.gradle
 ./part1/build.gradle
 ./build.gradle
 ./settings.gradle
 
 | 
这时候可以发现settings.gradle已经悄悄将它们 include 进来了。
| 12
 3
 4
 5
 
 | ➜ cat settings.gradlerootProject.name = 'simple-gradle'
 include 'part1'
 include 'part2'
 include 'part3'
 
 | 
在 build.gradle 里仍然是最开始的设置,这是第一种结构,子模块拥有独立的 build.gradle 文件。
右侧截图如图  
三、子模块使用父模块的 build.gradle 的目录结构
还是上面那个例子,我们改成只使用父模块的一个 build.gradle 文件的格式。
改之前:
| 12
 3
 
 | ➜  simple-gradle cat build.gradle group 'com.leadroyal'
 version '1.0-SNAPSHOT'
 
 | 
基本什么都没有。
改之后:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | ➜  simple-gradle cat build.gradleallprojects {
 group 'com.leadroyal'
 version '1.0-SNAPSHOT'
 }
 subprojects {
 apply plugin: "java"
 sourceCompatibility = 1.8
 repositories {
 mavenCentral()
 }
 }
 project(":part1") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":part2") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":part3") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":full") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 
 | 
这里需要使用 project(":part1") 这种方式来引入子模块,这样就可以删掉原来在分散的 build.gradle 的各种配置。
同样,在右侧可以看到合理的结构。
这里还可以使用 allprojects  和 subprojects  来批量进行一些操作,非常舒服。区别在于,allprojects 包括自己,而subprojects 不包括自己。
四、子模块之间如何相互依赖
还是以这part1/2/3为例,假设1独立,3依赖2。再写个总的,同时依赖1/2/3。 平时我们依赖一般都是依赖 maven 库,这里依赖本地的 project 的话,要用
compile project(":project-name")
例如我们刚刚说的这个例子
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | ➜  simple-gradlecat build.gradle allprojects {
 group 'com.leadroyal'
 version '1.0-SNAPSHOT'
 }
 subprojects {
 apply plugin: "java"
 sourceCompatibility = 1.8
 repositories {
 mavenCentral()
 }
 }
 project(":part1") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":part2") {
 dependencies {
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":part3") {
 dependencies {
 compile project(":part2")
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 project(":full") {
 dependencies {
 compile project(":part1")
 compile project(":part2")
 compile project(":part3")
 testCompile group: 'junit', name: 'junit', version: '4.12'
 }
 }
 
 | 
写了4个类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | package com.example.pkg1;
 public class Part1 {
 }
 
 package com.example.pkg2;
 
 public class Part2 {
 }
 
 package com.example.pkg3;
 
 import com.example.pkg2.Part2;
 
 public class Part3 extends Part2 {
 }
 
 package com.example.full;
 
 import com.example.pkg1.Part1;
 import com.example.pkg2.Part2;
 import com.example.pkg3.Part3;
 
 public class Main {
 public void func() {
 Part1 p1 = new Part1();
 Part2 p2 = new Part2();
 Part3 p3 = new Part3();
 }
 }
 
 | 
这个配置是可以正常编译通过的。
五、命令行里的一些操作
./gradlew build
使用当前项目提供的 gradle 版本和配置。  
gradle build
使用系统提供的 gradle 版本和配置。  
gradle dependencies
打印所有编译选项的依赖。  
gradle dependencies -q --configuration runtime
仅打印runtime执行时的依赖。  
gradle part1:build
执行 part1这个 project 的 build 任务。