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)建流程包含了以下幾步:
- 編譯器將源碼轉(zhuǎn)換成dex文件,并將其它所有內(nèi)容轉(zhuǎn)換成已編譯資源
- 打包工具將dex文件和已編譯資源合并成單個(gè)apk文件,apk文件此時(shí)處于未簽名狀態(tài)
- 打包工具根據(jù)當(dāng)前構(gòu)建任務(wù)對(duì)應(yīng)buildTypes中選擇的signingConfigs選項(xiàng),使用相應(yīng)秘鑰對(duì)apk文件對(duì)apk進(jìn)行簽名
- 打包工具使用zipalign工具對(duì)apk包進(jìn)行優(yōu)化
- 構(gòu)建結(jié)束,生成最終apk文件

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目錄下寫入空文件,以提升渠道包打包速度