今天要聊的就跟android有關了,但是在介紹的時候會引用到前兩篇的知識,所以沒有看過前兩篇文章的朋友,建議先看一下前面提到的概念: Gradle外掛學習筆記(一) Gradle外掛學習筆記(二)
android編譯任務
要講到android的編譯,肯定要先看看android在執行assemble都執行了什麼,這個我們可以列印一下任務(如何列印任務?這個我們後面再說,也是個groovy外掛):
app工程的preBuild任務開始執行
app工程的preDebugBuild任務開始執行
app工程的checkDebugManifest任務開始執行
app工程的preReleaseBuild任務開始執行
app工程的prepareComAndroidSupportAnimatedVectorDrawable2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportAppcompatV72600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportConstraintConstraintLayout102Library任務開始執行
app工程的prepareComAndroidSupportSupportCompat2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportCoreUi2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportCoreUtils2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportFragment2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportMediaCompat2600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportV42600Alpha1Library任務開始執行
app工程的prepareComAndroidSupportSupportVectorDrawable2600Alpha1Library任務開始執行
app工程的prepareDebugDependencies任務開始執行
app工程的compileDebugAidl任務開始執行
app工程的compileDebugRenderscript任務開始執行
app工程的generateDebugBuildConfig任務開始執行
app工程的generateDebugResValues任務開始執行
app工程的generateDebugResources任務開始執行
app工程的mergeDebugResources任務開始執行
app工程的processDebugManifest任務開始執行
app工程的processDebugResources任務開始執行
app工程的generateDebugSources任務開始執行
app工程的incrementalDebugJavaCompilationSafeguard任務開始執行
app工程的javaPreCompileDebug任務開始執行
app工程的compileDebugJavaWithJavac任務開始執行
app工程的compileDebugNdk任務開始執行
app工程的compileDebugSources任務開始執行
app工程的mergeDebugShaders任務開始執行
app工程的compileDebugShaders任務開始執行
app工程的generateDebugAssets任務開始執行
app工程的mergeDebugAssets任務開始執行
app工程的transformClassesWithDexForDebug任務開始執行
app工程的mergeDebugJniLibFolders任務開始執行
app工程的transformNativeLibsWithMergeJniLibsForDebug任務開始執行
app工程的transformNativeLibsWithStripDebugSymbolForDebug任務開始執行
app工程的processDebugJavaRes任務開始執行
app工程的transformResourcesWithMergeJavaResForDebug任務開始執行
app工程的validateSigningDebug任務開始執行
app工程的packageDebug任務開始執行
app工程的assembleDebug任務開始執行
複製程式碼
這是執行assembleDebug列印的所有任務,當然你要執行assembleRelease任務肯定是一致的。 那接下來介紹一下常用的幾個任務:
-
mergeDebugResources 任務的作用是將所有依賴的aar或library module中的資源合併到app/build/intermediates/res/merged/debug目錄裡
-
processDebugManifest任務是將所有依賴的aar或library module中AndroidManifest.xml中的節點,合併到專案的AndroidManifest.xml中
-
processDebugResources的作用是呼叫aapt生成專案和所有aar依賴的R.java,同時生成資源索引檔案,把符號表輸出到app/build/intermediates/symbols/debug/R.txt
-
compileDebugJavaWithJavac這個任務是用來把java檔案編譯成class檔案,輸出的路徑是app/build/intermediates/classes/debug 編譯的輸入目錄有
-
transformClassesWithJarMergingForDebug的作用是把compileDebugJavaWithJavac任務的輸出app/build/intermediates/classes/debug,和app/build/intermediates/exploded-aar中所有的classes.jar和libs裡的jar包作為輸入,合併起來輸出到app/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar,我們在開發中依賴第三方庫的時候有時候報duplicate entry:xxx 的錯誤,就是因為在合併的過程中在不同jar包裡發現了相同路徑的類
-
transformClassesWithDexForDebug這個任務的作用是把包含所有class檔案的jar包轉換為dex,class檔案越多轉換的越慢
使用android提供的外掛
如果想在自己的外掛中產生干預android編譯的行為,肯定要依賴android的gradle外掛。這裡要說明兩種情況:
- 如果使用buildSrc(不明白是什麼的,請檢視前兩篇文章)的方式,不需要做額外的依賴。
- 如果是新建java Library的形式需要新增依賴:
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.android.tools.build:gradle:2.3.3'
}
複製程式碼
看看原始碼
上一篇文章說到了可以通過android的額外屬性名訪問Project的android屬性,從而訪問android的編譯。
我們今天會通過原始碼看一下,android到底是如何實現的。
首先是AppPlugin.java
檔案(即外掛的主檔案),裡面有這麼一行程式碼:
project.getExtensions()
.create(
"android",
AppExtension.class,
project,
instantiator,
androidBuilder,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
extraModelInfo);
複製程式碼
可以看出AppExtension就是android擴充屬性的檔案,這裡要說明的是如果是編譯Library,使用的就是LibraryPlugin外掛,對應的擴充檔案為LibraryExtension
。好了我們還是繼續說兩個檔案對比一下不難發現,兩個檔案都是差不多的,都是繼承TestedExtension,只是屬性變數不太一樣。今天我們還是隻說AppExtension。
在這個檔案中有一個這樣的陣列ApplicationVariant:
圖有些大了,但是應該可以看明白BaseVariant中的方法比較多,我省略了一下,省略的部分主要是獲取上一節列印的Task的方法。暫時應該不會用到。
這裡有個重要的方法BaseVariantOutput
,我們常用的替換佔位符(多渠道打包等)都會用到,這裡做個簡單的介紹:
以上介紹的方法和類都很重要,想更深瞭解的朋友不妨仿照下面的demo,多列印些log看看輸出的內容都是什麼,
寫個小demo
如果上面的圖認真的看一下,就可以瞭解variant中包含了android編譯的主要功能引數,所以要下手,只能從這裡下手了。 在這裡我們先舉個簡單的小例子(在之前的demo中做個修改,不熟悉的朋友請參考前兩章):
void apply(Project project) {
project.extensions.create("deep", MyExtension)
project.afterEvaluate {
MyExtension extension = project['deep'];
String a = extension.aaa
String b = extension.bbb
println("deep:${a},${b}")
project.tasks.getByName("preDebugBuild") {
it.doFirst {
project.android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.processManifest.doLast {
def manifestFile = "${project.getProjectDir().absolutePath}/build/intermediates/manifests/full/${variant.dirName}/AndroidManifest.xml"
def updatedContent = new File(manifestFile).getText('UTF-8').replaceAll("vvvvv", "ccccc")
new File(manifestFile).write(updatedContent, 'UTF-8')
}
}
}
}
}
}
}
複製程式碼
這段程式碼的作用很明顯是修改AndroidManifest中的字串vvvvv,改成ccccc。說白了作用就是替換字串,跟替換佔位符是一個作用。看看上面有個重要的寫法:
output ->
output.processManifest.doLast {}
複製程式碼
這是什麼意思呢,根據上面我們介紹的原始碼,可以知道這個就是在processManifest Task之後再去執行,再由上面介紹到的內容processManifest是合併AndroidManifest的任務,在合併之後,馬上去修改AndroidManifest檔案,保證了佔位符的替換。 再修改一下這個檔案,我們們看看output的file是什麼:
project.tasks.getByName("preDebugBuild") {
it.doFirst {
project.android.applicationVariants.all { variant ->
variant.outputs.each { output ->
println("out put="+output.getOutputFile())
}
}
}
}
複製程式碼
看看輸出:
有時候我們可能需要修改一下apk的名字,根據上面的原始碼,我們找到了一個方法就是setOutputFile
,來我們在打打log,看一下:
project.tasks.getByName("preDebugBuild") {
it.doFirst {
project.android.applicationVariants.all { variant ->
variant.outputs.each { output ->
println("getDirName:"+output.getDirName())
output.setOutputFile(new File("${project.getProjectDir().absolutePath}/build/${output.getName()}_aaa.apk"))
println("out put=" + output.getOutputFile())
}
}
}
}
複製程式碼
看看結果:
還有生成的apk:總結
這次介紹的內容比較多,要想多掌握,可能需要多除錯幾次,多打打log才能全部掌握,喜歡的使用者可以根據我上面提到的方法,多試幾次。 接下來的文章可能會側重介紹groovy的語法,因為有使用者微信公眾號給我留言說寫的程式碼,看著有些彆扭。還有可能會介紹一些其他gradle的外掛,比如上面提到的列印任務日誌等等。 喜歡的朋友可以關注我的公眾號,第一時間看到文章: