在使用Android Studio過程當中沒少被Gradle坑過,雖然網上有不少簡單粗暴的解決方案,但極少會說清楚原因,因此一直想看一本叫《Android Gradle權威指南》。 不過因爲書中實踐內容不少,更像一本工具書,並且Gradle現已發行了好幾版,所以本篇僅僅是陳列出一些大的要點,尤爲是那些熟悉又陌生的名詞,若是想要具體瞭解細節和操做流程,必定要跟着書探索喲~html
一.Gradle入門java
1.本書環境python
- JDK:OpenJDK 1.8.0
- Gradle:Gradle 2.14.1 All 版
- IDE:Android Studio 2.2.3
- Android Plugin:Android Gradle 2.2.3
- Android:API 23
2.Eclipse和Android Studioandroid
a.開發配置區別:git
b.Eclipse遷移到AndroidStudio兩種方式:api
3.Gradle的ZIP解壓後目錄安全
- docs:API、 DSL、指南等文檔
- init.d:gradle初始化腳本目錄
- lib:相關庫
- media:一些icon資源
- samples:示例
- src:源文件
- getting-started.html:入門連接
- LICENSE
- NOTICE
![]()
4.引例:Gradle版Hello World服務器
//build.gradle:
task hello{//定義一個任務Task名爲hello
doLast{//添加一個動做Action,表示在Task執行完畢後回調doLast閉包中的代碼
println'Hello World'//輸出字符串,單雙號都可
}
}
//終端:
gradle hello//執行build.gradle中名爲Hello的任務
//輸出:
Hello World
複製代碼
5.Gradle Wrapper閉包
a.含義:對Gradle一層包裝,便於使用統一Gradle構建app
b.目錄結構:
|--gradle
| |--wrapper
| |--gradle-wrapper.jar
| |--gradle-wrapper.properties
|--gradlew
|--gradlew.bat
複製代碼
gradle-wrapper.jar
:具體業務邏輯實現的jar包gradle-wrapper.properties
:配置文件,包含篇配置信息以下圖:
gradlew
:Linux下可執行腳本gradlew.bat
:Windows下可執行腳本c.經常使用命令:
gradle wrapper
,由Wrapper Task生成gradle wrapper --gradle-version XXX
用於指定使用的Gradle版本gradle wrapper --distribution-url XXX
用於指定下載Gradle發行版的url地址task wrapper(type:Wrapper){ //配置信息 }
6.Gradle日誌
a.日誌級別:
println 'XX'X
gradle -q tasks
7.Gradle命令行
./gradlew tasks
./gradlew --refresh-dependencies assemble
./gradlew clean jar
./gradlew -?
或./gradlew -h
或./gradlew -help
二.Groovy基礎
一句話代表Groovy的地位:Groovy於Gradle,比如Java於Android
1.特性:Groovy是個靈活的動態腳本語言,語法和Java很類似,又兼容Java,且在此基礎上增長了不少動態類型和靈活的特性,如支持閉包和DSL
2.語法
task printStringVar << {
def name = "張三」
println '單引號的變量計算:${name}'
println "雙引號的變量計算:${name}"
}
運行./gradlew printStringVar輸出結果:
單引號的變量計算:${name}
雙引號的變量計算:張三
複製代碼
//List
task printList<<{
def numList = [1,2,3,4,5,6];//定義一個List
println numList[1]//輸出第二個元素
println numList[-1]//輸出最後一個元素
println numList[1..3]//輸出第二個到第四個元素
numList.each{
println it//輸出每一個元素
}
}
//Map
task printlnMap<<{
def map1 =['width':1024,'height':768]//定義一個Map
println mapl['width']//輸出width的值
println mapl.height//輸出height的值
map1.each{
println "Key:${it.key},Value:${it.value}"//輸出全部鍵值對
}
}
複製代碼
//以集合的each方法爲例,接受的參數就是一個閉包
numList.each({println it})
//省略傳參的括號,並調整格式,有如下常見形式
numList.each{
println it
}
複製代碼
task helloJavaBean<<{
Person p = new Person()
p.name = "張三"
println "名字是: ${p.name}"//輸出類屬性name,爲張三
println "年齡是: ${p.age}"//輸出類屬性age,爲12
}
class Person{
private String name
public int getAge(){//省略return
12
}
}
複製代碼
//單個參數
task helloClosure<<{
customEach{
println it
}
}
def customEach(closure){
for(int i in 1..10){
closure(i)
}
}
//多個參數
task helloClosure<<{
eachMap{k,v->
println "${k} is ${v}"
}
}
def eachMap(closure){
def map1 = ["name":"張三","age":18]
map1.each{
closure(it.key,it.value)
}
}
複製代碼
1.Settings文件
settings.gradle
,放在Project下//添加:app和:common這兩個module參與構建
include ':app'
project(':app').projectDir = new File('存放目錄')
include':common'
project(':common').projectDir = new File('存放目錄')
複製代碼
2.Build文件
build.gradle
:整個Project的共有屬性,包括配置版本、插件、依賴庫等信息build.gradle
:各個module私有的配置文件
3.Gradle任務
a.含義:指原子性操做
b.關係:一個Gradle可包含多個Project,一個 Project可包含多個Task,即每一個Project是由多個Task組成的;Task是Project的屬性,屬性名就是任務名
c.建立
def task myTask = task(myTask)
myTask.doLast{
println "第一種建立Task方法,原型爲Task task(String name) throws InvalidUserDataException"
}
複製代碼
def task myTask = task(myTask,group:BasePlugin.BUILD_GROUP)
myTask.doLast{
println "第二種建立Task方法,原型爲Task task(String name,Map<String,?> args) throws InvalidUserDataException"
}
複製代碼
task myTask{
doLast{
println "第三種建立Task方法,原型爲Task task(String name,Closure configureClosure)"
}
}
複製代碼
以上建立方式實際上最終都會調用
TaskContainter#create()
方法,使用./gradlew myTask
命令執行任務
d.訪問
名稱.方法
tasks['名稱'].方法
tasks.getByPath('路徑/名稱')
,若不存在會拋出UnknownTaskException異常tasks.findByPath('路徑/名稱')
,若不存在返回nulltasks.getByName('名稱')
,若不存在會拋出UnknownTaskException異常tasks.findByName('名稱')
,若不存在返回null可見任務名稱是惟一的,這是由於TaskContainer的父類 NamedDomainObjectCopllection是個具備惟一不變名字的域對象的集合
e.依賴:在建立任務時經過dependsOn指定其依賴的任務,能夠控制任務的執行順序
task task1<<{
println 'hello'
}
task task2<<{
println 'world'
}
//依賴單個任務
task task3(dependsOn:task1){
doLast{
println 'one'
}
}
//依賴多個任務
task task4{
dependsOn task1,task2
doLast{
println 'two'
}
}
複製代碼
當執行task4時,會發現task一、task2會先執行,再執行task4
注:操做符<< 用在Task定義上至關於doLast
f.排序:除了經過強依賴來控制任務的執行順序,還能夠經過 shouldRunAfter
和 mustRunAfter
實現
taskB.shouldRunAfter(taskA) //表示taskB應該在taskA執行以後執行,有可能不會按預設執行
taskB.mustRunAfter(taskA) //表示taskB必須在taskA執行以後執行
複製代碼
g.分組& 描述:分組是對任務的分類,便於歸類整理;描述是說明任務的做用;建議兩個一塊兒配置,便於快速瞭解任務的分類和用途
def task myTask = task(myTask)
myTask .group = BasePlugin.BUILD_GROUP
myTask .description = '這是一個構建的引導任務'
複製代碼
h.啓用 & 禁用:enable屬性能夠啓動和禁用任務,執行被禁用的任務輸出提示該任務被跳過
def task myTask = task(myTask)
myTask.enable = false //禁用任務
複製代碼
i.執行分析:執行Task的時候其實是執行其擁有的actions List,它是Task對象實例的成員變量;在建立任務時Gradle會解析其中被TaskAction註解的方法做爲其Task執行的action,並添加到 actions List,其中doFirst和doList會被添加到action List第一位和最後一位
4.自定義屬性
Project、Task和SourceSet都容許用戶添加額外的自定義屬性、並對自定義屬性進行讀取和設置
//給Project添加自定義屬性
ext.age = 18
ext{
phone = 13888888888
address = 'Beijing'
}
//給Task添加自定義屬性
task customProperty {
ext.inner = 'innnnnner'
doLast{
println project.hasProperty('customProperty') //true
println project.hasProperty('age') //true
println project.hasProperty('inner')//返回fasle
println "${age}"
println "${phone}"
println "${inner}"
}
}
複製代碼
四.Gradle插件
1.做用
Gradle自己內置許多經常使用的插件,如若須要還能夠擴展示有插件或者自定義插件,如Android Gradle插件就是基於內置的Java插件實現的
2.擴展示有插件
a.插件種類
org.gradle.api.Plugin
接口的插件,能夠有plugin idb.應用插件:經過Project#apply()
方法,有三種用法
void apply (Map<String, ?> options)
apply plugin:'java'
apply plugin:org.gradle.api.plugins.JavaPlugin
apply plugin:JavaPlugin
apply from:'version.gradle'
apply plugin:'com.android.application'
注意:應用第三方發佈的做爲jar的二進制插件時,必須先在
buildscript{}
配置其classpath才能使用,不然會提示找不到該插件
//buildscript:爲項目進行前提準備和初始化相關配置依賴
buildscript {
repositories {
jcenter ()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0"
}
}
apply plugin:'com.android.application
複製代碼
void apply (Closure closure)
apply {
plugin:'java'
}
複製代碼
void apply (Action<? super ObjectConfigurationActicn> action)
3.自定義插件:實現Plugin接口、重寫apply()方法
1.項目結構
使用Java插件要先應用進來:
apply plugin:'java'
複製代碼
此時會添加許多默認設置和約定,好比有如下默認項目結構:
|-example
| |-build.gradle
| |-src
| |-main
| |-java 源代碼存放目錄
| |-resources 打包文件存放目錄
| |-test
| |-java 單元測試用例存放目錄
| |-resources 單元測試中使用的文件
複製代碼
2.源集(SourceSet)
sourceSets
屬性(是一個SourceSetsContainer)和sourceSets{}
閉包好比,在上述Java插件默認項目結構中的main和test就是內置的兩個源集,如今更改main源集的Java源文件的存放目錄到src/java下:
apply plugin:'java'
sourceSets{
main{
java{
srcDir 'src/java'
}
}
}
複製代碼
sourceSets{}
閉包添加apply plugin:'java'
sourceSets{
vip{
}
}
複製代碼
此時會新建兩個目錄:src/vip/java和src/vip/resources
補充:除了SourceSet,Java插件裏經常使用的其餘屬性:
![]()
3.配置第三方依賴
a.依賴方式
b.具體方法
repositories{}
閉包裏聲明依賴庫的位置dependencies{}
閉包添加依賴
group:name:version
project('項目名稱')
files('文件名稱')
,多個文件逗號分開,或者fileTree(dir:'文件名稱',include:'*.擴展名稱')
依賴指定文件夾下指定擴展名文件舉例:
apply plugin:'java'
repositories {
//外部依賴 依賴Maven中心庫
maveCentral()
}
dependencies {
//外部依賴 完整寫法
compile group:'com.squareup.okhttp3',name:'okhttp', version:'3.0.1'
//外部依賴 簡單寫法
compile 'com.squareup.okhttp3:okhttp:3.0.1'
//外部依賴 指定main源集依賴
mainCompile 'com.squareup.okhttp3:okhttp:3.0.1'
//項目依賴
compile project(':example')
//文件依賴 依賴libs下兩個Jar包
compile files('libs/example01.jar', 'libs/example02.jar')
//文件依賴 指定依賴libs下全部Jar包
compile fileTree(dir: 'libs',include: '*.jar')
}
複製代碼
4.內置任務
經常使用幾種任務:
build
任務:構建項目clean
任務:刪除build目錄及構建生成的文件assemble
任務:不執行單元測試,只編譯和打包check
任務:只執行單元測試javadoc
任務:生成Java格式的doc api文檔還有些通用任務、對源集適用的任務:
5.多項目構建
settings.gradle
配置管理多項目;在每一個項目都有一個build.gradle
,採用項目依賴就能實現多項目協做//settings.gradle
include ':app'
project(':app').projectDir = new File('存放目錄')
include ':base'
project(':base').projectDir = new File('存放目錄')
//app/build.gradle
apply plugin:'java'
dependencies {
compile project(':base')
}
複製代碼
6.發佈構件
artifacts{}
閉包配置須要發佈的構建,在uploadArchives{}
上傳發布構件//以發佈jar構件爲例
apply plugin:'java '
task publishJar(type:Jar)
artifacts{
archives publishJar
}
uploadArchives{
repositories{
//發佈到本地目錄
flatDir{
name 'libs'
dirs "$projectDir/libs"
}
//發佈到本地Maven庫
mavenLocal()
}
}
複製代碼
1.概述
Android Gradle插件繼承於Java插件,具備Java插件的全部特性,也有本身的特性,看下官方介紹:
2.插件分類
com.android.application
com.android.library
com.android.test
3.項目結構
|-example
| |-build.gradle
| |-example.iml
| |-libs
| |-proguard-rules.pro 混淆配置文件
| |-src
| |-androidTest
| |-java Android單元測試代碼
| |-main
| |-java App主代碼
| |-res 資源文件
| |-AndroidManifest.xml 配置文件
| |-test
| |-java 普通單元測試代碼
複製代碼
4.內置任務
connectedCheck
任務:在全部鏈接的設備或者模擬器上運行check檢查deviceCheck
任務:經過API鏈接遠程設備運行checkslint
任務:在全部ProductFlavor上運行lint檢查install
、uninstall
任務:在已鏈接的設備上安裝或者卸載AppsigningReport
任務:打印App簽名androidDependencies
任務:打印Android 依賴5.應用實例
//應用插件,Android Gradle屬於Android發佈的第三方插件
buildscript{
repositories{
jcenter()
}
dependencies{
classpath 'com.android.tcols.build:gradle:1.5.0'
}
}
apply plugin:'com.android.application'
//自定義配置入口,後續詳解
android{
compileSdkVersion 23 //編譯Android工程的SDK版本
buildToolsVersion "23.0.1" //構建Android工程所用的構建工具版本
defaultConfig{
applicationId "org.minmin.app.example"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes{
release{
minifyEnabled false
proguardFiles getDefaultPraguardFile('proguard-andrcid.txt'), 'proguard-rules.pro'
}
}
}
//配置第三方依賴
dependencies{
compile fileTree(dir:'libs', include:['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcorpat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
}
複製代碼
a.defaultConfig
屬性名 | 含義 |
---|---|
applicationId | 指定App包名 |
minSdkVersion | 指定App最低支持的Android SDK |
targetSdkVersion | 指定基於的Android SDK |
versionCode | 配置Android App的內部版本號 |
versionName | 配置Android App的版本名稱 |
testApplicationId | 配置測試App的包名,默認爲applicationId + 「.test」 |
testInstrumentationRunner | 配置單元測試使用的Runner,默認爲android.test.InstrumentationTestRunner |
proguardFile | 配置App ProGuard混淆所使用的ProGuard配置文件 |
proguardFiles | 同時配置多個ProGuard配置文件 |
signingConfig | 配置默認的簽名信息,也是一個ProductFlavor,可直接配置 |
b.buildTypes
assemble<BuildTypeName>
任務屬性名 | 含義 |
---|---|
applicationIdSuffix | 配置基於默認applicationId的後綴 |
debuggable | 是否生成一個可供調試的Apk |
jniDebuggable | 是否生成一個可供調試JNI代碼的Apk |
minifyEnabled | 是否啓用Proguard混淆 |
multiDexEnabled | 是否啓用自動拆分多個Dex的功能 |
zipAlignEnabled | 是否開啓開啓zipalign優化,提升apk運行效率 |
shrinkResources | 是否自動清理未使用的資源,默認爲false |
proguardFile | 配置Proguard混淆使用的配置文件 |
proguardFiles | 同時配置多個ProGuard配置文件 |
signingConfig | 配置默認的簽名信息,也是一個ProductFlavor,可直接配置 |
c.signingConfigs
屬性名 | 含義 |
---|---|
storeFile | 簽名證書文件 |
storePassword | 簽名證書文件的密碼 |
storeType | 簽名證書的類型 |
keyAlias | 簽名證書中密鑰別名 |
keyPassword | 簽名證書中該密鑰的密碼 |
android {
signingConfigs {
release{
storeFile file('myFile.keystore')
storePassword 'psw'
keyAlias 'myKey'
keyPassword 'psw'
}
}
}
複製代碼
d.productFlavors
屬性名 | 含義 |
---|---|
applicationId | 設置該渠道的包名 |
consumerProguardFiles | 對aar包進行混淆 |
manifestPlaceholders | |
multiDexEnabled | 啓用多個dex的配置,可突破65535方法問題 |
proguardFiles | 混淆使用的文件配置 |
signingConfig | 簽名配置 |
testApplicationId | 適配測試包的包名 |
testFunctionalTest | 是不是功能測試 |
testHandleProfiling | 是否啓用分析功能 |
testInstrumentationRunner | 配置運行測試使用的Instrumentation Runner的類名 |
testInstrumentationRunnerArguments | 配置Instrumentation Runner使用的參數 |
useJack | 標記是否啓用Jack和Jill這個全新的、高性能的編譯器 |
dimension | 維度,經過flavorDimensions方法聲明,聲明先後表明優先級 |
//定義baidu和google兩個渠道,並聲明兩個維度,優先級爲abi>version>defaultConfig
android{
flavorDimensions "abi", "version"
productFlavors{
google{
dimension "abi"
}
baidu{
dimension "version"
}
}
複製代碼
e.buildConfigFiled
buildConfigField(String type,String name,String value)
android{
buildTypes{
debug{
buildConfigField "boolean", "LOG_DEBUG", "true"
buildConfigField "String", "URL", ' "http://www.ecjtu.jx.cn/" '
}
}
}
複製代碼
6.多項目構建
和Java Grdle多項目構建同樣的,經過settings.gradle
配置管理多項目;在每一個項目都有一個build.gradle
,採用項目依賴就能實現多項目協做。
項目直接依賴通常適用於關聯較緊密、不可複用的項目,若是想讓項目被其餘項目所複用,好比公共組件庫、工具庫等,能夠單獨發佈出去。
7.多渠道構建
a.基本原理
Build Type有release、debug兩種構建類型 Product Flavor有baidu、google兩種構建渠道 Build Variant有baiduRelease、baiduDebug、googleRelease、googleDebug四種構件產出
構建渠道(Product Flavor)還能夠經過dimension進一步細化分組
assemble開頭的負責生成構件產物(Apk)
assembleBaidu:運行後會生成baidu渠道的release和debug包 assembleRelease:運行後會生成全部渠道的release包 assembleBaiduRelease:運行後只會生成baidu的release包
b.構建方式:經過佔位符manifestPlaceholders
實現:
//AndroidManifest
<meta-data
android: value="Channel ID"
android:name="UMENG_ CHANNEL"/>
//build.gradle
android{
productFlavors{
google{
manifestPlaceholders.put("UMENG_ CHANNEL", "google")
}
baidu{
manifestPlaceholders.put("UMENG_ CHANEL", "baidu")
}
}
複製代碼
//改進:經過productFlavors批量修改
android{
productFlavors{
google{
}
baidu{
}
ProductFlavors.all{ flavor->
manifestPlaceholders.put("UMENG_ CHANEL", name)
}
}
複製代碼
8.高級應用
a. 使用共享庫
<uses-library>
指定//聲明須要使用maps共享庫,true表示若是手機系統不知足將不能安裝該應用
<uses-library
android:name="com.google.android.maps"
android:required="true"
/>
複製代碼
b. 批量修改生成的apk文件名
applicationVariants
:僅僅適用於Android應用Gradle插件libraryVariants
:僅僅適用於Android庫Gradle插件testVariants
:以上兩種Gradle插件都使用
applicationVariants
是一個DomainObjectCollection集合,經過all方法遍歷每個ApplicationVariant,這裏有googleRelease和googleDebug兩個變體;而後判斷名字是否以.apk結尾,若是是就修改其文件名。示例中共有。
c.動態生成版本信息
defaultConfig
中的versionName
指定ext{}
括起來,經過apply from將其引入到build.gradle,版本信息就被看成擴展屬性直接使用了d.隱藏簽名文件信息
signingConfigs {
if (System.env.KEYSTORE_PATH != null) {
//打包服務器走這個邏輯
storeFile file(System.env.KEYSTORE_PATH)
keyAlias System.env.ALIAS
keyPassword System.env.KEYPASS
storePassword System.env.STOREPASS
} else {
//當不能從環境變量取到簽名信息時,使用本地debug簽名
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
複製代碼
e.動態添加自定義的資源
productFlavors{
google{
resValue 'string', 'channel_tips', 'google渠道歡迎你'
}
}
複製代碼
以google爲例,在debug模式下,資源文件保存目錄:build/generated/res/resValues/google/debug/values/generated.xml
f.Java編譯選項
經過compileOptions{}
閉包進行編譯配置,可配置項:
android{
compileOptions{
encoding = 'utf-8'
sourceCompatibility = JavaVersion.VERSI0N_ 1_ 6
targetCompatibility = JavaVersion.VERSION_ 1_ 6
}
}
複製代碼
g. adb選項配置
經過adbOptions{}
閉包進行adb配置,可配置項:
android{
adbOptions{
timeOutInMs = 5*1000
installOptions '-r', '-s'
}
}
複製代碼
h.DEX選項配置
經過dexOptions {}
閉包進行dex配置,可配置項: