之前寫過了一篇Gradle(一)你画,那是根據(jù)別人寫的文章總結(jié)寫的,當(dāng)時(shí)其實(shí)還是很多不懂桃漾,包括我現(xiàn)在對gradle的理解也其實(shí)還是似懂非懂坏匪,但是還是要寫,每次寫完之后包括再重新多看幾次呈队,都會有新的感悟剥槐。
然后我想說的是關(guān)于Gradle的文章,其實(shí)網(wǎng)上寫得好的并不是很多宪摧,就那一兩篇寫得比較好粒竖,然后其他都是千篇一律,還是比較建議就是看官方的文檔几于,但是官方的文檔其實(shí)有時(shí)候也讀不懂他說的到底是個(gè)什么意思蕊苗,所以我這里比較推薦兩本書:《Android Gradle權(quán)威指南》和《Gradle of Android 中文版》
其實(shí)這兩本書也并不是把所有的東西都講得很清楚,但是讀之后會至少有個(gè)概念沿彭。當(dāng)然如果以前沒接觸腳本的話估計(jì)看一遍還是看不懂朽砰,包括我其實(shí)看了很多一樣的內(nèi)容,但是現(xiàn)在也還有很多不知道。
一.Gradle
相關(guān)的內(nèi)容比較多瞧柔,我就不一點(diǎn)一點(diǎn)開始講漆弄,就貼個(gè)別人寫好的文章出來,但我個(gè)人還是比較建議去看書
https://www.cnblogs.com/ut2016-progam/p/5871430.html
1.gradle
gradle是什么呢造锅,我個(gè)人簡單的理解就是他是一個(gè)構(gòu)建工具撼唾,他有個(gè)特性是約定優(yōu)于配置,他是基于Groovy的領(lǐng)域?qū)S谜Z言(DSL)哥蔚,其實(shí)這個(gè)DSL我也不懂具體是什么意思倒谷。
關(guān)于Groovy語言,其實(shí)我感覺和js還是比較相似的糙箍,是不是所有的腳本語言都一個(gè)尿性渤愁,建議還是要看一下,其他的都還好深夯,主要需要注意一下它的那個(gè)閉包的概念抖格,也就是Closure類型。
2.生命周期
比較重要的一點(diǎn)是gradle的構(gòu)建過程是有生命周期這一概念的塌西,分為三個(gè)階段:
(1)初始化階段:創(chuàng)建 Project 對象他挎,如果有多個(gè)build.gradle,也會創(chuàng)建多個(gè)project
(2)配置階段:在這個(gè)階段捡需,會執(zhí)行所有的編譯腳本办桨,同時(shí)還會創(chuàng)建project的所有的task,為后一個(gè)階段做準(zhǔn)備
(3)執(zhí)行階段:在這個(gè)階段站辉,gradle 會根據(jù)傳入的參數(shù)決定如何執(zhí)行這些task,真正action的執(zhí)行代碼就在這里
其實(shí)可以先把每一個(gè).gradle文件當(dāng)成一個(gè)Project 呢撞,初始化階段就是把.gradle變成Project對象,第二個(gè)階段就是創(chuàng)建Project對象里面的任務(wù)饰剥,我個(gè)人理解就是走.gradle文件里面的代碼(但是估計(jì)這個(gè)解釋不是很對殊霞,我對第二個(gè)階段不是很清楚),第三個(gè)階段就是執(zhí)行這些任務(wù)汰蓉。
那么什么時(shí)候開始走這個(gè)生命周期呢绷蹲,不知道,我沒有找到有哪篇文章說這個(gè)的顾孽,但是從我打印的結(jié)果來看祝钢,我認(rèn)為是只要執(zhí)行某些任務(wù)都會走這3個(gè)生命周期,比如說Rebuild或者運(yùn)行程序若厚,都會執(zhí)行生命周期拦英,比如這段Rebuild時(shí)的打印內(nèi)容
第一個(gè)部分就是執(zhí)行的任務(wù),其實(shí)在AS的工具欄點(diǎn)按鈕测秸,相當(dāng)于在命令行輸入要執(zhí)行的命令疤估,第二個(gè)部分是我在gradle文件里面寫的一些打印的操作灾常,我覺得這個(gè)就是配置階段,因?yàn)槟阈薷牧薌radle文件中的內(nèi)容铃拇,它需要重新進(jìn)行配置吧钞瀑,第三個(gè)部分前面有冒號的就是執(zhí)行任務(wù),也就是執(zhí)行階段慷荔。
所以對生命周期我是這樣認(rèn)為的仔戈,初始化階段只要setting.gradle文件沒變,它就只進(jìn)行初始化一次拧廊,如果這個(gè)文件變了,就需要重新進(jìn)行初始化晋修。然后是配置階段就是執(zhí)行項(xiàng)目Gradle和模塊gradle里面的代碼吧碾,每次執(zhí)行任務(wù)都會重新執(zhí)行配置階段吧。然后執(zhí)行階段就是打印中的最后一部分墓卦,很明顯能看出倦春,也是每次操作都會執(zhí)行這個(gè)生命周期。
其實(shí)我在開發(fā)gradle的時(shí)候遇見了一個(gè)BUG落剪,點(diǎn)擊sync now之后睁本,會先跑一遍日記,然后刷新再跑一遍忠怖,在第一遍打印的結(jié)尾會打印這個(gè)
要不是我手快都截不到圖呢堰,可以看出這時(shí)候是配置階段,而這時(shí)候還沒生成variant凡泣,所以我當(dāng)時(shí)獲取variant出現(xiàn)了為空的BUG枉疼。我感覺這個(gè)階段也是多渠道資源進(jìn)行合并的階段。
3.插件
在android中Gradle還有一個(gè)比較重要的概念就是插件鞋拟。在根目錄下的build.gradle文件骂维,也就是整個(gè)工程的build.gradle文件
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
里面某塊表示什么意思,網(wǎng)上也有很多說明贺纲,因?yàn)槲也唤?jīng)常改動這個(gè)文件航闺,所以這里我就別在不懂裝懂了。
然后就是每個(gè)module的gradle文件猴誊,這個(gè)是比較常用的潦刃,一般都會這樣寫:
apply plugin: 'com.android.application'
android {
.....
}
這個(gè)就是插件,gradle里面引用了com.android.application這個(gè)插件稠肘。這個(gè)是一個(gè)閉包福铅,如果之前看過groovy就知道,可以看到Gradle里面基本所有的地方都用到閉包的寫法项阴。android這個(gè)關(guān)鍵字傳入的閉包里面的defaultConfig啊buildTypes啊這些都是屬于這個(gè)插件的內(nèi)容滑黔,但也不一定笆包,因?yàn)檫@個(gè)插件是是擴(kuò)展了java插件,舉個(gè)栗子略荡,里面的sourceSets就是java插件的庵佣。所以要想知道里邊具體是怎么配置的,可以搜索這個(gè)插件的內(nèi)容汛兜。常用到的就大概這些
defaultConfig是基本的配置信息
buildTypes和productFlavors我在多渠道打包的文章有詳細(xì)講
sourceSets就是源集巴粪,等下會詳細(xì)去講一點(diǎn)
其他的詳細(xì)內(nèi)容也不太想多說什么,因?yàn)橐黄恼驴隙ㄊ菍懖煌晁信渲美锞唧w該詳細(xì)怎么做的粥谬,我這里暫時(shí)先打算說SourceSets
二.SourceSets
SourceSets被稱作原集合肛根,一般可以用它來指定資源的路徑。
比如我這個(gè)地方漏策,我想把兩個(gè)文件夾中的代碼和原文件夾中的進(jìn)行合并派哲,我可以這樣寫
sourceSets{
main{
java {
srcDirs "src/mtone"
srcDirs "src/mttwo"
}
}
}
可以看到這個(gè)set,學(xué)過java的都知道這是集合掺喻,其實(shí)gradle里面很多地方可以用java代碼去寫
我們打印看看這個(gè)set是什么
sourceSets{
main{
java {
srcDirs "src/mtone"
srcDirs "src/mttwo"
sourceSets.all{set ->
println "${set.name}的文件是 ${set.java.srcDirs}"
}
}
}
}
}
可以看到打印的結(jié)果
set的name其實(shí)就是基本和渠道差不多芭届,然后java.srcDirs就是這個(gè)set的java文件的路徑,這是一個(gè)我比較想說的點(diǎn)感耙,前面我說了指定資源路徑褂乍,java其實(shí)是其中一個(gè),我們現(xiàn)在可以來看看大概有什么資源文件
所以你可以寫一個(gè)文件夾即硼,然后在文件夾寫一些資源文件逃片,當(dāng)你某個(gè)渠道想要把這些資源文件加進(jìn)去的時(shí)候,你可以這樣寫
渠道名{
main{
res{
srcDirs "你自己資源文件夾的路徑"
}
}
}
這樣就可以在編譯時(shí)把你自己的資源文件和原項(xiàng)目的進(jìn)行合并只酥,當(dāng)然會有一定的合并規(guī)則题诵,這個(gè)我不多說,你可以百度“資源合并規(guī)則”层皱。還有一點(diǎn)是我覺得并不是在編譯時(shí)才合并的性锭,而是在配置的生命周期時(shí)執(zhí)行sourceSets里面的操作,對所有的資源進(jìn)行合并叫胖。
然后我再說一個(gè)場景我這三個(gè)包都要進(jìn)行合并的草冈,但是出現(xiàn)了這樣的一個(gè)問題,我想很多人都碰過這個(gè)問題瓮增,沒錯(cuò)怎棱,就是重復(fù)類的問題,這時(shí)我這里的main也就是原本的目錄下有個(gè)MyTestOne.java類绷跑,但是我的mtone目錄下也有一個(gè)MyTestOne.java類拳恋,這時(shí)你就沒法合并了,這時(shí)你編譯的話會報(bào)一個(gè)錯(cuò)誤砸捏,報(bào)重復(fù)類的錯(cuò)誤谬运。
想想就知道不可能有兩個(gè)同名類共存隙赁,因?yàn)槿绻阏{(diào)用的話系統(tǒng)怎么知道你是調(diào)用了哪個(gè),所以我們的做法是必須在合并的時(shí)候去去掉其中一個(gè)MyTestOne.java梆暖。其實(shí)這里也是說我們可以在gradle中動態(tài)的去選著給項(xiàng)目指定使用哪個(gè)MyTestOne.java伞访。
有人用過gradle的話肯定知道可以使用 exclude來排除,但是如果我們直接這樣寫的話
exclude 'com/example/kylin/fristtest/MyTestOne.java'
就會把兩個(gè)MyTestOne.java都給排除轰驳,因?yàn)樗麄兊陌且粯拥暮裰溃恍拍憧梢栽囋嚒K晕覀兊淖龇ň褪且玫剿诤喜⒅笆菍儆谀膫€(gè)文件夾的级解,這時(shí)候就需要用到set的java路徑冒黑,因?yàn)檫@個(gè)是屬于java的部分
sourceSets{
main{
java {
srcDirs "src/mtone"
srcDirs "src/mttwo"
sourceSets.all{set ->
println "${set.name}的文件是 ${set.java.srcDirs}"
if(name == "main"){
Set myt = java.srcDirs;
println myt
}
}
}
}
}
我們直接這樣打印看看
可以看出我的結(jié)果這里打印出了三個(gè),這樣我們就可以拿到自己想保留的路徑下的MyTestOne.java勤哗,我們可以這樣寫
sourceSets{
main{
java {
srcDirs "src/mtone"
srcDirs "src/mttwo"
sourceSets.all{set ->
println "${set.name}的文件是 ${set.java.srcDirs}"
if(name == "main"){
Set myt = java.srcDirs;
println myt
myt.each {
if(it.name != "mtone"){
exclude 'com/example/kylin/fristtest/MyTestOne.java'
}
}
}
}
}
}
}
拿到set之后可以遍歷薛闪,然后用name獲取具體的路徑,這樣子就可以排除指定路徑下的MyTestOne.java俺陋。
但是這里不能獲取到多渠道的路徑,比如我這樣寫
我多加了ali和baidu的渠道昙篙,這里卻獲取不了腊状,為什么?
因?yàn)檫@里看到外層
沒錯(cuò)已經(jīng)是指定到main的苔可,如果我們想在ali的渠道下弄缴挖,我們需要再寫個(gè)ali的閉包
我們這樣寫,把引入的資源放到ali中焚辅,可以看到結(jié)果
可以看出這里有個(gè)很奇怪的問題映屋,沒錯(cuò),就是多渠道的文件同蜻,可以不用指定srcDirs棚点,他會自動合并,但是自動合并情況下你用set是無法獲取到其他渠道的文件湾蔓,set只能獲取在閉包內(nèi)srcDirs定義的文件瘫析。我覺得這個(gè)原因是因?yàn)槟闶菑纳系较聢?zhí)行時(shí)打印的,這時(shí)候還沒有合并默责,等你的gradle里面寫的代碼執(zhí)行完之后贬循,才開始進(jìn)行合并操作,所以打印的時(shí)候還沒開始進(jìn)行合并桃序。
那怎么辦杖虾,我還想獲取其它渠道的文件的話該怎么辦,有兩個(gè)辦法媒熊,第一個(gè)就是在main中用srcDirs指定渠道的文件奇适,我覺得AS默認(rèn)合并的操作也是指定srcDirs坟比,但是我沒試過這種方法,不知道是否可行滤愕。
第二種方法就是用File温算,沒錯(cuò),我之前說過gradle中可以寫java的代碼间影,那我其實(shí)也可以使用java中的File類來操作文件注竿。
比如說我想獲取到src文件夾下的所有文件,我可以這樣寫魂贬,然后順便打印看看
File file = file('src')
File[] tempList = file.listFiles()
println "所有文件夾" + tempList
可以看到確實(shí)能打印出來巩割,然后你想怎么做,按照java操作File的方式去做就行了付燥。
從這個(gè)打印還可以看到一個(gè)很有意思的地方宣谈,就是他和SourceSets內(nèi)部打印的順序是穿插的,我也不知道為什么會這樣键科。
好了闻丑,我暫時(shí)就只說這么多,還想看詳細(xì)的SourceSets的操作勋颖,可以去看官網(wǎng)的 API 嗦嗡,而如果你想做某些操作而API沒有的話,你可以考慮一下用java能不能實(shí)現(xiàn)你想要的功能饭玲。
還有就是Gradle的內(nèi)容太多了侥祭,而網(wǎng)上很多人講得都比較淺,上面對SourceSets的操作是我自己在實(shí)踐中去不斷踩坑才總結(jié)出來的茄厘。所以我只能說Gradle太難了矮冬,閉包的思想,還有原理次哈,不同的插件還有不同的操作胎署,而且你在開發(fā)時(shí)報(bào)錯(cuò)的話,查找錯(cuò)誤信息還要帶點(diǎn)推理的思路去找出錯(cuò)誤在哪窑滞,說不定你每次運(yùn)行都會產(chǎn)生不同的錯(cuò)誤硝拧。最主要是沒有找到那種比較去完整理解的教程,所以寫這個(gè)東西相關(guān)的還是挺難的葛假,首先自己就不是很懂障陶,然后還不知道要怎么去組織語言,我這也只能碰到什么寫什么了聊训。要是我把這個(gè)東西弄懂個(gè)七八十抱究,我也不專門寫文章,直接寫書得了带斑。
我想說的是鼓寺,如過我對SourceSets的操作能幫到你勋拟,那當(dāng)然是好,如果幫不到你妈候,我也沒辦法敢靡,它的內(nèi)容還是比較多的,我現(xiàn)在肯定也沒法全懂苦银,這東西也沒人教我啸胧,我去踩坑,能寫的我都寫了幔虏,有些坑我沒踩過我也不懂怎么處理纺念。
這篇是補(bǔ)上周的,寫gradle組織語言有點(diǎn)難想括,所以上周沒能出一篇