1.一套完整的Android App打包流程(Gradle方案)
java
第一步:aapt。爲res目錄下的資源生成R.java文件,同時爲AndroidManifest.xml生成Manifest.java文件android
第二步:aidl。把項目中自定義的aidl文件生成相應的Java代碼文件ruby
第三步:javac。把項目中全部的Java代碼編譯成class文件。包括三部分Java代碼,本身寫的的業務邏輯代碼,aapt生成的Java文件,aidl生成的Java文件微信
第四步:proguard。混淆同時生成proguardMapping.txt。這一步是可選的app
第五步:dex。把全部的class文件(包括第三方庫的class文件)轉換成dex文件ide
第六步:aapt。把res目錄下的資源、assets目錄下的文件,打包成一個.ap_文件函數
第七步:apkbuilder。將全部的dex文件、ap_文件、AndroidManifest.xml打包爲.apk文件,這是一個未簽名的apk包工具
第八步:jarsigner。對apk進行簽名gradle
第九步:zipaligin。對要發佈的apk文件進行對齊操做,以便在運行時節省內存ui
2.res目錄下的全部資源會生成一個R.java文件,每一個資源都對應一個R中的十六進制整數變量,由三部分組成,即PackageId+TypeId+EntryId(通常是默認0x7f+兩位+四位)
3.aapt命令在打包過程當中都作了什麼
1)把assets和res目錄下的全部資源、AndroidManifest.xml,都保存在一個後綴名ap_的文件中,就是一個壓縮包
2)爲res目錄的每一個資源,生成一個資源id常量,把id值和資源名稱的對應關係,存放在resources.arsc文件中
3)把這些資源id常量,都定義在R.java文件中
4.在aapt命令執行完,纔會執行javac命令,把包括R.java在內的素有java文件,進行編譯
***插件化中資源id衝突的解決方案***
方案1:
把宿主和插件的資源都合併到一塊兒
方案1.1:重寫AAPT命令,在插件apk打包過程當中,經過指定資源id的前綴,好比0x71,來保證宿主和插件的資源id永遠不會衝突
1)在AAPT的命令行參數中傳遞apk打包時的前綴值
2)把這個值設置給Bundle實體的mApkModule字段,做爲ResourceTable構造函數的參數傳遞進去
3)在ResourceTable的構造函數,讀取Bundle參數中的mApkModule值,也就是前綴值,設置給packageId
都是c代碼,就不貼了
4)把新的AAPT工具命名爲aapt_mac,放到項目根目錄下
apply plugin: 'com.android.application'
import com.android.sdklib.BuildToolInfo
import java.lang.reflect.Method
Task modifyAaptPathTask = task('modifyAaptPath') << { { variant -> BuildToolInfo buildToolInfo = variant.androidBuilder.getTargetInfo().getBuildTools() Method addMethod = BuildToolInfo.class.getDeclaredMethod("add", BuildToolInfo.PathId.class, File.class) addMethod.setAccessible(true) BuildToolInfo.PathId.AAPT, new File(rootDir, "aapt_mac")) println "[LOG] new aapt path = " + buildToolInfo.getPath(BuildToolInfo.PathId.AAPT) }}
android { compileSdkVersion 25 buildToolsVersion "25.0.3"
defaultConfig { applicationId "jianqiang.com.testreflection" minSdkVersion 21 targetSdkVersion 25 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
{ modifyAaptPathTask.execute() }
aaptOptions { '--PLUG-resoure-id', '0x71' }}
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.2.0'}
經過反射,把AAPT路徑臨時修改成指向當前app根路徑下的mac_aapt
方案1.2:在插件apk打包後,修改R,java和resources.arsc中存儲的資源id值,好比默認的0x7f前綴,修改成0x71,這樣就保證了宿主和插件的資源id永遠不會衝突
方案1.3:在public.xml中指定apk中全部資源的id值。但每增長一個資源,都要維護public.xml。因此只能用於固定幾個特定的值
afterEvaluate { for (variant in android.applicationVariants) { def scope = variant.getVariantData().getScope() String mergeTaskName = scope.getMergeResourcesTask().name
def mergeTask = tasks.getByName(mergeTaskName)
mergeTask.doLast { copy { int i = 0 println android.sourceSets.main.res.srcDirs from(android.sourceSets.main.res.srcDirs) { include 'values/public.xml' rename 'public.xml', (i++ == 0 ? "public.xml" : "public_${i}.xml") }
into(mergeTask.outputDir) } } }}
string.xml
<resources> <string name="app_name">ActivityHook1</string>
<string name="string1">Test String</string></resources>
public.xml
<resources> <public type = "string" name="string1" id = "0x7f050024"/></resources>
宿主的資源值固定了之後,插件若是想使用宿主的資源,只要把宿主打包成jar,而後複製到插件項目的某個位置,使用gradle腳本provided就能夠了,這樣打出的插件不會包含宿主的代碼
方案2:若是不事先合併資源,那就爲每一個插件建立一個AssetManager,每一個AssetManager都是經過反射調用addAssetPath方法,把插件本身的資源添加進去。詳細的代碼見資源的插件化
方案1的缺點是資源id的前綴是有限的,就256個值,當一個app中有多於256個插件時,就要考慮方案2了
--摘自《android插件化開發指南》
歡迎關注個人微信公衆號:安卓圈
本文分享自微信公衆號 - 安卓圈(gh_df75572d44e4)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。