下面講講在Android Gradle實戰中遇到的一些問題和經驗,感覺還是蠻多干貨的。
這個東西基本上已經爛大街了,gradle的項目一般都會使用Product Flavor,看完美團的文章,你應該就懂了。
美團Android自動化之旅—適配渠道包
很多App有內測版和正式版,怎么讓他們同時安裝在一個手機上?同時安裝在一個手機上,要求packageName不同的,用productFlavors可以解決,但可能不夠優雅,alpha版本還要來個debug和release版本豈不是很蛋疼?可以用buildTypes來解決,淘寶架構師朱鴻的文章有比較詳細的講解,但有些內容可能有些過時了,需要更改腳本。
項目依賴的遠程包如果有更新,會有提醒或者自動更新嗎? 不會的,需要你手動設置changing標記為true,這樣gradle會每24小時檢查更新,通過更改resolutionStrategy可以修改檢查周期。
之前上傳aar同一版本到maven倉庫,但依賴卻沒有更新,該怎么辦呢?可以直接刪除本地緩存,緩存在~/.gradle/caches目錄下,刪除緩存后,下次運行就會自動重新下載遠程依賴了。
在工程的build.gradle中添加如下腳本:
在build.gradle同目錄下添加gradle.properties文件,配置如下:
gradle.properties的屬性會被build.gradle讀取用來上傳aar,后執行./gradlew :Zxing:uploadArchives即可。
更多配置,可參考建立企業內部maven服務器并使用Android Studio發布公共項目。
項目構建過程中那么多任務,有些test相關的任務可能根本不需要,可以直接關掉,在build.gradle中加入如下腳本:
tasks會獲取當前project中所有的task,enabled屬性控制任務開關,whenTaskAdded后面的閉包會在gradle配置階段完成。
任務可以取消了,但還不盡興啊,想加入任務怎么搞?前面講了dependsOn的方法,那就拿過來用啊,但是原有任務的依賴關系你又不是很清楚,甚至任務名稱都不知道,怎么搞?
比如我想在執行dex打包之前,加入一個hello任務,可以這么寫:
afterEvaluate是什么鳥?你可以理解為在配置階段要結束,項目評估完會走到這一步。
variant呢?variant = productFlavors+ buildTypes,所以dex打包的任務可能就是dexCommonDebug。
你怎么知道dex任務的具體名稱?Android Studio中的Gradle Console在執行gradle任務的時候會有輸出,可以仔細觀察一下。
hello任務定義的這么復雜干啥?我直接就叫hello不行嗎?不行,each就是遍歷variants,如果每個都叫hello,多個variant都一樣,豈不是傻傻分不清楚,加上variant的name做后綴,才有任務的區分。
關鍵來了,dx.taskDependencies.getDependencies(dx)會獲取dx任務的所有依賴,讓hello任務依賴dx任務的所有依賴,再讓dx任務依賴hello任務,這樣就可以加入某個任務到構建流程了,是不是感覺非常靈活。
我突然想到,用doFirst的方式加入一個action到dx任務中,應該也可以達到上面效果。
gradle加速可以看看這位朋友寫的加速Android Studio/Gradle構建,我就不多嘴了。并行編譯,常駐內存,還有離線模式這些思路對gradle的加速感覺還是比較有限。
想要更快,可以嘗試下Facebook出品的Buck,可以看一下Vine團隊適配Buck的技術文章,我們的架構師也有適配Buck,加速效果在10倍左右,但有兩個缺點,不支持Windows系統,不支持遠程依賴。
你想知道每個執行任務的運行時間嗎?你想知道每個執行任務都是干嘛的嗎?把下面這段腳本加入build.gradle中即可:
上面是對每個任務計時的一個例子,想要了解每個任務的作用,你可以修改上面的腳本,打印出每個任務的inputs和outputs。比如assembleDebug那么多依賴任務,每個都是干什么的,一會compile,一會generate,有什么區別?看到每個task的輸入輸出,就可以大體看出它的作用。如果對assemble的每個任務監聽,你會發現改一行代碼build的時間主要花費在了dex上,buck牛逼的地方就是對這個地方進行了優化,大大減少了增量編譯運行的時間。
Android項目中,根工程默認的build.gradle應該是這樣的:
一會一個jcenter()這是在干什么?buildscript方法的作用是配置腳本的依賴,而我們平常用的compile是配置project的依賴。repositories的意思就是需要包的時候到哥這里來找,然后你以為com.android.tools.build:gradle:1.2.3會從jcenter那里下載了是吧,圖樣圖森破,不信加入下面這段腳本看看輸出:
結果是這樣的:
file:/Applications/Android%20Studio.app/Contents/gradle/m2repository/ https://jcenter.bintray.com/
我靠,倉庫然直接在Android Studio應用內部,所以說你去掉buildscript的jcenter()完全沒有關系啊,下面還有更爽的,我們知道有依賴傳遞,上面classpath 中的gradle依賴gradle-core,gradle-core依賴lint,lint依賴lint-checks,lint-checks后依賴到了asm,并且這個根目錄中的依賴配置會傳到所有工程的配置文件,所以如果你要引用asm相關的類,不用設置classpath,直接import就可以了。你怎么知道前面的依賴關系的?看上面m2repository目錄中對應的pom文件就可以了。
為什么講到ASM呢?ASM又是個比較刁的東西,可以直接用來操縱Java字節碼,達到動態更改class文件的效果。可以用ASM面向切面編程,達到解耦效果。Android DEX自動拆包及動態加載簡介中提到的class依賴分析和R常量替換的腳本都可以用ASM來搞。
腳本寫多了,都擠在一個build.gradle里也不好,人長大了總要自己出去住,那可以把部分腳本抽出去嗎?當然可以,新建一個other.gradle把腳本抽離,然后在build.gradle中添加apply from 'other.gradle'即可,抽出去以后你會發現本來可以直接import的asm包找不到了,怎么回事?根工程中配置的buildscript會傳遞到所有工程,但只會傳到build.gradle腳本中,其他腳本可不管,所以你要在other.gradle中重新配置buildscript,并且other.gradle中的repositories不再包含m2repository目錄,自己配置jcenter()又會導致依賴重新下載到~/.gradle/caches目錄。如果不想額外下載,也可以在other.gradle中這么搞:
ApplicationId versus PackageName提到,gradle中的applicationid用來區分應用,manifest中packageName用來指定R文件包名,并且各個productFlavor 的manifest中的packageName應該一致。applicationid只是gradle腳本中的定義,其實后生成的apk中的manifest文件的packageName還是會被applicationid替換掉。
那獲取R文件的包名怎么搞?要獲取AndroidManifest中package屬性,并且這個manifest要是起始的文件,因為終文件中的package屬性會被applicationid沖掉,由于各個manifest中的package屬性一樣,并且非主manifest可以沒有package屬性,所以只有獲取主manifest的package屬性才是準確的。
無用的資源就不要打包進APK了。
Resource Shrinking
之前在創業公司,用 Travis做持續繼承,遇到一個讓我很糾結的問題。在Travis上執行構建腳本如下:
后生成的APK在運行的時候報錯,提示找不到某個.so文件,解壓發現APK中果然缺少某個庫工程的.so文件,但在本地運行的時候卻是沒有問題,糾結了好久,后來研究發現Android Studio中執行Clean Project的時候,會執行generateSources的任務,把它加入構建腳本后才打包正確。近發現,這原來是個Bug,并且已經在android gradle1.3被修復了。
匆匆忙忙間,寫了很多東西。讀完此文,希望你能感受到構建神器的魅力,感受到它的靈活強大,當然也希望能讓你使用Gradle更加得心應手。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。