1. Gradle
Gradle是開(kāi)源的自動(dòng)化構(gòu)建工具,基于JVM剥哑,有良好的擴(kuò)展性和性能,對(duì)IDE支持,支持編寫(xiě)自定義任務(wù)占卧,在Android領(lǐng)域,用于構(gòu)建和編譯應(yīng)用的資源和源代碼溢吻,然后將它們打包成可供您測(cè)試乌庶、部署、簽署和分發(fā)的 APK左痢,另外在插件化和熱修復(fù)的領(lǐng)取中起重要的作用靡羡。
gradle構(gòu)建有三個(gè)階段
- 初始化
分析哪些module將參與構(gòu)建系洛,settings.gradle文件會(huì)被解析。 - 配置
處理module的build腳本略步,處理屬性和task的依賴(lài)關(guān)系描扯,build.gradle文件會(huì)被解析配置 - 執(zhí)行
按照配置過(guò)程處理的任務(wù)依賴(lài)關(guān)系相繼執(zhí)行task
2. 通過(guò)Gradle動(dòng)態(tài)修改Manifest文件
例如:在release包manifest中動(dòng)態(tài)添加標(biāo)簽為meta-data的渠道號(hào)channel<meta-data android:name=“channel" android:value=“1001" />
思路
- 在項(xiàng)目配置結(jié)束的task中,找到生成Release的manifest的task
- 通過(guò)該task找到輸出的manifest文件并讀取文本內(nèi)容
- 使用Groovy的Xml Api對(duì)manifest進(jìn)行修改
- 修改后重新寫(xiě)趟薄,輸出至原始目錄
完整代碼如下绽诚,代碼解釋見(jiàn)3.X
project.afterEvaluate { //3.1
android.applicationVariants.all { ApplicationVariant variant -> //3.2
String variantName = variant.name.capitalize() //3.3
def processManifestTask = project.tasks.getByName("process${variantName}Manifest") //3.4
processManifestTask.doLast { pmt -> //3.5
String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml" //3.6
def manifest = file(manifestPath).getText() //3.7
if (project.hasProperty("channel")) {
def channelNo = project.property("channel")
def xml = new XmlParser().parseText(manifest) //3.8
xml.application[0].appendNode("meta-data", ['android:name': 'channel', 'android:value': channelNo])
def serialize = XmlUtil.serialize(xml)
file(manifestPath).write(serialize)
}
}
}
}
終端中運(yùn)行: gradle clean aR -P channel=101
查看apk中的manifest文件發(fā)現(xiàn)渠道號(hào)已經(jīng)動(dòng)態(tài)的添加進(jìn)去了
3.1 project
project和build.gralde一一對(duì)應(yīng),在構(gòu)建初始化期間杭煎,Gradle Project為每個(gè)參與構(gòu)建的項(xiàng)目組裝一個(gè)對(duì)象恩够。
project.afterEvaluate(Closure closure)
參數(shù)是一個(gè)閉包,在項(xiàng)目配置后立即調(diào)用羡铲。project作為參數(shù)傳遞給閉包蜂桶。
3.2 ApplicationVariant
Android的構(gòu)建變體,可以理解為一個(gè)版本也切,例如debug扑媚,release等等,在項(xiàng)目配置結(jié)束后可以通過(guò)AppExtension.getApplicationVariants().all方法獲取贾费,或者通過(guò)DefaultGroovyMethods.each()方法獲取钦购。
由api可知,使用all可以處理后面添加進(jìn)來(lái)的構(gòu)建變體褂萧,使用each只能處理當(dāng)前的押桃,所以建議用all。
3.3 variant.name.capitalize()
通過(guò)查看源碼發(fā)現(xiàn)capitalize()是org.codehaus.groovy.runtime下StringGroovyMethods的一個(gè)方法导犹,查看Groovy Api:
將第一個(gè)字母變大寫(xiě)唱凯,便于后續(xù)字符串拼接獲取manifest,
variant.name= release
variant.name.capitalize()= Release
3.4 project.tasks.getByName(String name)
T getByName(String name) throws UnknownTaskException;
根據(jù)Api可知,通過(guò)task的名稱(chēng)獲取該task
3.5 task.doLast
task是gradle的執(zhí)行單元谎痢,gradle的構(gòu)建是通過(guò)taks的執(zhí)行來(lái)完成磕昼。
定義名為hello的task如下:
task hello {
printf "hello\n"
}
hello.doLast {
printf "doLast\n"
}
執(zhí)行g(shù)radle clean
> Configure project :app
hello
執(zhí)行g(shù)radle hello
> Configure project :app
hello
> Task :app:hello
doLast
可以看到在執(zhí)行一些配置任務(wù)時(shí)候hello任務(wù)也會(huì)一起執(zhí)行,但是hello的doLast并沒(méi)有執(zhí)行节猿,如果不需要在配置時(shí)候執(zhí)行我們的自定義的任務(wù)票从,可以使用doLast來(lái)完成。
3.6 manifestOutputDirectory
通過(guò)該task的manifest輸出路徑獲取manifest文件
processManifestTask.doLast { pmt ->
String manifestPath = "$pmt.manifestOutputDirectory/AndroidManifest.xml"
.......
代碼是個(gè)閉包滨嘱,pmt是processManifestTask的自定義簡(jiǎn)稱(chēng)峰鄙,可以不定義,那么代碼為
processManifestTask.doLast { ->
String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
.......
3.7 file(manifestPath).getText()
public static String getText(File file) throws IOException {
return IOGroovyMethods.getText(newReader(file));
}
傳入個(gè)file對(duì)象通過(guò)BufferedReader讀取從而獲取文本
3.8 操作XML
使用Groovy解析XML的最常用方法如下:
- groovy.util.XmlParser
- groovy.util.XmlSlurper
相同點(diǎn)
- 兩者都是基于SAX它們都是低內(nèi)存占用
- 兩者都可以更新/轉(zhuǎn)換XML
不同點(diǎn)
- 返回值
- XmlSlurper. parseText返回GPathResult
- XmlParser. parseText返回Node
- 使用場(chǎng)景
- 如果將現(xiàn)有文檔轉(zhuǎn)換成另一個(gè)文檔太雨,那么推薦用XmlSlurper
- 如果只有少許節(jié)點(diǎn)吟榴,也推薦用XmlSlurper,這樣不用在內(nèi)存中創(chuàng)建完整的結(jié)構(gòu)
- 如果想同時(shí)更新和閱讀囊扳,那么推薦用XmlParser
3.8.1 添加節(jié)點(diǎn)
用到的api:appendNode(XmlParser為例)
def xml = new XmlParser().parseText(manifest)
xml.application[0].appendNode("meta-data", ['android:name': 'channel', 'android:value': channelNo])
此時(shí)xml.application[0]是獲取第一個(gè)application節(jié)點(diǎn)吩翻,雖然manifest只有一個(gè)application兜看,也需要帶上[0],例如獲取application內(nèi)的第一個(gè)activity節(jié)點(diǎn)為:
xml.application.activity[0]
3.8.2 序列化并更新
將修改過(guò)的xml進(jìn)行序列化為原生manifest狭瞎,再通過(guò)文件路徑寫(xiě)入更新
def serialize = XmlUtil.serialize(xml)
file(manifestPath).write(serialize)
引用參考
groovy Xml Api解析
groovy Api
遷移到 Android Plugin for Gradle 3.0.0
Android DSL官方文檔
全面理解Gradle - 定義Task