Gradle For Android(5)--管理多Module的编译

多Module构建

通常一个多Module的工程会有一个根目录,而它的子目录下包含了所有的Module。为了告诉Gradle这个Project的结构,这个目录下包含了所有要构建的Modules,并且会有一个settings.gradle文件放在这个Project的根目录下。每一个Module都可以提供它自己的build.gradle文件。

一个多Module的Project目录结构如下:

project
   ├─── setting.gradle
   ├─── build.gradle
   ├─── app
   │    └─── build.gradle
   └─── library
        └─── build.gradle

这是一个最简单最直接多Module工程的配置。settings.gradle文件声明了在这个工程中的所有Module:

include ':app', ':library'

它保证了applibrary模块都包含在了Build配置中。我们所需要做的就是把模块的路径和名称添加到这个文件中即可。

如果需要把library模块作为依赖,包含到app模块的话,需要在app这个模块的build.gradle中添加以下代码:

dependencies {
       compile project(':library')
}

为了在一个Module中添加一个依赖Lib,需要使用project()这个函数,并且将该Lib的路径作为参数。如果想要使用子目录来组成Module的话,Gradle也可以进行配置,例如下面的工程结构:

project
├─── setting.gradle
├─── build.gradle
├─── app
│    └─── build.gradle
└─── libraries
     ├─── library1
     │    └─── build.gradle
     └─── library2
          └─── build.gradle

在这种情况下app模块仍然在根目录下,但是Project会有两个不同的Library,并且这些Library没有在根目录下,而是在一个子目录libraries下。像这种目录结构,我们可以在settings.gradle中使用以下声明:

include ':app', ':libraries:library1', ':libraries:library2'

所有的路径都是相对于根目录而言的,而根目录的定义也就是settings.gradle文件所在的地方。

当添加一个子目录的Module作为另一个Module的依赖关系的话,你应该相对于根目录来引用它。也就意味着,假如上述例子中,app模块依赖于library1,那么在app模块中的build.gradle文件如下:

dependencies {
       compile project(':libraries:library1')
}

如果你声明了子目录的依赖关系,所有的Path都应该相对于根目录。因为Gradle创建所有工程的依赖模型都是从Project的根目录开始的。

The build lifecycle revisited

了解构建过程模型会有助于理解多Module工程的打包。

在第一个阶段,也就是initialization阶段,Gradle会找到settings.gradle文件。如果这个文件不存在的话,那么Gradle会认为你只有一个单独的Module需要构建。如果有多个Module的话,那么这个settings.gradle文件就是可以定义子Module的地方。

如果这些子目录都有自己的build.gradle文件,那么Gradle就会处理这些,并且把他们添加到构建过程的Model中。这也就是为什么你应该在Module中使用相对于根目录的路径进行依赖。Gradle总是会根据根目录来配置依赖关系。

一旦你知道了构建过程Model是如何把他们放到一起的时候,我们也就知道了配置多Module的构建配置。我们可以在根目录的build.gradle中配置给所有的Module中使用的属性和设置。这样有助于我们可以得到整个Build Configuration的预览,不过可能会比较复杂,尤其是当你有很多不同的plugins的时候。

另一方面,每个模块都有单独的build.gradle文件,这种策略可以保证各个模块间不会那么紧密,并且它也可以更好的跟踪Build的修改,因为日志中就会打印出来它归属于哪个Module。

你可以在根目录下拥有一个Build文件,来定义一些通用的属性,让所有的Module都可以读取,而且每一个模块的配置都只在自己的模块内部生效,所以Android Studio在根目录创建了一个build.gradle文件,并且在Module中同样也有build.gradle文件。

Module tasks

当你已经拥有了多模块的工程后,你需要在执行任务之前思考一下。

当使用命令行在Project的根目录下执行一个Task的时候,Gradle会检查出哪个模块有这个名字的Task,然后为每个模块执行这个任务

例如,有一个Mobile APP模块,还有一个Android Wear模块,当执行gradlew assembleDebug的时候,就会构建Debug版本的Mobile App和Android Wear模块。当你修改路径到一个特殊的Module时,Gradle将只会执行单独的模块,即使你在Project的根目录下使用Gradle Wrapper的时候也一样。例如,执行../gradlew assembleDebug在Android Wear模块的目录下,将只会构建Android Wear模块。

改变目录,然后执行module中指定的任务来构建单独的模块会比较麻烦。我们可以配置Module名字以及Task名字,然后执行,就只会在特殊的Module中执行指定Task。例如,为了只构建Android Wear模块,我们可以使用gradlew :wear:assembleDebug命令来单独构建该模块。

Adding a Java library

当添加一个新的Java Library Module的话,Android Studio生成出来的build.gradle会跟下面的一样:

apply plugin: 'java'
   dependencies {
       compile fileTree(dir: 'libs', include: ['*.jar'])
}

Java Library Module会使用java插件,而不是使用Android Plugin。这也意味着很多Android特殊的属性和任务都不可用,不过在一个Java Library也不需要那些。

build.gradle也有基本的依赖管理,所以我们可以在libs目录下添加Jar文件,而不需要特殊的配置。如果需要添加一个Java Library Module名为javalib作为你App的依赖的话,需要添加这一行:

dependencies {
       compile project(':javalib')
}

这也告诉Gradle去引入一个名为javalib的模块。如果添加了这个依赖的话,那么javalib这个模块总会在App模块构建前完成构建。

Adding an Android library

生成一个Android Library,默认的build.gradle文件会以如下开始:

apply plugin: 'com.android.library'

添加其他工程的依赖于Java Library相同:

dependencies {
       compile project(':androidlib')
}

一个Android Library不仅仅只包含Java代码,还有Android的资源,比如说Strings,layouts,Manifest等。在引用了Android Library之后,我们可以使用Library的类以及资源。

Analyzing the build file

build.gradle文件比较大,所以只看一下一些比较重要的部分:

buildscript {
       dependencies {
           classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
       } 
}

App Engine Plugin需要在构建脚本中添加classpath

我们在添加Android Plugin的时候见到过,而在这个地方,我们可以再添加其他的两个插件:

   apply plugin: 'java'
   apply plugin: 'war'
   apply plugin: 'appengine'

java插件主要用来生成Jar包。而war插件是后端运行和分发的重要的插件,这个插件会生成一个War文件,可以在Java Web应用中被应用。最后appengine插件可以加载一系列构建的Task,执行并且部署后端。

下一个比较重要的代码块定义了App Engine模块的依赖:

dependencies {
       appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
       compile 'com.google.appengine:appengine-endpoints:1.9.18'
       compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
       compile 'javax.servlet:servlet-api:2.5'
}

第一个依赖使用了appengineSdk,指定了这个Module使用的SDK。endpoints``这个依赖是Cloud Enpoints工作所必须依赖的库,只有当你选择使用了Cloud Endpoints才需要被添加。而servlet```则是为了一些Google App Engine模块所使用的。

appengine代码块中指定App Engine特殊的配置:

appengine {
       downloadSdk = true
       appcfg {
           oauth2 = true
       }
       endpoints {
           getClientLibsOnBuild = true
           getDiscoveryDocsOnBuild = true
       } 
}

downloadSdk属性可以改变本地开发环境是否下载SDK。如果你已经在设备上安装了Google App Engine SDK的话,你可以设置downloadSdk属性为false。
appcfg代码块用来配置App Engine SDK,在一个典型的Google App Engine的安装过程中,你可能手动的在命令行配置一些参数。而使用appcfg代码块后,可以使用它来替代命令行参数。
endpoints代码块包含了一些Cloud Endpoints特殊的配置。

Using the backend in an app

当创建了一个App Engine模块的时候,Android Studio会自动的在build.gradle文件中添加依赖。

dependencies {
       compile project(path: ':backend', configuration: 'android-endpoints')
}

Speeding up multimodule builds

当构建一个多Module的工程时候,Gradle处理会线性的处理所有Module。随着电脑的核越来越多,我们可以让构建的过程并行处理。该特性已经在Gradle中存在了,但是默认是不可用的。如果你想在工程中应用的话,需要在gradle.properties中添加配置:

org.gradle.parallel=true

Module coupling

当你有多Module的工程的话,你可以使用allprojects在任何模块中用到这些属性。Gradle可以让一个模块去引用另外一个模块的属性,这样会使得多模块的构建变得简单一些,但是会让模块间变得耦合。

两个模块间当要访问对方Task或者Properties的时候,就会变得耦合。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容