Gradle 學(xué)習(xí)筆記

Gradle 是一款構(gòu)建系統(tǒng)工具奠宜,它的 DSL 基于 Groovy 實現(xiàn)跑杭。Gradle 構(gòu)建的大部分功能都是通過插件方式來實現(xiàn),所以非常靈活啡彬,還可以自定義自己的插件羹与。

Gradle 命令

  1. gradle wrapper 生成 Wrapper
  2. gradle -q 打印日志,-s 關(guān)鍵性堆棧信息庶灿,-S 全部堆棧信息
  3. gradle tasks 查看有哪些 Task 可以執(zhí)行
  4. gradle help --task tasks 查看指定 Task 的幫助
  5. gradle --refresh-dependencies <task> 執(zhí)行特定 Task 前強(qiáng)制刷新依賴
  6. gradle clean jar 多任務(wù)調(diào)用注簿,只需按順序以空格分開即可,多個任務(wù)可以繼續(xù)添加
  7. gradle aR 可以縮寫調(diào)用基于駝峰命名的 Task跳仿,aR 表示 assembleRelease 可以調(diào)用的前提是沒有兩個及以上縮寫沖突的 Task

Groovy 語言基礎(chǔ)

通過 def 關(guān)鍵字定義變量和方法

1. 字符串的表示

單引號和雙引號都可以定義一個字符串诡渴,區(qū)別是單引號中的所有表達(dá)式都是常量字符串,而雙引號可以對字符串里的表達(dá)式做運算。一個美元符號加一對花括號妄辩,花括號里放表達(dá)式惑灵,例如:{a+b},只有一個變量時可以省略花括號眼耀,例如:name

2. 集合

// List 的定義:
def numList = [1,2,3,4,5,6] 
// 訪問:
numList[1] // 第二個元素 
numList[-1] // 倒數(shù)第一個元素
numList[-2] // 倒數(shù)第二個元素
numList[1..3] // 訪問第二個到第四個元素

// each 方法用來完成遍歷操作
task pritList << {
    def numList = [1,2,3,4,5,6]
    numList.each {
        // it 表示遍歷到的元素
        pritln it 
    }
}


// Map 的定義
def map = ['width':1024 , 'height':768]
// Map 的訪問
map['width']
map.height

// Map 的遍歷,遍歷到的是一個 Map.Entry 元素
task printMap << {
    def map = ['width':1024 , 'height':768]
    map.each {
        println "Key:${it.key},Value:${it.value}"
    }
}

3. 方法

方法的調(diào)用可以省略(), method(1,2) 可以寫成:method 1,2

有返回值的時候 return 語句不是必須的英支,沒有 return 的時候,Groovy 會把最后一句代碼作為其返回值

Groovy 支持閉包哮伟,如果最后一個參數(shù)時閉包干花,可以將閉包的花括號寫到 () 外面,并且如果只有閉包一個參數(shù)楞黄,() 可以不寫

list.each({println it})
// 簡化
list.each {
    println it
}

4. JavaBean

Groovy 中并不一定要定義 getter/setter 方法才能訪問成員變量池凄,并且定義了 getter/setter 方法后我們也可以訪問沒有聲明的成員變量

5. 向閉包傳遞參數(shù)

如果只有一個參數(shù),直接放入調(diào)用閉包時的括號里面鬼廓,默認(rèn)使用時就是 it肿仑,,如果是多個參數(shù)碎税,閉包中就必須要顯示聲明出來

// 定義一個多個參數(shù)的閉包尤慰,并傳入 eachMap 方法,閉包需要兩個參數(shù)雷蹂,通過 k 和 v 區(qū)分
eachMap{k,v -> println "$k is $v"}

6. 閉包委托

Groovy 閉包支持閉包方法的委托伟端,Groovy 的閉包有 thisObject owner delegate 三個屬性,當(dāng)在閉包中調(diào)用方法時匪煌,由他們來確定哪個對象來處理责蝠,默認(rèn)情況下 delegate 和 owner 是相等的,但是 delegate 是可以被修改的虐杯,Gradle 中的閉包的很多功能都是通過修改 delegate 實現(xiàn)的

thisObject 的優(yōu)先級最高玛歌,默認(rèn)情況下使用 thisObject 來處理閉包中調(diào)用的方法昧港,如果有則執(zhí)行擎椰。thisObject 其實就是這個構(gòu)建腳本的上下文,和腳本中的 this 是相當(dāng)?shù)?/p>

owner 的優(yōu)先級比 delegate 高创肥,所以閉包內(nèi)方法的調(diào)用順序是:thisObject > owner > delegate

在 DSL 中达舒,比如 Gradle,我們一般會指定 delegate 為當(dāng)前的 it叹侄,這樣就可以在閉包中對該 it 進(jìn)行配置巩搏,或者調(diào)用其方法

task configClosure {
    // 設(shè)置委托模式優(yōu)先后,就可以在閉包中直接多該 Person 實例配置以及進(jìn)行方法調(diào)用
    person {
        personName = "Jerry"
        personAge = 20
        dumpPerson()
    }
}

class Person {
    String personName
    int personAge
    
    def dumpPerson() {
        println "name:$personName age:$personAge"
    }
}

def person(Closure<Person> closure) {
    Person p = new Person();
    closure.delegate = p
    // 委托模式優(yōu)先
    closure.setResolveStrategy(Closure.DELEGATE_FIRST);
    closure(p)
}

Gradle 構(gòu)建腳本基礎(chǔ)

1. Setting 文件

setting.gradle 用于初始化及工程樹的配置趾代,放在根目錄下贯底,該文件的大多數(shù)作用都是為了配置子工程,在 Gradle 中多工程是通過工程樹表示的撒强,類比 AndroidStudio 中的 Project 和 Module禽捆,根工程相當(dāng)于 Project笙什,子工程相當(dāng)于 Module,一個根工程可以有多個子工程

一個子工程只有在 Setting 文件中配置了 Gradle 才會識別胚想,才會在構(gòu)建時被包含進(jìn)去琐凭。配置一個子工程時可以指定相應(yīng)的目錄,如果不指定浊服,默認(rèn)目錄是 Setting 文件其同級的目錄

配置子工程時统屈,子工程的名字可以不與目錄相同,只要其他工程添加依賴時使用配置子工程時的名字即可

// 配置一個子工程
include ':example02'
// 指定子工程的目錄
project(':example02').projectDir = new File(rootDir,'chapter01/example02')

2. Project 和 Task

Project 就是一個個獨立的模塊牙躺,多個 Project 組成了整個 Gradle 的構(gòu)建愁憔,而 Project 是由一個個 Task 構(gòu)成的,Task 就是一個操作述呐,一個原子性操作惩淳,比如打一個 jar 包,復(fù)制一個文件等乓搬。

定義一個 Task

// 調(diào)用 Project 的 task 方法思犁,接受一個 name(任務(wù)名稱) 為參數(shù),返回一個 Task 對象
def Task exTask1 = task(exTask1)

// 以一個任務(wù)名字 + 一個對該任務(wù)配置的 Map 對象來創(chuàng)建
def Task exTask2 = task(exTask2, group: BasePlugin,BUILD_GROUP)

// 以一個任務(wù)名字加閉包的形式进肯,閉包中也可以添加任務(wù)配置
task customTask1 {
    doFirst {
        println "first"
    }
    
    doLast {
        println "last"
    }
}

這里的 task 看著像一個 關(guān)鍵字激蹲,其實他是 Project 對象的一個函數(shù),原型為 create(String name,Closure configureClosure) ,customeTask1 為任務(wù)的名字江掩,第二個參數(shù)是一個閉包学辱,將閉包提到括號外表再省略 () 就成了上面簡潔的寫法。其中 doFirst 和 doLast 是任務(wù)的兩個方法环形,分別在任務(wù)執(zhí)行前后會調(diào)用策泣,此外,Task 還有其他方法和屬性

還可以通過 TaskContainer 創(chuàng)建任務(wù)抬吟,其實以上提到的 Task 創(chuàng)建方式最終都是通過這種方式創(chuàng)建的萨咕,在 Gradle 里,Project 對象已經(jīng)定義好了一個 TaskContainer火本,就是 tasks 危队,以上幾種創(chuàng)建方式的作用是一樣的

// TaskContainer 方式創(chuàng)建 Task
tasks.create("customTask2") {
   doFirst{println "first"}
   doLast{println "last"}
}

3. 任務(wù)依賴

創(chuàng)建任務(wù)的時候,通過 dependsOn 指定其依賴的任務(wù),

task exHello {
    println 'hello'
}

// 依賴一個任務(wù)
task exMain (dependsOn: exHello) {
    doLast { println 'world'}
}

// 依賴多個任務(wù)钙畔,dependsOn 是 Task 類的一個方法茫陆,可以接收多個依賴的任務(wù)作為參數(shù)
task exMain2 {
    dependsOn exHello exMain
    doLast {println 'end'}
}

4. 任務(wù)間通過 API 控制、交互

創(chuàng)建一個任務(wù)類似定義一個變量擎析,變量名就是任務(wù)名簿盅,類型是 Task,使用 Task 的 API 可以訪問他的方法和屬性或者對任務(wù)重新配置,和變量一樣桨醋,要使用任務(wù)名操作任務(wù)见秽,必須先定義聲明,因為腳本是順序執(zhí)行的

Project 在創(chuàng)建任務(wù)的時候讨盒,會同時把該任務(wù)對應(yīng)的任務(wù)名注冊為 Project 的一個屬性解取,類型是 Task

task exHello {
    println 'hello'
}

exHello.doFirst {
    println 'fist'
}

5. 自定義屬性

Project 和 Task 都允許用戶添加額外的自定義屬性,通過應(yīng)用所屬對應(yīng)的 ext 屬性來添加額外的屬性返顺。添加之后可以通過 ext 屬性對自定義屬性讀取和設(shè)置禀苦,如果要同時添加多個自定義屬性,可以通過 ext 代碼塊遂鹊。

相比局部變量振乏,自定義的屬性有更廣泛的作用域,可以跨 Project秉扑,跨 Task 訪問這些自定義屬性慧邮,前提時能訪問這些屬性所屬的對象。項目組一般使用它來自定義版本號和版本名稱等

// 自定義一個 Project 的屬性
ext.age = 18

// 通過代碼庫同時自定義多個屬性
ext {
    phone = '18000000000'
    name = 'Hen'
}

Gradle 任務(wù) - Task

1. 多種任務(wù)訪問方式

首先舟陆,我們創(chuàng)建的任務(wù)都會作為 Project 的一個屬性误澳,屬性名就是任務(wù)名,我們可以通過該任務(wù)名稱訪問和操作該任務(wù)

其次任務(wù)都是通過 TaskContainer 創(chuàng)建的秦躯,其實 TaskContainer 就是我們創(chuàng)建任務(wù)的集合忆谓,在 Project 中可以通過 tasks 屬性訪問 TaskContainer ,所以就可以以訪問集合元素的方式訪問我們創(chuàng)建的任務(wù),訪問的時候踱承,任務(wù)名就是 [] 操作符的參數(shù)倡缠,[] 在 Groovy 中是一個操作符

task exAccessTask

tasks['exAccessTask'].doLast {...}

然后還有通過路徑訪問和通過名稱訪問,這兩個都有 find 和 get 兩種方式茎活,區(qū)別是 get 的時候如果找不到該任務(wù)就會拋出 UnkownTaskException 而 find 在找不到任務(wù)時會返回 null

task exAccessTask1

tasks['exAccessTask1'].doLast {
    // 通過路徑訪問
    tasks.findByPath(':exacple:exAccessTask')
    tasks.getByPath(':exacple:exAccessTask1')

    // 通過名稱訪問
    tasks.findByName('exAccessTask')
    tasks.getByName('exAccessTask1')
}

2. 任務(wù)分組和描述

任務(wù)的分組其實就是對任務(wù)的分類昙沦,便于對任務(wù)進(jìn)行歸類整理。任務(wù)的描述就是說明這個任務(wù)的作用载荔。

def Task myTask = task example

example.group = BasePlugin.BUILD_GROUP
example.description = '這是一個構(gòu)建的引導(dǎo)任務(wù)'

3. << 操作符

<< 操作符在 Gradle 的 Task 上是 doLast 方法的對標(biāo)記形式

// 定義一個 exDoLast 的 task 并調(diào)用 doLast 方法
task(exDoLast) >> {
    ...
}

4. 任務(wù)的執(zhí)行分析

當(dāng)我們執(zhí)行一個 Task 的時候盾饮,其實就是遍歷執(zhí)行其擁有的 actions 列表,這個列表保存在 Task 對象實例的 actions 成員變量中身辨,其類型是一個 List丐谋。自定義的 Task 類型中可以聲明一個被 TaskAction 注解標(biāo)注的方法芍碧,意思是該 Task 本身執(zhí)行要執(zhí)行的方法煌珊。當(dāng)我們使用 Task 方法創(chuàng)建任務(wù)的時候,Gradle 會解析其中被 TaskAction 注解的方法作為其 Task 執(zhí)行的 Action泌豆,然后把該 Action 添加到 actions List 里定庵,doFirst 和 doList 會將對應(yīng)的 action添加到第一位和最后一位,最后這個 action List 的執(zhí)行順序就確定了。

采用非依賴的形式控制任務(wù)的執(zhí)行順序蔬浙,可以通過 shouldRunAfter 和 mustRunAfter 兩個方法來實現(xiàn)猪落,這個限制在腳本中添加,通過 gradle 命令執(zhí)行 task 時起作用

taskB.shouldRunAfter(taskA) 表示 taskB 應(yīng)該在 taskA 執(zhí)行之后執(zhí)行畴博,這里是應(yīng)該而不是必須笨忌,所以又可能執(zhí)行順序不會按預(yù)設(shè)的執(zhí)行

taskB.mustRunAfter(taskA) 表示 taskB 必須在 taskA 執(zhí)行之后執(zhí)行

Task 中有個 enable 屬性,用于啟動和禁用任務(wù)俱病,默認(rèn)時 true官疲,表示啟用,設(shè)置為 false 則禁止該任務(wù)執(zhí)行亮隙,輸出會提示該任務(wù)被跳過途凫,調(diào)用 taskName.enable = false 即可

5. 任務(wù)的 onlyIf 斷言

斷言就是一個條件表達(dá)式。Task 有一個 onlyIf 方法溢吻,他接受一個閉包作為參數(shù)维费,如果該閉包返回 true 則該任務(wù)執(zhí)行,否則跳過促王,可以用來判斷控制打包等任務(wù)犀盟。

命令行中 -P 的意思時為 Project 指定 K-V 格式的屬性鍵值對,使用格式為 -PK-V

task example {
    println "執(zhí)行"
}

example.onlyIf {
    def execute = false
    if(project.hasProperty("build_apps"){
        Object buildApps = project.property("build_apps")
        if("all".equals(buildApps) {
            execute = true
        }
    }
    execute
}

// 命令

// 會執(zhí)行
gradle -Pbuild_apps=all example 

// 不會執(zhí)行
gradle example 

6. 任務(wù)規(guī)則

TaskContainer 繼承了 NamedDomainObjectCopllection, 是一個具有唯一不變名字的域?qū)ο蟮募嫌牵锩嫠械脑囟加幸粋€唯一不變的名字且蓬,改名字時 String 類型,我們可以通過名字獲取該元素题翰,NamedDomainObjectCopllection 的規(guī)則就是當(dāng)想獲取的名字的元素不存在時恶阴,對應(yīng)在 TaskContainer 中就是想獲取的任務(wù)不存在時,調(diào)用我們添加的規(guī)則來處理這種異常情況豹障。

通過調(diào)用 addRule 來添加我們自定義的規(guī)則冯事,addRule 有兩個重載方法,第一個是直接添加一個 Rule血公,第二個是通過閉包配置成一個 Rule 在添加

// 通過閉包的形式昵仅,對 tasks 這個 Task 添加任務(wù)規(guī)則
tasks.addRule("對該規(guī)則的一個描述,這里是處理任務(wù)找不到時的處理") { String taskName ->
    task(taskName) << {
        println("該 $taskName 任務(wù)不存在")
    }
}

Gradle 插件

Gradle 本身提供一些基本的概念和整體核心的框架累魔,其他用于描述真實使用場景邏輯的都以插件擴(kuò)展的方式實現(xiàn)

把插件應(yīng)用到項目中摔笤,插件會擴(kuò)展項目的功能,幫助我們在項目構(gòu)建的過程中做很多事情垦写,我們只需要按照它約定的方式吕世,使用它提供的任務(wù)、方法或者擴(kuò)展買就可以對我們的項目進(jìn)行構(gòu)建梯投。

  1. 添加任務(wù)命辖,這些任務(wù)能幫我們做一下比如測試况毅,編譯,打包等的功能
  2. 可以添加依賴配置到我們的項目中尔艇,通過插件配置我們項目在構(gòu)建過程中需要的依賴尔许,比如一些第三方庫等依賴。
  3. 可以向項目中現(xiàn)有的對象類型添加新的擴(kuò)展屬性/方法等终娃,我們可以通過她們幫助我們配置優(yōu)化構(gòu)建味廊,比如 android{} 這個配置可就是 AndroidGradle 插件為 Project 對象添加的一個擴(kuò)展
  4. 可以對項目進(jìn)行一些約定,比如應(yīng)用 Java 插件后棠耕,約定 src/main/java 目錄是我們的源代碼存放位置毡们,等等

插件的應(yīng)用

  1. 應(yīng)用二進(jìn)制插件,二進(jìn)制插件就是實現(xiàn)了 org.gradle.api.Plugin 接口的插件昧辽,可以有 plugin id
// 下面三種應(yīng)用方式效果相同
apply plugin:'java' // 通過唯一的 plugin id
apply plugin:org.gradle.api.plugins.JavaPlugin // 通過插件類型
apply plugin:JavaPlugin // org.gradle.api.plugins 是默認(rèn)導(dǎo)入的
  1. 應(yīng)用腳本插件衙熔,apply from:'version.gradle' ,應(yīng)用腳本插件就是把這個腳本加載進(jìn)來,使用 from 關(guān)鍵字搅荞,后面可以時本地的腳本文件也可以時一個網(wǎng)絡(luò)文件红氯,如果是網(wǎng)絡(luò)文件的話要使用 HTTP URL

apply 的使用方式有三種,我們上面使用到的時第一種咕痛,接受一個 Map 類型參數(shù)的方式

常用的方式還有接受閉包的方式

apply {
    plugin:'java'
}

還用一種 Action 的方式痢甘,這個知道即可,需要時再查文檔

  1. 應(yīng)用第三方發(fā)布的插件茉贡,使用第三方發(fā)布的插件我們必須要在 buildscript{} 里配置其 classpath 才能使用塞栅,buildscript 是 Project 的一個方法,參數(shù)類型是一個閉包
// 配置 AndroidGradle 插件
buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath 'com.android.tools.build.gradle:1.5.0'
    }
}

buildscript{} 塊是在構(gòu)建項目之前腔丧,為了項目進(jìn)行前期準(zhǔn)備和初始化相關(guān)配置依賴的地方放椰,配置好 buildscript 后就可以應(yīng)用插件了,如果沒有進(jìn)行 buildscript 配置,則會提示找不到這個插件

buildscript{} 通過在 repositories 中配置倉庫愉粤,通過 dependencies 中配置插件的 classpath

apply plugin: 'com.android.applicaition'
  1. 使用 plugin DSL 應(yīng)用插件,更簡潔一下砾医,并且使用這種配置方式,如果插件已經(jīng)托管到了 https://plugins.gradle.org/ 網(wǎng)站上我們就不需要在 buildscript 中配置 classpath
plugins {
    id 'java'
}

自定義插件

自定義插件必須實現(xiàn) Plugin 接口衣厘,這個接口有一個 apply() 方法如蚜,該方法在插件被應(yīng)用的時候執(zhí)行,所以我們可以實現(xiàn)這個方法影暴,做一些想做的時错邦,比如為 Project 創(chuàng)建新的 Task 等

Java 插件

源碼合集

SoureSet - 源代碼集合 - 源集,是 Java 插件用來描述和管理源代碼及其資源的一個抽象概念型宙,是一個 Java 源代碼文件和資源文件的集合撬呢。通過源集,我們可以方便的訪問源代碼目錄早歇,設(shè)置源集的屬性倾芝,更改源集的 Java 目錄或者自由目錄等。

main 和 test 是 Java 插件為我們內(nèi)置的兩個源代碼集合箭跳,我們也可以通過 SourceSets 添加自己的源集晨另,也可以通過 SourceSets 更改 main 和 test 這兩個內(nèi)置源集的代碼路徑

源集有很多的屬性,通過這些屬性我們可以對源集進(jìn)行配置谱姓,這些屬性包括 name借尿,java,java.srcDirs屉来,resources.srcDirs 等

新創(chuàng)建的源集必須配合多渠道使用路翻,創(chuàng)建的源集的名字與匹配的渠道的名字應(yīng)該是一樣的,這樣在編譯打包時就會自動匹配茄靠,否則創(chuàng)建的源集是無法使用的

// 創(chuàng)建新的源集
sourceSets {
    vip{
        java {
            srcDir 'src/java'
        }
        
        resources {
            srcDir 'src/resources'
        }
    }
}

// 新的渠道
productFlavors {
    // vip 渠道
    vip{} 
}

配置第三方的依賴

要使用第三方依賴茂契,必須告訴 gradle 如何找到這些依賴,就需要在 Gradle 中配置倉庫的依賴慨绳,這樣 Gradle 就知道在哪兒搜尋我們依賴掉冶,Gradle 中通過 repositories{} 塊來配置倉庫

repositories {
   maveCentral() 
}

有了倉庫配置以后,就可以在 dependencies{} 塊中添加依賴脐雪,指定 group name version

dependencies {
    //  標(biāo)準(zhǔn)寫法
    compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.0.1'
    
    // 簡寫方式厌小,group,name战秋,version 中間通過冒號分割
    compile 'com.squareup.okhttp3:okhttp:3.0.1'
    
    // 項目依賴,依賴后這個項目的 Java 類就會為你所用
    compile project(':example')
    
    // 文件依賴璧亚,例如 Jar 包依賴,如下所示脂信,依賴了兩個 Jar 包
    compile files('libs/example01.jar', 'libs/example02.jar')
    
    // Jar 包統(tǒng)一依賴,指定文件夾下的指定擴(kuò)展名文件都會依賴
    compile fileTree(dir: 'libs',include: '*.jar')
}

依賴的方式

名稱 意義
compile 編譯時依賴
runtime 運行時依賴
testCompile 編譯測試用例時依賴
testRuntime 僅僅在測試用例運行時依賴
archives 該項目發(fā)布構(gòu)件(JAR 包等)依賴
default 默認(rèn)依賴配置

Gradle 3.0 添加的新的依賴方式癣蟋,添加了隔代隔離的概念,即 A 依賴 B狰闪,B implementation lib梢薪,此時編譯期 A 是不能直接訪問 lib 中的類的,運行期可以尝哆。隔離依賴只對 Java 代碼生效秉撇,對 resource 無效

名稱 意義 隔代隔離效果
implementation 編譯期隔代依賴不可見,運行期間可見 “隔代”編譯期間隔離
api 與 compile 一樣秋泄,編譯期/運行期 都可見
compileOnly 依賴項僅編譯器對模塊可見琐馆,并且編譯和運行期對模塊的消費者不可用
runtimeOnly 依賴項僅在運行時對模塊及其消費者可用 編譯期間隔離

為不同源集指定不同的依賴 sourceSet + 依賴指令

例如:vipCompile 'com.squareup.okhttp:okhttp:2.5.0'

多項目構(gòu)建

Gradle 提供了基于根項目對其所有子項目通用配置的方法,Gradle 的根項目可以理解為是一個所有子項目的容器恒序,subprojects 方法可以對其所有子項目進(jìn)行配置差牛,如果相對包括根項目在內(nèi)的所有項目進(jìn)行統(tǒng)一配置,可以使用 apllprojects 用法同 subprojects

subprojects 和 allprojects 都是 Project 中的方法裕坊,接受一個閉包,其中通過 Project 中的方法 repositories 和 dependencies 配置依賴倉庫的地址和依賴的項目厉碟。

這里需要注意 buildscript 中和 Project 中的 repositories 和 dependencies 方法是不同的

subprojects {
    // 為所有子項目添加代碼倉庫
    repositories {
        mavenCentral()
    }
    
    // 為所有子項目添加插件的 classpath
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
    
    // 為所有子項目添加 java 插件
    applu plugin:'java'
}

發(fā)布構(gòu)件

我們的庫項目開發(fā)完畢后就可以配置構(gòu)件然后上傳構(gòu)件到本地目錄/maven 庫等里面,上傳就是發(fā)布屠缭。

// 配置
task publishJar(type: Jar)
version '1.0.0'

// artifacts 通過一個 Task 生成 jar 包
artifacts {
    archives publishJar
}

// uploadArchives 是一個 upload task 用于上傳我們發(fā)布的構(gòu)件到本地/maven庫等地方
uploadArchieves {
    repositores {
        ...
    }
}

生產(chǎn) idea 和 Eclipse 配置

在一個已經(jīng)初始化 wrapper 的項目中箍鼓,通過 apply plugin:'idea' 就可以生產(chǎn) IDEA 的工程配置文件,可以直接導(dǎo)入到 IDEA 中使用

Android Gradle 插件

Android Gradle 插件屬于第三方插件呵曹,所以需要在 buildscript 中配置 classpath款咖,這部分配置可以寫到根工程的 build.gradle 文件中,這樣所有的子工程就都可以使用了

AndroidGradle 插件繼承 Java 插件奄喂,具有所有 Java 插件的特性

Android 插件默認(rèn)提供了 androidTest铐殃,main,test 三種源集和三個渠道

分類

  1. App 插件跨新,id: com.android.application
  2. Library 插件富腊,id: com.android.libraay
  3. Test 插件,id: com.android.test

android{} 塊是 Android 插件提供的一個擴(kuò)展方法域帐,參數(shù)類型是一個閉包蟹肘,在 Android 插件的構(gòu)建任務(wù)執(zhí)行時會調(diào)用這個方法,android{} 塊中可以讓我們自定義自己的 Android 工程俯树。

android{} 擴(kuò)展中的配置

  1. compileSdkVersion 編譯 Android 工程的 SDK 版本帘腹,原型是 android{} 中提功能的一個擴(kuò)展方法
  2. buildSdkVersion 使用的 Android 構(gòu)建工具的版本
  3. defaultConfig 是默認(rèn)的配置,是一個 ProductFlavor许饿,ProductPlavor 允許我們根據(jù)不同的情況同時生成多個不同的 APK阳欲,不過不針對自定義的 ProductPlavor 單獨配置,會為這個 ProductPlavor 使用默認(rèn)的 defaultConfig 的配置陋率,有關(guān) ProductPlavor 中的配置下面再說
  4. buildTypes 是一個 NamedDomainObjectContainer球化,是一個域?qū)ο螅?sourceSet 類似瓦糟,buildType 里有 release 和 debug 等筒愚,我們可以新增任意多個需要構(gòu)件的類型,Gradle 會幫我們自動創(chuàng)建一個 BuildType菩浙,名字就是我們定義的名字巢掺,BuildType 中的屬性也可以設(shè)置,具體的配置下面再說
  5. productFlavors 是 AndroidGradle 提供的一個方法劲蜻,用來添加不同的渠道陆淀,接受域?qū)ο箢愋偷?ProductFlavor 閉包作為參數(shù),可以通過 productFlavors{} 閉包添加渠道先嬉,每一個都是一個 ProductFlavor 類型的渠道轧苫,名字就是渠道名。

defaultConfig 屬性

  1. applicationId 指定生成的 App 的包名疫蔓,默認(rèn)為 null(構(gòu)件時會在 Application 中的 package 獲取)
  2. minSdkVersion 含懊,可以接受 int 和 String 兩種身冬,會統(tǒng)一處理
  3. targetSdkVersion
  4. versionCode
  5. versionName
  6. testApplicationId 配置測試 App 包名,有默認(rèn)值岔乔,applicationId + ".test"
  7. signingConfig 配置默認(rèn)簽名信息酥筝,是 ProductPlavor 中的一個 SigningConfig 屬性,下面會詳細(xì)介紹
  8. proguardFile 混淆使用的 ProGuard 配置文件
  9. proguardFiles 可以同事接受多個 ProGuard 配置文件
  10. ...

配置簽名信息

Android Gradle 提供了 signingConfigsf 配置塊便于我們生成多個簽名配置信息,signingConfigs 是 Android 的一個方法,它接受一個域?qū)ο笞鳛槠鋮?shù)重罪。前面我們講過,其類型是 NamedDomainObjcctContainer,這樣我們在 signingConfigs{} 塊中定義的都是一個 SigningConfig ,一個 SigningConfig 就是一個簽名配置,其可配置的元素如下:

storeFile 簽名證書文件
storePassword 簽名證書文件的密碼
storeType 簽名證書的類型
keyAlias 簽名證書中密鑰別名
keyPassword 簽名證書中該密鑰的密碼

signingConfigs {
    debug {
        storeFile file('./keystore/debug.keystore')
        storePassword "debug"
        keyAlias "debug"
        keyPassword "debug"
    }
    release {
        storeFile file('./keystore/release.keystore')
        storePassword "release"
        keyAlias "release"
        keyPassword "release"
        v2SigningEnabled false
    }
}

// 使用一
defaultConfig {
    // 在 signingConfigs 中選擇需要的簽名信息
    signingConfig signingConfigs.release
}

// 使用二
buildTypes {
    release {
        // 對構(gòu)建類型設(shè)置簽名信息
        signingConfig signingConfigs.release
    }
}

隱藏簽名文件

為了防止簽名文件的泄漏樱哼,可以在服務(wù)器端存儲簽名文件及其密鑰哀九,并在 signingConfigs 中通過在服務(wù)端存儲的位置獲取剿配,如果本地需要調(diào)試,就在獲取不到服務(wù)端的密鑰時使用本地 debug 的密鑰阅束,保證了簽名文件的安全性

構(gòu)建的應(yīng)用類型 BuildType

Android Gradle 幫我們內(nèi)置了 relase 和 debug 兩個構(gòu)建類型呼胚,差別在于能否在設(shè)備上調(diào)試和簽名不同,其代碼和資源是一樣的

buildTypes {
    release {
    
    }
    debug {
    
    }

}

如果需要新增構(gòu)建類型在 buildTypes{} 中繼續(xù)添加元素即可息裸,buildTypes 也是 android 中的一個方法蝇更,接受一個域?qū)ο螅砑拥拿恳粋€都是 BuildType 類型

構(gòu)建類型中的配置

  1. applicationSuffix 用于配置默認(rèn) applicationId 的后綴
  2. debugable 用于配置是否生成一個可供調(diào)試的 Apk 呼盆,值可以是 true 或者 false
  3. jniDebugable 用于配置是否生成一個可供 Jni 調(diào)試的 Apk 年扩,值可以是 true 或者 false
  4. minifyEnable 是否啟用 Proguard 混淆
  5. miltiDexEnable 是否啟用自動拆分多個 Dex
  6. proguardFile 配置混淆文件
  7. profuardFiles 配置多個混淆文件
  8. shrinkResources 是否自動清理未使用的資源文件,默認(rèn) false
  9. signingConfig 簽名配置
  10. zipalignEnable 是否啟用 zipaline 優(yōu)化访圃,是一個優(yōu)化整理 apk 的工具厨幻,可以提高系統(tǒng)和應(yīng)用的運行效率,更快讀寫 apk 中的資源腿时,降低內(nèi)存使用等况脆,默認(rèn)開啟

每一個 BuildType 都會生成一個 SourceSet ,默認(rèn)位置 src// 這樣我們就可以針對不同 BuildType 單獨指定期 Java 源代碼批糟,res 資源等格了,構(gòu)建時,AndroidGradle 會使用它們代替 main 中的相關(guān)文件

每一個 BuildType 都會生成一個 assemble 任務(wù)徽鼎,執(zhí)行相應(yīng)的 assemble 任務(wù)就可以生成對應(yīng) BuildType 的 APK

批量修改生成的 apk 名字

applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            def outputName = new String(outputFile.name);
            if (outputFile != null && outputName.endsWith('.apk')) {
                def fileName = outputFile.name.replace('.apk', "-${versionName}_.apk");
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
}

動態(tài)配置 AndroidManifest 文件

在構(gòu)建的過程中動態(tài)修改 AndroidManifest 中的內(nèi)容盛末,Android Gradle 提供了 manifestPlaceholder、Manifest 占位符來替換 AndroidManifest 中的內(nèi)容

ManifestPlaceholder 是一個 Map 類型否淤,所以可以同時定義多個占位符满败,ManifestPlaceholder 是 BaseConfigImpl 中的一個屬性,ProductFlavor 繼承了 BaseConfigImpl叹括,所以可以在 BaseConfigImpl 中訪問算墨,在 BuildType 中也直接訪問 BaseconfigImpl 中的 ManifestPlaceholder

// BuildType 中定義
buildTypes {
    debug {
        // 替換原來的 ManifestPlaceholder
        manifestPlaceholders = ["UMENG_CHANNEL","google"]
        
        // 添加新的元素
        manifestPlaceholders.put("UMENG_CHANNEL","google")
    }
}

在 gradle 配置文件中定義了 manifestPlaceholder 后,在構(gòu)建時汁雷,它會把 AndroidManifest 文件中所有的占位符變量為 manifestPlaceholder 中定義的占位符替換為 manifestPlaceholder 中對應(yīng)的 value 值

// AndroidManifest 文件
<meta-data android:value="${UMENG_CHANNEL}" android:name=""UMENG_CHANNEL" />

如果在每一個 Flavor 中都需要配置相同占位符的不同 value净嘀,可以通過 productFlavors.all 方法遍歷所有的 ProductFlavor 完成批量修改,name 為 ProductFlavor 的名字

productFlavors.all { flavor ->
    manifestPlaceholders.put("UMENG_CHANNEL",name)
}

BuildConfig 文件

BuildConfig 文件時 Gradle 編譯后生成的报咳,自動生成的包含包名、是否 Debug挖藏、構(gòu)建類型暑刃、Flavor、版本號膜眠、版本名稱的常量岩臣,開發(fā)中我們可以很方便的訪問

Android Gradle 提供了 buildConfigField(String type,String name,String value) 方法讓我們添加自己的常量到 BuildConfig 文件中,type 是要生成的字段類型宵膨,name 是要生成常量的名字架谎,value 是生成字段的常量值,在 Gradle 腳本中就可以添加

注意 value 中的值辟躏,是單引號中的部分谷扣,是什么就寫什么,要原封不動放入單引號捎琐,如果是 String 会涎,雙引號不能省略

// 比如在不同 productFlavor 中配置同一常量的不同值,這樣在編譯不同的 ProductFlavor 時就會生成不同的值
productFlavors {
    google {
        buildConfigField 'String','WEB_URL','"http://google.com"'
    }
    
    baidu {
       buildConfigField 'String','WEB_URL','"http://baidu.com"' 
    }
}

動態(tài)添加自定義的資源

開發(fā)中遇到的資源包括圖片、動畫瑞凑、字符串等末秃,這些資源我們可以在 res 文件中定義,除了這種方式籽御,針對 res/values 類型练慕,里面有 arrays、ids篱蝇、attrs贺待、colors、dimens零截、strings 等類型麸塞,Gradle 提供了 resValue(String type,String name,String vlue) 方法在 ProductPlavor 和 BuildType 中可以定義這些資源的方法,這樣我們就能根據(jù)不同渠道不同構(gòu)建類型來自定義特有資源,也可以替換 res/valuse 文件夾中已經(jīng)定義的資源,可以定義 id涧衙、bool哪工、dimen、integer弧哎、color 等類型

// 以 productFlavors 為例雁比,這樣在構(gòu)建時不同渠道生成的 App 名字就會不同
productFlavors {
    google {
        resValue 'String','app_name','GoogleApp'
    }
    
    baidu {
       resValue 'String','app_name','BaiduApp'
    }
}

Java 編譯選項

Android Gralde 提供了一個 compileOptions 方法,接受一個 CompileOptions 類型的閉包為參數(shù)撤嫩,來對構(gòu)建過程中 Java 編譯選項進(jìn)行配置

compileOptions {
    // 配置源文件編碼為 UTF-8
    encoding = 'utf-8'
    
    // 配置 Java 源代碼的編譯版本
    sourceCompatibility JavaVersion.VERSION_1_8
    // 配置生成的字節(jié)碼的版本
    targetCompatibility JavaVersion.VERSION_1_8
}

adb 操作選項配置

Gradle 對 Android 手機(jī)的操作偎捎,最終還是調(diào)用了 adb 命令,Android Gralde 只不過是對 adb 命令的調(diào)用做了一層封裝,可以通過 android 提供的 adbOptions 方法配置 Gradle 調(diào)用 adb 時的一些配置茴她,該方法接受一個 AdbOptions 類型的閉包寻拂,AdbOptions 可以對 adb 操作選項添加配置

android {
    adbOptions {
        // 配置操作超時時間,單位毫秒
        timeOutInMs = 5* 1000_0 
        
        // adb install 命令的選項配置
        installOptions '-r','-s'
    }
}

DEX 選項配置

Android 中 Java 代碼被編譯成 class 字節(jié)碼丈牢,生成 apk 時又通過 dx 指令優(yōu)化成 Android 虛擬機(jī)可執(zhí)行的 DEX 文件祭钉,AndroidGradle 插件會調(diào)用 SDK 中的 dx 命令,dx 命令最終調(diào)用了 Java 編寫的 dx.jar 庫己沛,是 Java 編寫的慌核,所以當(dāng)調(diào)用 dx.jar 庫是如果內(nèi)存不足就會出現(xiàn)異常,默認(rèn)給 dx 分屏的內(nèi)存是一個 G8申尼,也就是 1024M

android 提供了 dexOptions 方法用來添加對 dx 操作的配置垮卓,接受一個 DexOptions 類型的閉包,配置由 DexOptions 提供

android {
    dexOptions {
        // 配置是否啟用 dx 增量模式晶姊,默認(rèn) false扒接,增量模式速度塊伪货,但是有很多限制们衙,不建議開啟
        increment true
        
        // 配置執(zhí)行 dx 命令是為其分配的最大堆內(nèi)存 
        javaMaxHeapSize '4g'
        
        // 配置是否開啟 jumbo 模式,代碼方法是超過 65535 需要強(qiáng)制開啟才能構(gòu)建唱歌
        jumboMode true
        
        // 配置是否預(yù)執(zhí)行 dex Libraries 工程碱呼,開啟后會提高增量構(gòu)建速度蒙挑,不過會影響 clean 構(gòu)建的速度,默認(rèn) true
        preDexLibraries true 
        
        // 配置 Andriod Gradle 運行 dx 命令時使用的線程數(shù)量
        threadCount 8
    }
}

自動清理未使用的資源

Android Gradle 提供了構(gòu)建時自動清理未使用的資源的方法愚臀,會將項目中的和第三方依賴中的都清理

Resource Shrinking -- 資源清理 -- shrinkResource true
Code Shrinking -- ProGuard 混淆代碼清理 monifyEnabled true

shrinkResource 是在 BuildType 中配置的忆蚀,默認(rèn)不啟用

Android Gradle 提供了 keep 方法來配置我們不希望被清理的資源

新建一個 xml 文件,res/raw/keep.xml 然后通過 tools:keep 屬性來配置姑裂,可以通過都好分割配置資源列表馋袜,支持通配符 *

keep 中還有一個 tools:shrinkMode 屬性,用來配置自動清理資源的模式舶斧,默認(rèn)是 safe 是安全的欣鳖,這種情況下代碼中通過 getResource() 方法引用的資源也會被保留,如果設(shè)置成 shrink 模式茴厉,這些資源也會被清理

keep 文件在構(gòu)建時也會自動被清理泽台,如果想保留也要添加到 keep 屬性中

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="safe"
    tools:keep="@layout/getui_notification,@drawable/h_good,@drawable/h_ok"/>

除了 shrinkResource 外,Android Gradle 還提供了 resConfig 配置矾缓,屬于 ProductFlavor 的一個方法怀酷,用來配置哪些類型的資源會被打包到 apk 中,比如只打包中文資源嗜闻,只打包 hdpi 中的圖片等蜕依,它的參數(shù)就是我們 Android 開發(fā)時用到的資源限定符,這樣在打包時可以清理大量無用資源

android {
    defaultConfig {
        // 接受一個
        resConfig 'zh'
        // 接受多個
        resConfigs 'zh','hdpi'
    }
}

Android Gradle 多項目構(gòu)建

Android 的項目有三種,庫項目样眠、應(yīng)用項目竞滓、測試項目,對應(yīng)三種項目也有三種插件吹缔,com.android.library,com.android.applistion,com.android.test商佑,開發(fā)特定的項目需要依賴特定的插件

Android 多項目設(shè)置

通過根項目中的 setting.gradle 進(jìn)行配置

庫項目的引用和配置

引用庫項目也是通過在項目的 build.gradle 中的 dependencies 中指定,庫項目引用時厢塘,庫項目會生成一個 aar 包茶没,如果時 Java 項目就會生成一個 jar 包提供給其他項目引用

dependencies {
    compile project(':lib')
}

庫項目默認(rèn)發(fā)布出來的是一個 aar 包,默認(rèn)發(fā)布的是 release 版本晚碾,可以在庫項目中通過 android 提供的 defaultPublishConfig 配置來修改,這個方式可以配置不同的版本抓半,只要名字合法即可,例如 "flavor1Debug" 格嘁,這樣就可以針對不同的 Flavor 和 BuildType 來配置

android {
    defaultPublishConfig "debug"
}

android {
    defaultPublishConfig "flavor1Debug"
}

如果想同時發(fā)布多個版本的 aar 供不同項目引用笛求,可以配置同時發(fā)布多個 aar 包,首先子項目進(jìn)行配置,然后在其他項目引用該項目的生活糕簿,通過 path 指定子項目探入,通過 configration 指定 Flavar 和 BuildType,BuildType 又可以指向匹配 SourceSets 懂诗,這樣就可以同時發(fā)布多個版本的 aar 蜂嗽,且這些 aar 的內(nèi)容可以不一樣

// 子項目配置
android {
    publishNonDefault true
}

// 其他項目依賴子項目時的配置
dependencies {
    flavor1Compile project(path: ':lib1', configration: 'flavor1Release')
    flavor1Compile project(path: ':lib1', configration: 'flavor2Release')
}

庫項目的單獨發(fā)布

對于一些公共組件庫、工具庫等殃恒,我們可以將它們單獨發(fā)布植旧,供其他項目引用,我們也可以將它們發(fā)布到 Maven 或者 jcenter 中

庫項目發(fā)布到 Maven 倉庫

首先要搭建 Maven 私服离唐,使用 Nexus Repository Manager 并部署啟動

Maven 庫部署以后病附,就可以把項目發(fā)不到中心庫了,想要通過 Maven 發(fā)布亥鬓,首先要在 build.gradle 中應(yīng)用 Maven 插件完沪,這個插件提供給 Product 一些擴(kuò)展

應(yīng)用 Maven 插件以后,需要配置 Maven 構(gòu)建的三要素贮竟,group:artifact:version

版本號可以指定成快照形式丽焊,比如 1.0.0-SNAPSHOT ,這時候會發(fā)不到 snapshot 庫里咕别,每次發(fā)布版本號不會變化技健,只會在版本號后按順序 + 1,例:1.0.0-1惰拱,1.0.0-2

引用的時候版本號寫成 1.0.0-SNAPSHOT 即可雌贱,Maven 會幫我們下載最新也就是序號最大的快照版本啊送,在聯(lián)調(diào)測試的時候可以使用這種方式,當(dāng)調(diào)試結(jié)束欣孤,就可以發(fā)布正式 release 版本

apply plugin: 'com.android.library'
apply plugin: 'maven'

version '1.0.0'
group 'org.snow.widget'

配置好 version 和 group 后馋没,進(jìn)行發(fā)布配置,比如發(fā)不到哪個 Maven 庫降传,用戶名密碼是什么篷朵,發(fā)布的格式是什么,它的 artifact 即項目 name 是什么等

uploadArchives {
    repositories {
        mavenDeployer {
            
            // 快照發(fā)布, URL 用戶名 密碼
            snapshotRepository(url: mavenServer + mavenSnapshots) {
                authentication(userName: repoUsername, password: repoPassword)
            }
            
            // 正式發(fā)布
            repository(url: mavenServer + mavenReleases) {
                authentication(userName: repoUsername, password: repoPassword)
            }
            
            // 項目 name
            pom.artifactId = "pull-view"
            
            // 發(fā)布格式
            pom.packaging = 'aar'
        }
    }
}

引用發(fā)布的項目庫

引用自己發(fā)布的庫項目婆排,首先需要在 Gradle 中添加對應(yīng)的 classapth声旺,然后在 dependencies 中添加引用

repositories {
    maven {
        // 項目庫的 url
        url ".../groups/relases"
    }
}

dependencies {
    compile 'org.snow.widget:pull-view:1.0.0'
}

為了使用快照版本,需要單獨配置 classpath段只,因為快照庫和 relase 庫地址不同,引用也要單獨添加

repositories {
    maven {
        // 項目庫的 url
        url ".../groups/snapshots"
    }
}

dependencies {
    compile 'org.snow.widget:pull-view:1.0.0-SNAOSHOTS'
}

可以通過 group 類型的配置腮猖,同時將 relase 和 snapshots 類型的庫地址添加到 classpath, 引用還是區(qū)分 relase 和快照即可

repositories {
    maven {
        url ".../groups/public"
    }
}

Andriod Gradle 多渠道構(gòu)建

在 Android Gradle 中,定義了一個叫 Build Variant 的概念赞枕,即 構(gòu)建變體澈缺,即不同的構(gòu)件,也就是不同的 apk炕婶,一個 Build Variant 由一個 BuildType 和一個 ProductFlavor 構(gòu)成姐赡,比如 release 和 debug,goole 和 baidu 古话,就可以構(gòu)成四個變體雏吭。

Android Gradle 通過了 productFlavors 擴(kuò)展來添加不同渠道锁施,它接受域?qū)ο箢愋偷?ProductFlavor 閉包作為參數(shù)陪踩,可以添加很多渠道,每一個 ProductFlavor 都是一個渠道悉抵,在 NamedDomainObjectContainer 中的名字就是渠道肩狂,比如 google ,baidu 等

渠道配置以后姥饰,Android Gradle 就會生成很多 Task 傻谁,基本都是基于 BuildType + ProductPlavor 的方法生成的,如 assembleGoogle assembleGoogleRelase assembleGoogleDebug 等,assembleRelease 運行后就會生成所有渠道的 release 包列粪,assembleGoogle 運行后就會生成 google 渠道的 release 和 debug 包审磁。出了 assemble 還有 install 系列的等

每個 ProductPlavor 還可以有自己的 SourceSet 和自己的 dependencies 依賴,這樣岂座,每個渠道都可以定義自己的資源态蒂,源代碼以及第三方類庫

多渠道構(gòu)建定制

通過配置 ProductFlavor 達(dá)到靈活控制每一個渠道

  1. applicationId 配置該渠道包名
  2. consumerProguardFiles ,對 Android 庫項目生效费什,配置混淆文件列表钾恢,會被打包到 aar,當(dāng)應(yīng)用項目開啟混淆時,會使用 aar 包里的混淆文件對 aar 包里的代碼進(jìn)行混淆瘩蚪,這樣應(yīng)用項目就不用對該 aar 進(jìn)行混淆配置了
  3. manifestPlaceholders 上面已經(jīng)介紹了
  4. multiDexEnabled 開啟多個 dex 的配置
  5. proguardFiles 混淆文件
  6. signingConfig 簽名文件
  7. versionCode 版本號
  8. versionName 版本名
  9. userJack 是否啟用 Jack 和 Jill 編譯器泉懦,Google 開發(fā)的編譯器,速度快性能高疹瘦,但不支持注解崩哩、 JDK8 的特性,處于試驗階段
  10. testApplicationId 單元測試的 apk 的包名
  11. testFunctionalTest 是否為功能測試言沐,testHandleprofiling 是否啟用分析功能
  12. testInstrumentationRunner 測試使用...
  13. testInstrumentationrunnerArguments 測試使用 ...
  14. dimension ...

dimension

開發(fā)中如果遇到這樣的情況琢锋,比如:項目區(qū)分收費和免費版并且代碼不同,還區(qū)分不同的機(jī)器架構(gòu) x86 和 arm 并且代碼不同呢灶,甚至還有其他區(qū)分緯度的區(qū)分吴超,這時候,我們在配置 ProductFlavor 時鸯乃,就會出現(xiàn) 收費版的配置在 x86 和 arm 中都各寫一份鲸阻,如果緯度更多,那么需要寫的地方就更多缨睡,修改和增加時就更容易出錯

為了解決這個問題鸟悴,Android Gradle 為我們提供了 dimension 來解決這個問題

dimension 是 ProductFlavor 的一個屬性,接受一個字符串奖年,為該 ProductFlavor 的緯度细诸,多個 ProductFlavor 可以同屬一個緯度,這個緯度必須先定義出來才能使用陋守,在 android{} 中震贵,productFlavors{} 之前通過 flavorDimensions 聲明緯度,定義后就可以在 ProductFlavor 中使用水评,在一個 ProductFlavor 中添加了 dimension 屬性后猩系,就相當(dāng)于把這個 ProductFlovar 添加進(jìn)了這個緯度的分組中。

只要定義了一個緯度中燥,并且這個緯度分組中有了內(nèi)容寇甸,其他所有非當(dāng)前緯度的 ProductFlavor 都會與這個緯度中每一個 ProductFlavor 進(jìn)行組合。就比如 收費和付費是一個緯度 'version'疗涉,我們在 paid 和 free 兩個 ProductFlavor 指定了 demision 為 version拿霉,padi 和 free 中還可增加其他配置。這樣我們再添加其他 ProductFlavor 后咱扣,比如添加了 arm 绽淘,Android Gradle 會以 BuildType+arm+version 的格式生成一系列 Task,這里就會生成 ArmPaidDebug ArmPaidRelease ArmFreeDebug ArmFreeRelease

這樣偏窝,我們只用緯度去分組收恢,去配置武学,剩下的 AndroidGradle 來組合,實現(xiàn)了共性配置伦意,維護(hù)起來也很方便

新版本的 AndroidGradle 插件火窒,強(qiáng)制要求,必須為每一個 ProductFlavor 添加 dimension驮肉,否則編譯會報錯

android {
    flavorDimensions "abi","version"
    
    productFlavors {
        free {
            dimension 'version'
        }
        
        paid {
            dimension 'version'
        }
        
        x86 {
            dimension 'api'
        }
        
        arm {
            dimension 'api'
        }
    }
}

以上配置中熏矿,定義了兩個分組,加上默認(rèn)的兩種 BuildType 后兩兩結(jié)合以后就會生成八種組合方式

lint 檢查

Android 為我們提供了針對代碼离钝、資源的優(yōu)化工具 lint票编,它可以幫助我們檢查出哪些資源沒有被使用,使用了哪些新的 API卵渴,哪些資源沒有被國際化等慧域。并且會生成一份報告,告訴我們哪些需要優(yōu)化浪读。

Lint 是一個命令行工具昔榴,是 Android Tool 目錄下的一個工具,可以在命令行中直接執(zhí)行碘橘。Android Gradle 中也對 Lint 做了很好的支持互订,提供了 lintOptions{} 閉包來配置 lint,達(dá)到做檢查的目的

lintOptions 是 Android 對象的一個方法痘拆,接受一個類型為 LintOptions 的閉包仰禽,用于配置 Lint,Lint 提供了非常多的配置,以后可以根據(jù)項目配置想要的 Lint 檢查規(guī)則

執(zhí)行 gradle lint 命令即可運行 lint 檢查纺蛆,默認(rèn)生成的報告在 outputs/lint-results.html 中

android {
    lintOptions {
        // 遇到 lint 檢查錯誤會終止構(gòu)建吐葵,一般設(shè)置為 false
        abortOnError true 
        // 將警告當(dāng)作錯誤來處理
        warningAsErros true
        // 檢查新 API
        check 'NewApi'
    }
}

多渠道構(gòu)建的效率

通過在 APK 包中的 METE-INF 文件夾下新增文件,文件名為有意義的渠道名字犹撒,通過 Python 腳本復(fù)制原始 APK 包折联,解壓后在 METE-INF 中添加文件,然后再打包

在使用時识颊,Android 程序的 APP 中通過對 apk 進(jìn)行讀取,就可以讀到 METE-INF 中文件的名字奕坟,這樣就得到了渠道標(biāo)示祥款,取到標(biāo)示后可以存入 SP 中,這樣就不用每次都讀取 APK

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月杉,一起剝皮案震驚了整個濱河市刃跛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苛萎,老刑警劉巖桨昙,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件检号,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛙酪,警方通過查閱死者的電腦和手機(jī)齐苛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桂塞,“玉大人凹蜂,你說我怎么就攤上這事「笪#” “怎么了玛痊?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狂打。 經(jīng)常有香客問我擂煞,道長,這世上最難降的妖魔是什么趴乡? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任颈娜,我火速辦了婚禮,結(jié)果婚禮上浙宜,老公的妹妹穿的比我還像新娘官辽。我一直安慰自己,他們只是感情好粟瞬,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布同仆。 她就那樣靜靜地躺著,像睡著了一般裙品。 火紅的嫁衣襯著肌膚如雪俗批。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天市怎,我揣著相機(jī)與錄音岁忘,去河邊找鬼。 笑死区匠,一個胖子當(dāng)著我的面吹牛干像,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驰弄,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼麻汰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戚篙?” 一聲冷哼從身側(cè)響起五鲫,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岔擂,沒想到半個月后位喂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪耘,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年塑崖,在試婚紗的時候發(fā)現(xiàn)自己被綠了七冲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡弃舒,死狀恐怖癞埠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聋呢,我是刑警寧澤苗踪,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站削锰,受9級特大地震影響通铲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜器贩,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一颅夺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛹稍,春花似錦吧黄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奉芦,卻和暖如春赵抢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背声功。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工烦却, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人先巴。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓其爵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筹裕。 傳聞我的和親對象是個殘疾皇子醋闭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容