Gradle模塊化代碼精煉:讓你的gradle代碼控制在100行之內

本篇來自 wustor 的投稿,分享了一個Gradle模塊化配置的實現技巧,但願你們會喜歡!android

wustor 的博客地址:git

https://www.jianshu.com/u/94dc45995a85github

 

概述api

 

咱們知道,Android Studio是利用gradle進行構建的,咱們常常接觸到的gradle腳本是build.gradle。build.gradle有兩個,一個在project下,一個是在app目錄下。隨着項目的迭代,咱們會在app目錄下的gradle中添加不少依賴,project下的gradle卻不會發生很大的變化,因此會致使app下面的gradle文件愈來愈大,有時候查找對應的方法以及task很是不方便,尤爲是在集成了tinker熱修復以後,app下面的gradle已經達到了將近1000多行。最近恰好有時間認真研究了一下gradle,確切地說是groovy,而後經過腳本依賴實現了gradle解耦,成功的把app目錄下的gradle代碼控制在100行之內。服務器

 

正文app

 

常見配置

一般的作法是在project目錄下新建一個config.gradle文件,以下:ide

ext { 
    android = [ 
            compileSdkVersion: 25, 
            buildToolsVersion: "25.0.3 
    ] 
    supportLibrary = "25.4.0" 
    tinkerVerison = "1.9.1" 
    dependencies = [ 
  "multidex"       : "com.android.support:multidex:1.0.1", 
  "okhttp3"       : "com.squareup.okhttp3:okhttp:3.9.0" 
}

而後在project下的build.gradle文件中引用模塊化

apply from: "config.gradle"

再接着在app目錄下的build.gradle中獲取並使用工具

apply plugin: 'com.android.application' 
apply from: "package.gradle" 
def cfg = rootProject.ext.android 
def librarys = rootProject.ext.dependencies 

android { 
    compileSdkVersion cfg.compileSdkVersion 
    buildToolsVersion cfg.buildToolsVersion 
    dexOptions { 
        jumboMode = true 
    } 
//此處省略一萬行代碼 } 
dependencies { 
    compile fileTree(include: ['*.jar'], dir: 'libs') 
    compile librarys["multidex"] 
    compile librarys["okhttp3"] 
//此處省略一萬行代碼 }

這種方式可以將咱們的gradle統一進行管理,雖然並不能減小app的目錄下的build.gradle的代碼量以前,可是以爲夠用了,自己對基於groovy的gradle不是很熟,雖然隨着項目迭代,app目錄下的build.gradle代碼量愈來愈大,尤爲是當項目集成了tinker以後,然後集成了packer-ng-plugin打包,以及加入了一些自定義的Task以後,代碼會顯得很是臃腫,有時候改一個東西,須要找好久,因爲對groovy不是很熟,在網上也看過一些文章,基本上都是在介紹gradle的基本知識以及依賴統一管理,加上在gradle裏面寫代碼沒有提示,一度讓我覺得這可能就是build.gradle的最終版了,直到最近項目剛上線,稍微有點空閒,而後決定完全簡化一下gradle代碼。測試

url優化

按照config的配置,通常來說,咱們開發的時候至少會有兩個服務器地址,正式跟測試,爲了統一管理,先是寫死在buildType裏面的

buildTypes { 
        release { 
            minifyEnabled true 
            shrinkResources true 
            buildConfigField "String", "AlphaUrl", "\"releaseUrl1\"" 
//          buildConfigField "String", "AlphaUrl", "\"releaseUrl2\"" 
        } 
        debug { 
            minifyEnabled true 
              buildConfigField "String", "AlphaUrl", "\"debugUrl1\"" 
//            buildConfigField "String", "AlphaUrl", "\"debugUrl2/\"" 

        } 
    }

 

當咱們的服務器地址只有一個正式的或者一個測試的時候,這樣寫徹底OK的,可是若是是有多個地址的話,你每切換一次,都須要從新同步一下,還有就是,多人協做開發的時候,每次從Git服務器上面更新代碼,只要更新到app目錄下的gradle,都是須要從新同步的,參照config的配置,咱們若是是引用的話就每次只須要讀取引用的那個url,切換隻須要修改config中的代碼就能夠了,下面是進行的優化:

在config中添加代碼

url = [ 
  "debug"  : "debugUrl1", 
//"debug"  : "debugUrl2", //"debug"  : "debugUrl3", 
   "release": "releaseUrl1", 
// "release": "releaseUrl2" 
    ]

從新引用

//獲取 
def url = rootProject.ext.url 
//使用 //debug 
buildConfigField "String", "AlphaUrl", "\"${url["debug"]}\"" 
//release 
buildConfigField "String", "AlphaUrl", "\"${url["release"]}\""

依賴優化

先看一看以前的代碼

dependencies { 
 compile fileTree(include: ['*.jar'], dir: 'libs') 
 compile librarys["multidex"] 
 compile librarys["supportAppcompat"] 
 //此處省略一萬行代碼

實際上library就是一個Map,其實這句代碼轉化成Java就是

compile fileTree(include: ['*.jar'], dir: 'libs') 
HashMap<String,String> hashMap=new HashMap<>(); 
compile hashMap.get("multidex") 
compile hashMap.get("supportAppcompat")

其實可能你也知道了,實際上咱們徹底能夠寫一個循環來簡化這些代碼,就跟HashMap的遍歷同樣

dependencies { 
    compile fileTree(include: ['*.jar'], dir: 'libs') 
    librarys.each { k, v -> compile v } 
}

模塊化配置

在咱們進行常規的gradle配置中,咱們並無在project下的build目錄添加不少代碼,只是新建了一個config.gradle文件,而後再project目錄下添加了一行依賴,因此就可以調用config中的代碼,一樣的,咱們也能夠把tinker,packer-ng-plugin,以及自定義task的配置文件用一個gradle文件進行配置,而後在app的目錄下進行引用,實際上就是利用了gradle的插件依賴。

tinker配置

首先在app的目錄下新建一個tinker.grale配置文件,爲何是tinker目錄下,由於tinker的運行須要依賴'com.android.application'這個插件,因此必須放在這個目錄下,而後複製粘貼tinker的配置代碼,注意不要忘記修改tinker的id,我是在project目錄下統一進行配置的tinker,因此只須要調用config中的代碼

//此處省略一萬行代碼 
def getTinkerIdValue() { 
    return rootProject.ext.tinker.id 

} 
//此處省略一萬行代碼

而後在app下面的build.gradle中進行引用,注意看一下這行代碼的位置,不要放在最開始,由於在依賴tinker.gradle文件的時候,否則你是打不了tinker的補丁的,tinker須要讀取application的一些信息,放在buildTypes 以後,當時我也是調試了很久。

apply plugin: 'com.android.application' 
def cfg = rootProject.ext.android 
def librarys = rootProject.ext.Dependencies 
def tinker = rootProject.ext.tinker 
def url = rootProject.ext.url 

buildTypes { 
  //此處省略一萬行代碼 } 
apply from: "tinker.gradle"

到此,tinker就可使用了,下面繼續配置walle的gradle

packer配置

新建package.gradle文件

apply plugin: 'packer' packer { 
    archiveNameFormat = '${buildType}-v${versionName}-${channel}' 
    archiveOutput = new File(project.rootProject.buildDir, "apks") 
    channelList = ['xiaomi','meizu'] 
} 
}

在app下的build.gradle中添加依賴

apply plugin: 'com.android.application' 
apply from: "package.gradle"

task配置

雖然Android Studio的application自帶了不少task,可是並不能知足咱們有些需求,好比我須要用Python將測試包上傳至fir,就須要自定義task,因此我也打算把這部分給分離出來,新建upload.gradle

ext { 
   //此處省略一萬行代碼 
    startUpload = this.&startUpload 
} 
//上傳至fir 
def startUpload() { 
 //此處省略一萬行代碼 
}

在project中引用

apply from: "upload.gradle"

在app的build.gradle中能夠直接調用

task toFir << { 
    startUpload() 
}

toFir <<,實際上是gradle的語法,若是不加<<的話,每次編譯的時候都會執行這個task,加了<<,只有執行這個task的時候纔會執行裏面的代碼

0?wx_fmt=png

只有81行,這樣一來,調試就很輕鬆了,哪一個腳本除了問題,就直接去調試相應的腳本就行了,不用在本身的gradle裏面改來改去。

 

運行測試

 

tinker 測試

運行命令gradlew tinkerpatchDebug或者打開右側的可視化工具欄點擊tinker下的tinkerpatchDebug,運行測試,運行結果:

Result: final signed patch result: G:\Note\ChuangMei\app\build\outputs\tinkerPatch\debug\patch_signed.apk, size=2110 Result: final signed with 7zip patch result: G:\Note\ChuangMei\app\build\outputs\tinkerPatch\debug\patch_signed_7zip.apk, size=2439 Warning: patch_signed_7zip.apk is bigger than patch_signed.apk 329 byte, you should choose patch_signed.apk at these time! 
Tinker patch done, total time cost: 8.806000s 
Tinker patch done, you can go to file to find the output G:\Note\ChuangMei\app\build\outputs/tinkerPatch/debug 
-----------------------Tinker patch end------------------------- 
BUILD SUCCESSFUL in 3m 16s

packer測試

運行命令gradlew clean apkDebug運行測試,運行結果:

> Task :app:apkDebug 
============================================================ 
PackerNg - https://github.com/mcxiaoke/packer-ng-plugin ============================================================ 
Variant: debug 
Input: G:\Note\ChuangMei\app\build\outputs\apk\app-debug.apk 
Output: G:\Note\ChuangMei\build\apks 
Channels: [xiaomi meizu] 
Generating: debug-v1.6.3-xiaomi.apk 
Generating: debug-v1.6.3-meizu.apk 
Outputs: G:\Note\ChuangMei\build\apks

task測試

運行命令 gradlew task toFir,運行結果:

開始上傳至fir 
http://api.fir.im/apps success_apk:{"is_completed":true} 
success_icon:{"is_completed":true} 
上傳結束 with value 0

幾點說明

  • gradle所放位置

爲何有的是放在project目錄下,有的是放在app的目錄下,由於gradle引用默認的是當前路徑,這樣放我引用的時候就不須要去配置所引用的gradle路徑,固然若是你緣由放置在同一個目錄下面也是OK的,只須要在apply的時候加上引用的path便可。

  • 引用位置

爲何有的引用是放在頭部,有的引用須要放置在中間,這個取決於引用的插件是否須要讀取application的配置信息,若是是tinker,必須放置在中間,由於它生成patch包須要獲取不少application信息,若是是packer打包的話,則不須要,這個須要格外留意一下,否則會有不少莫名其妙的錯誤。

  • 方法調用

在同一個gradle腳本里面,方法調用是很簡單的,可是當咱們有多個gradle腳本的時候,如何相互調用彼此的方法呢,其實我以前想優化的時候,也是卡在這裏,由於屬性調用很簡單,gradle提供了ext,因此咱們能夠很容易的獲取其餘gradle的屬性,若是咱們如今有兩個gradle,一個是first.gradle,一個是second.gradle,我想在second.gradle裏面調用first.gradle中的方法,應該怎麼作呢?

只須要在first.gradle中進行以下配置

ext{  
    test= this.&test 
 }  
    def  test(){   
       println("我被調用了") 
}

而後在second.gradle中進行配置

//直接調用 
   startUpload() 
//經過task調用 task toFir << { 
    startUpload() 
}

 

結語

 

package

須要配置簽名,在keystore.properties中進行配置,能夠進行多渠道打包

tinker

爲了保證對gradle進行模塊化分離不影響項目的構建,因此我都是用本身的真實項目進行構建的,由於簡單的Demo很難模擬真實項目中的構建環境,因此demo中我只提供了gradle的配置文件,沒有進行tinker的配置,可是已經對tinker進行了模塊化分離。

upload

上傳至fir的代碼是利用Python腳本進行上傳的,須要配置Python環境以及安裝requests庫。代碼 地址:

https://github.com/wustor/GradleModule

若是有什麼問題歡迎留言哈,謝謝!

相關文章
相關標籤/搜索