基于Gradle的Android應(yīng)用打包實(shí)踐

0x01 基本項(xiàng)目結(jié)構(gòu)

使用Android Studio創(chuàng)建的Android項(xiàng)目會(huì)劃分成三個(gè)層級(jí):

  • project : settings.gradle定義了構(gòu)建應(yīng)用時(shí)包含了哪些模塊;build.gradle定義了適用于項(xiàng)目中所有模塊的構(gòu)建配置

  • module : 可以是一個(gè)app類型的module,對(duì)應(yīng)生成apk應(yīng)用;也可以是一個(gè)lib類型的module,對(duì)應(yīng)生成aar包. 每個(gè)module中包含的build.gradle定義了針對(duì)該module的各種構(gòu)建選項(xiàng)

  • sourceset : 每個(gè)module的源碼和資源分組為多個(gè)源集,main源集包含了所有變體共用的源碼和資源. 源集結(jié)合productFlavors和buildTypes編譯可以很好的實(shí)現(xiàn)多套源碼,資源,以及依賴庫(kù)的應(yīng)用打包

      +--- Comi/
      |
      \--- build.gradle
      \--- setting.gradle
      +--- app/
      |    |
      |    \--- build.gradle
      |    \--- build/
      |    \--- libs/
      |    \--- src/
      |          |
      |          \--- main/
      |          |      \--- java/
      |          |            \--- com.icomico.comi/
      |          |      \--- res/
      |          |            \--- drawable/
      |          |            \--- layout/
      |          |            \--- ...
      |          |      \--- AndroidManifest.xml
      |          \--- jp/
      |          \--- ...
      +--- comi_base/
      +--- ...
    

0x02 構(gòu)建流程

構(gòu)建流程涉及到了將項(xiàng)目轉(zhuǎn)化成apk包的各方面,并配合有可靈活配置的構(gòu)建選項(xiàng),基本的構(gòu)建流程包含了以下幾步:

  1. 編譯器將源碼轉(zhuǎn)換成dex文件,并將其它所有內(nèi)容轉(zhuǎn)換成已編譯資源
  2. 打包工具將dex文件和已編譯資源合并成單個(gè)apk文件,apk文件此時(shí)處于未簽名狀態(tài)
  3. 打包工具根據(jù)當(dāng)前構(gòu)建任務(wù)對(duì)應(yīng)buildTypes中選擇的signingConfigs選項(xiàng),使用相應(yīng)秘鑰對(duì)apk文件對(duì)apk進(jìn)行簽名
  4. 打包工具使用zipalign工具對(duì)apk包進(jìn)行優(yōu)化
  5. 構(gòu)建結(jié)束,生成最終apk文件
basic build process

0x03 靈活配置構(gòu)建選項(xiàng)

基于Gradle的Android插件提供了一系列可自定義配置的構(gòu)建選項(xiàng),可以方便在構(gòu)建過(guò)程中選擇多樣的構(gòu)建方式,以下列出一些常用的構(gòu)建選項(xiàng)

  • defaultConfig
    包含了用于所有變體種的一些構(gòu)建選項(xiàng),如果productFlavors中配置了同樣的構(gòu)建選項(xiàng),則會(huì)覆蓋defaultConfig中的對(duì)應(yīng)項(xiàng)

      defaultConfig {
          applicationId 'com.icomico.comi'  //應(yīng)用包名
          minSdkVersion 10
          targetSdkVersion 23
          versionCode 1000
          versionName '1.0'
          multiDexEnabled true  //支持多dex文件,用于解決方法數(shù)超過(guò)65K的限制時(shí)打包輸出問題
      }
    
  • signingConfigs
    包含了app簽名文件的各項(xiàng)配置

      signingConfigs {
          release {    
              storeFile file('xxx')
              storePassword 'xxx'
              keyAlias 'xxx'
              keyPassword 'xxx'
              v2SigningEnabled false  //是否使用7.0版本引入的APK signature scheme v2簽名方式,默認(rèn)為true
          }
          debug {
              ...
          }
      }
    
  • buildTypes
    構(gòu)建類型中可定義多個(gè)構(gòu)建類型,每個(gè)構(gòu)建類型可以選擇不同的構(gòu)建屬性,包括使用的簽名配置,優(yōu)化選項(xiàng),混淆選項(xiàng)等.必須定義至少一個(gè)構(gòu)建類型才能開始構(gòu)建應(yīng)用

      buildTypes {
          release {
              signingConfig signingConfigs.release  //使用的簽名配置
              minifyEnabled true  //是否使用proguard進(jìn)行代碼壓縮優(yōu)化
              shrinkResources true  //是否進(jìn)行資源壓縮,使用資源壓縮前需要使用代碼壓縮
              proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-fresco.pro'  //指定使用的proguard配置文件
              manifestPlaceholders = [DEBUG: false]  //替換manifest文件中標(biāo)記為${DEBUG}的文本
          }
          debug {
              ...
          }
      }
    
  • productFlavors
    谷歌翻譯為產(chǎn)品風(fēng)味的選項(xiàng),用來(lái)發(fā)布不同的應(yīng)用版本,并可以為不同版本指定包括包名,版本號(hào)等不同的構(gòu)建屬性

      productFlavors {
          develop {
              applicationId 'com.icomico.comi.dev'  //應(yīng)用包名
              manifestPlaceholders = [TEMP_CH_NAME name]}  //使用flavor名稱替換manifest中的渠道號(hào)字段,實(shí)現(xiàn)修改渠道號(hào)
          official {
              applicationId 'com.icomico.comi'
              manifestPlaceholders = [TEMP_CH_NAME: name]
          }
          ...
      }
    
  • sourceSets
    源集可指定了不同變體使用的源碼和資源文件,默認(rèn)情況下,源集與src目錄下的目錄結(jié)構(gòu)一一對(duì)應(yīng),每個(gè)src下的目錄為一個(gè)源集.sourceSets配置項(xiàng)可在變體基礎(chǔ)上指定變體使用的源集文件位置,這樣可以在定義了大量變體,同時(shí)使用的源碼和資源文件沒有區(qū)別的情況下,避免在src下生成同等數(shù)量的目錄

      sourceSets { 
          develop {
              jni.srcDir 'src/main/jni'
              java.srcDir 'src/dev/java'
              res.srcDir 'src/dev/res'
              manifest.srcFile 'src/dev/AndroidManifest.xml'
          }
          official {
              ...
          }
          ...
      }
    
  • dependencies
    依賴項(xiàng)用以管理來(lái)自本地或遠(yuǎn)程地址上的項(xiàng)目依賴,包括了模塊依賴,遠(yuǎn)程和本地的庫(kù)文件依賴. 同時(shí)可以指定使用依賴項(xiàng)的方式以及為不同變體指定不同的依賴項(xiàng)

      dependencies {
          compile project(':comi_base')  //編譯時(shí)依賴,gradle 將此配置的依賴項(xiàng)添加到類路徑和應(yīng)用的apk
          apk files('libs/apk_use.jar')  //僅運(yùn)行時(shí)依賴,需要將其與apk一起打包.只可用于jar包形式的依賴項(xiàng)
          provided files('libs/apk_unuse.jar')  //不與apk一起打包的編譯時(shí)依賴,只可用于jar包形式的依賴項(xiàng)
          officialCompile project(':comi_player')  //構(gòu)建official版本時(shí)使用的依賴項(xiàng)
          debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'  //構(gòu)建debug編譯類型時(shí)使用的依賴項(xiàng)
          releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'  //構(gòu)建release編譯類型時(shí)使用的依賴項(xiàng)
          baiduCompile files('libs/BDAutoUpdateSDK_20150605_V1.2.0.jar')  //構(gòu)建baidu渠道版本時(shí)使用的依賴項(xiàng)
          baiduCompile files('libs/need_lib.jar')
          baiduCompile files('libs/patchupdate.jar')
      }
    

0x04 多種變體的打包實(shí)踐

  • 基于module劃分的項(xiàng)目結(jié)構(gòu)
    利用module將項(xiàng)目工程劃分為多個(gè)app module和多個(gè)lib module的結(jié)構(gòu),多個(gè)app工程可以各自選擇需要使用到的lib工程,每個(gè)lib工程實(shí)現(xiàn)相對(duì)獨(dú)立的功能模塊.

      +--- Comi/
      |
      +--- app/
      |    |
      |    \--- build.gradle
      |    \--- src/
      |          |
      |          \--- main/
      |          |      \--- java/
      |          |            \--- com.icomico.comi/
      |          |      \--- res/
      |          \--- jp/
      |          |      \--- java/
      |          |            \--- com.icomico.comi/
      |          |                  |
      |          |                  \--- ChConfig.java
      |          \--- official/
      |          |      \--- java/
      |          |            \--- com.icomico.comi/
      |          |                  |
      |          |                  \--- ChConfig.java
      |          \--- ...
      |    \ ...
      +--- app_special/
      +--- comi_base/
      +--- comi_reader/
      +--- comi_player/
      +--- comi_web/
      +--- comi_danmaku/
      +--- ...
    
  • 使用build.gradle腳本管理module的構(gòu)建配置

      android {
          defaultConfig { ... }
          buildTypes { ... }
          signingConfigs { ... }
          sourceSets { ... }
          productFlavors {
              develop {
                  applicationId 'com.icomico.comi.dev'  //應(yīng)用包名
                  manifestPlaceholders = [TEMP_CH_NAME name]}  //使用flavor名稱替換manifest中的渠道號(hào)字段,實(shí)現(xiàn)修改渠道號(hào)
              official {
                  applicationId 'com.icomico.comi'
                  manifestPlaceholders = [TEMP_CH_NAME: name]
              }
              ...
          }
          ...
      }
      dependencies {
          compile project(':comi_base')
          officialCompile project(':comi_player')
          ...
      }
    
  • 變體
    由buildTypes, productFlavors, sourceSets三項(xiàng)配置, 可以形成gradle打包任務(wù)中的多種變體, gradle針對(duì)每種變體組合都創(chuàng)建了一個(gè)構(gòu)建任務(wù)assemble<productFlavor><buildType>,可指定執(zhí)行不同變體的構(gòu)建任務(wù)

  • 為變體指定包名,替換指定字段,指定依賴項(xiàng)等
    在app工程的build.gradle構(gòu)建腳本中,使用productFlavors來(lái)定義了多個(gè)渠道的變體,并為不同渠道設(shè)置了不同的應(yīng)用包名,渠道號(hào)等.同時(shí)可以在dependencies中為不同的變體選擇了依賴的lib工程,如comi_player庫(kù)僅為official渠道的變體集成

  • 不同變體使用多套代碼及資源
    針對(duì)代碼,在app工程的src目錄下,利用sourceset劃分出official和jp兩個(gè)源集,其中各自定義了用一份java類ChConfig.java,在打包中不同源集的apk文件包含的便是不同的代碼文件.main目錄下的代碼文件會(huì)包含在每個(gè)源集中,因此在main目錄下就不能定義同樣的ChConfig.java文件
    針對(duì)資源,與代碼文件相同,可以在源集目錄下定義同名的資源,生成apk包時(shí)會(huì)覆蓋main目錄下的同名資源

0x05 渠道打包

  • 結(jié)合持續(xù)集成環(huán)境,可以將渠道號(hào)等參數(shù)以一定規(guī)則作為文件名,在已生成好的apk文件中的META-INF目錄下寫入空文件,以提升渠道包打包速度

0x06 參考文獻(xiàn)

  1. https://developer.android.google.cn/studio/build/index.html
  2. http://tech.meituan.com/mt-apk-packaging.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容