插件化-Apk編譯過程概述

插件化-Apk編譯過程概述

0x00

大致的看了一下目前插件化的開源實(shí)現(xiàn)验毡,或多或少都會(huì)對(duì)Apk的編譯過程做出改動(dòng)称龙,因此嘗試分析了一下Apk的打包過程识啦。一個(gè)Apk文件實(shí)際上就是一個(gè)zip壓縮包伴栓,我們把一個(gè)Apk解壓后诬辈,里面的內(nèi)容類似下圖混弥。

里面每個(gè)文件是什么含義趴乡,我們待會(huì)再看对省。那么,如何生成一個(gè)Apk文件呢晾捏?通常情況下蒿涎,我們使用一些構(gòu)建工具來(lái)編譯我們的工程,例如古老的Ant惦辛,maven劳秋,我們正在使用的gradle,以及更加黑科技的buck等胖齐,但是玻淑,這些構(gòu)建工具并不直接作用于編譯過程,打開sdk中的build-tools目錄呀伙,如下

這些就是google為我們提供的工具补履,通過它們,我們得以將代碼編譯成Apk剿另,構(gòu)建工具只是這些的工具的封裝箫锤。

0x01 HelloWorld

下面我們來(lái)嘗試手動(dòng)編譯一個(gè)最簡(jiǎn)單的Apk。開始之前雨女,先簡(jiǎn)單介紹一下我們要使用的工具谚攒。

  1. aapt[Android Asset Packaging Tool]這個(gè)工具主要幫助我們處理資源文件,以及創(chuàng)建氛堕,更新馏臭,查看一個(gè)Apk文件。
  2. dx岔擂,這個(gè)工具幫助我們把.class文件轉(zhuǎn)換成一個(gè)dex文件位喂,dex[Dalvik Executable]文件就是Dalvik虛擬機(jī)的可執(zhí)行文件,這個(gè)文件的具體格式稍后會(huì)做簡(jiǎn)單的介紹乱灵。
  3. zipalign塑崖,這個(gè)工具用來(lái)優(yōu)化我們生成的apk文件,它將資源文件進(jìn)行4字節(jié)對(duì)齊痛倚,當(dāng)資源文件映射進(jìn)內(nèi)存時(shí)规婆,對(duì)齊到4字節(jié)邊界可以加快資源文件的訪問速度。

還有兩個(gè)我們使用的工具并沒有出現(xiàn)這里蝉稳,而是存在于JDK中抒蚜。

  1. javac,很常用的工具,用來(lái)將java源碼文件編譯成字節(jié)碼文件
  2. keytool耘戚,創(chuàng)建簽名的工具
  3. jarsigner嗡髓,用來(lái)對(duì)生成的apk進(jìn)行簽名的工具

下面我們來(lái)開始編譯helloWorld,首先我們編寫了一個(gè)最簡(jiǎn)單的Apk收津。

這個(gè)工程只有一個(gè)Activity,并且不依賴任何庫(kù)饿这。

Step 1: 生成R.java文件

R.java是我們?cè)L問資源的必需品浊伙,R文件是一個(gè)普通的類,其中根據(jù)資源的類型有不同的靜態(tài)內(nèi)部類长捧,每個(gè)靜態(tài)內(nèi)部類中的靜態(tài)常量分別定義一條資源標(biāo)示符嚣鄙,這個(gè)類并不是我們編寫的而是由aapt工具生成的。
在工程的根目錄串结,執(zhí)行:

aapt package \   #打包資源文件
-f \             #強(qiáng)制覆蓋已有文件
-m \             #使R文件在-J參數(shù)指定的位置生成
-S res \         #資源目錄
-J gen \         #R.java的位置
-I $ANDROID_HOME/platforms/android-23/android.jar \ #base-package
-M AndroidManifest.xml                              #清單文件的路徑

Step 2: 編譯代碼

生成后的文件存放在gen目錄下哑子,有了R.java 我們就可以使用javac來(lái)編譯我們的代碼了,繼續(xù)執(zhí)行:

javac -classpath \                          #添加依賴包肌割,多個(gè)jar包用:分割
$ANDROID_HOME/platforms/android-23/android.jar \ #sdk-23
-source 1.7 -target 1.7 \                        #指明源碼版本和字節(jié)碼版本
-d ./build \                                     #編譯后的class文件的路徑
./java/com/haizhi/oa/buildtest/*.java \          #源碼1卧蜓,這是我們寫的Activity
./gen/com/haizhi/oa/buildtest/R.java             #源碼2,R.java

Step 3: 編譯為dex文件

在上一步中声功,我們將代碼編譯成了字節(jié)碼烦却,但是dalvik并不能直接執(zhí)行字節(jié)碼宠叼,需要進(jìn)一步的將class文件編譯成dex文件先巴,這個(gè)過程是通過dx這個(gè)工具實(shí)現(xiàn)的,在build目錄下冒冬,我們繼續(xù)執(zhí)行:

dx --dex --output=classes.dex .   #指定輸出為classes.dex 輸入為當(dāng)前目錄

至此伸蚯,我們已經(jīng)獲得了生成一個(gè)Apk需要的所有東西。

Step 4: 打包所有的資源文件

在工程的根目錄简烤,執(zhí)行:

aapt package \
-f \
-S res \
-I $ANDROID_HOME/platforms/android-23/android.jar \
-M AndroidManifest.xml \
-F test.apk.u                       #生成apk文件

此時(shí)剂邮,我們已經(jīng)獲得了一個(gè)apk文件,下面我們要對(duì)它簽名横侦,首先需要使用keytool工具生成一個(gè)簽名文件挥萌,這個(gè)步驟可以自行百度。

Step 5: 將classes.dex文件加入apk中

aapt add -f test.apk.u classes.dex

Step 6: 簽名枉侧,對(duì)齊
在工程的根目錄引瀑,執(zhí)行:

簽名:
jarsigner -storepass **密*碼** -keystore ../chenlong.keystore test.apk.u chenlong

對(duì)齊:
zipalign 4 test.apk.u test.apk

經(jīng)過上述5個(gè)步驟,我們生成了一個(gè)apk榨馁,下面安裝到模擬器上執(zhí)行一下憨栽,如圖:

以上,就是一個(gè)最簡(jiǎn)單的Apk的編譯過程翼虫,其中Apk最重要的兩個(gè)部分屑柔,資源和代碼被編譯成了resources.arsc+res以及dex文件。res是實(shí)際的資源珍剑,resources.arsc則是一個(gè)索引掸宛,AssetManager通過這個(gè)索引獲取資源的實(shí)際內(nèi)容,這其中的過程比較復(fù)雜招拙,暫時(shí)還沒有太多的分析唧瘾,至于dex文件翔曲,倒是可以啰嗦兩句。

我們知道劈愚,java源碼文件編譯后生成了字節(jié)碼文件瞳遍,然后被jvm執(zhí)行,字節(jié)碼文件中有一個(gè)非常重要的區(qū)域是常量池菌羽,編譯的過程中掠械,字節(jié)碼文件并不會(huì)保存方法和字段的最終內(nèi)存布局信息,也就是說注祖,方法和字段并不像C/C++那樣被編譯成地址猾蒂,jvm在加載Class文件的時(shí)候,需要從常量池獲取對(duì)應(yīng)的符號(hào)引用是晨,再在類創(chuàng)建時(shí)或運(yùn)行時(shí)解析并翻譯到具體的內(nèi)存地址中【參考:深入理解Java虛擬機(jī)-JVM高級(jí)特性與最佳實(shí)踐】肚菠。一個(gè)字節(jié)碼文件中,除了方法體中的內(nèi)容被編譯為字節(jié)碼指令外罩缴,大部分的信息都保存在常量池中蚊逢,通過索引來(lái)訪問,包括類的名稱箫章,類的字段烙荷,類的繼承關(guān)系,類中方法的定義等檬寂。

那么终抽,dex文件和class文件有什么區(qū)別呢?

首先桶至,dalvik虛擬機(jī)的字節(jié)碼指令是16位昼伴,而jvm是8位,因此镣屹,java 字節(jié)碼被轉(zhuǎn)換成dex 字節(jié)碼圃郊;其次,dex文件將多個(gè)class文件合并成一個(gè)野瘦,合并了這些class文件的常量池描沟,并作出了其他的優(yōu)化,讓dex文件執(zhí)行的更快鞭光,更節(jié)省內(nèi)存吏廉。對(duì)于dex文件的詳細(xì)格式,可以參考 dex-format惰许,我嘗試了一下直接閱讀dex文件席覆,講真,不是很好讀汹买。佩伤。下圖是我們剛剛編譯出的dex文件的16進(jìn)制格式聊倔,加了一些簡(jiǎn)單的標(biāo)注和分塊,一共3012個(gè)字節(jié)生巡。

0x02 進(jìn)階-編譯一個(gè)帶依賴的工程

在實(shí)際的編碼過程中耙蔑,我們往往會(huì)去依賴一些子工程,子工程有兩種孤荣,一種是java工程甸陌,一種是Android Lib工程。java工程中不包含資源文件盐股,編譯后的輸出是jar包钱豁,而Android Lib工程包含資源文件,編譯后的輸出為aar文件疯汁。

對(duì)于jar包牲尺,我們只需要在編譯apk的java代碼時(shí),將jar包加入classpath幌蚊,然后在編譯dex文件時(shí)谤碳,將jar包一起編譯進(jìn)去就可以了,但是對(duì)于aar文件霹肝,就稍微有點(diǎn)復(fù)雜了估蹄。

首先,我們還是創(chuàng)建一個(gè)工程沫换,如圖:

這個(gè)工程依賴了design包,v7包中的appcompat最铁,同時(shí)讯赏,上述這些包又依賴了v4包,冷尉,recyclerview漱挎,support-vector-drawable,animated-vector-drawable雀哨,support-annotations磕谅。

上述這些依賴都是Android Lib工程,因此我們需要處理依賴包中的資源雾棺。首先膊夹,我們需要這些依賴的aar文件作為輸入,到哪里去找aar文件呢捌浩?最初放刨,我在sdk下找到了這些依賴的jar包和相應(yīng)的資源目錄,但是尸饺,當(dāng)我嘗試編譯的時(shí)候进统,總是提示我找不到資源助币,我很苦惱,后來(lái)在高旭大神的指點(diǎn)下螟碎,我看了一下gradle的實(shí)現(xiàn)方式眉菱,發(fā)現(xiàn)gradle并不使用jar包+資源來(lái)重新編譯這些依賴庫(kù)而是直接使用了google提供的這些依賴庫(kù)的aar文件,于是我嘗試將編譯好的aar文件解包掉分,再使用解包后的資源和jar包進(jìn)行編譯倍谜。

Step 1: 生成R.java文件

aapt package -f -m --auto-add-overlay \
-S res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/appcompat-v7/23.3.0/aarEx/res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/recyclerview-v7/23.3.0/aarEx/res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/design/23.3.0/aarEx/res \
-J gen \
--extra-packages android.support.v7.appcompat:android.support.v7.recyclerview:android.support.design \
-I $ANDROID_HOME/platforms/android-23/android.jar -M ./AndroidManifest.xml

其中,--auto-add-overlay 參數(shù)用來(lái)加載多個(gè)資源目錄叉抡,按照從左向右的順序尔崔,如果后面的資源重復(fù)則跳過,如果不重復(fù)則新增褥民。
--extra-packages用來(lái)對(duì)不同的資源目錄生成包名不同的R文件季春,多個(gè)包名通過:分割。

Step 2: 編譯代碼

javac -classpath $ANDROID_HOME/extras/android/support/v7/appcompat/libs/android-support-v4.jar:\
$ANDORID_HOME/extras/android/support/annotations/android-support-annotations.jar:\
$ANDROID_HOME/platforms/android-23/android.jar:\
$ANDROID_HOME/extras/android/support/design/libs/android-support-design.jar:\
$ANDROID_HOME/extras/android/support/v7/appcompat/libs/android-support-v7-appcompat.jar:\
$ANDROID_HOME/extras/android/support/v7/recyclerview/libs/android-support-v7-recyclerview.jar \
-source 1.7 -target 1.7 \
-d ./build \
./java/com/haizhi/oa/buildtest/*.java \
./gen/com/haizhi/oa/buildtest/R.java \
./gen/android/support/design/R.java \
./gen/android/support/v7/appcompat/R.java \
./gen/android/support/v7/recyclerview/R.java

Step 3: 編譯dex文件

dx --dex --output=classes.dex . \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/support-v4/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/support-v4/23.3.0/aarEx/libs/internal_impl-23.3.0.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/design/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/appcompat-v7/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/recyclerview-v7/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/support-vector-drawable/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/animated-vector-drawable/23.3.0/aarEx/classes.jar \
/Users/chenlong/sdk/extras/android/m2repository/com/android/support/support-annotations/23.3.0/support-annotations-23.3.0.jar

Step 4: 生成apk文件

aapt package -f -m --auto-add-overlay \
-S res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/appcompat-v7/23.3.0/aarEx/res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/recyclerview-v7/23.3.0/aarEx/res \
-S /Users/chenlong/sdk/extras/android/m2repository/com/android/support/design/23.3.0/aarEx/res \
-I $ANDROID_HOME/platforms/android-23/android.jar -M ./AndroidManifest.xml \
-F test.apk.u

Step 5: 將classes.dex文件加入apk中

aapt add -f test.apk.u classes.dex

后面的簽名消返、對(duì)齊操作和之前一樣

最后载弄,我們?cè)谀M器上運(yùn)行一下打包后的apk文件,如圖:

0x03 總結(jié)

編譯流程的簡(jiǎn)單分析就是這些撵颊,在上述流程中我們可以看到宇攻,主要過程是資源處理和dex文件生成上,其中對(duì)資源的處理是插件化的一個(gè)難點(diǎn)倡勇,我的分析并不是很全面逞刷,比如對(duì)于多個(gè)資源目錄合并的過程,aapt自身提供的機(jī)制和gradle的實(shí)現(xiàn)就不太一樣妻熊,gradle在最終調(diào)用aapt之前已經(jīng)將資源合并夸浅,傳入aapt的只有一個(gè)合并后的資源目錄,可以參考gradle 資源合并機(jī)制扔役,后續(xù)我會(huì)針對(duì)資源文件的處理做單獨(dú)的分析帆喇。

上述內(nèi)容如有錯(cuò)誤,懇請(qǐng)指正亿胸,我會(huì)繼續(xù)分析插件化的相關(guān)技術(shù)實(shí)現(xiàn)坯钦,敬請(qǐng)期待。


原文:插件化-Apk編譯過程概述

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侈玄,一起剝皮案震驚了整個(gè)濱河市婉刀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拗馒,老刑警劉巖路星,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡洋丐,警方通過查閱死者的電腦和手機(jī)呈昔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)友绝,“玉大人堤尾,你說我怎么就攤上這事∏停” “怎么了郭宝?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)掷漱。 經(jīng)常有香客問我粘室,道長(zhǎng),這世上最難降的妖魔是什么卜范? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任衔统,我火速辦了婚禮,結(jié)果婚禮上海雪,老公的妹妹穿的比我還像新娘锦爵。我一直安慰自己,他們只是感情好奥裸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布险掀。 她就那樣靜靜地躺著,像睡著了一般湾宙。 火紅的嫁衣襯著肌膚如雪樟氢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天创倔,我揣著相機(jī)與錄音嗡害,去河邊找鬼。 笑死畦攘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的十电。 我是一名探鬼主播知押,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鹃骂!你這毒婦竟也來(lái)了台盯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤畏线,失蹤者是張志新(化名)和其女友劉穎静盅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒿叠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年明垢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片市咽。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痊银,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出施绎,到底是詐尸還是另有隱情溯革,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布谷醉,位于F島的核電站致稀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏俱尼。R本人自食惡果不足惜抖单,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望号显。 院中可真熱鬧臭猜,春花似錦、人聲如沸押蚤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)揽碘。三九已至次屠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雳刺,已是汗流浹背劫灶。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掖桦,地道東北人本昏。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像枪汪,于是被迫代替她去往敵國(guó)和親涌穆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容