Android編譯打包流程和資源加載淺析,及如何提高工具打包的資源加載效率

背景:

簡(jiǎn)單來(lái)說(shuō)我們的打包工作就是hack原始包,向其中注入代碼。ps:我們不是黑客!

原始打包(hack)方案:

  • 1.反編譯原始apk,得到文件夾A。
  • 2.將要加入的jar包變成dex,再變成smali,放入A。
  • 3.將要加入的資源直接放入A。
  • 4.重新編譯A生成新apk。

現(xiàn)在的方案來(lái)自Android逆向。
好了,看出問(wèn)題在哪嗎?
資源直接放入A,未經(jīng)編譯,所以沒(méi)有索引。

這種資源如何在運(yùn)行時(shí)加載?

采用Resources.getIdentifier()。

getResources().getIdentifier("icon", "drawable", "com.XX.XX.mainactivity");

官方不鼓勵(lì)用這種方式,采用索引比使用名稱更快。原話如下:
use of this function is discouraged. It is much more efficient to retrieve resources by identifier than by name.

這種打包方案是可行的,但是官方的提示,始終內(nèi)心不安,于是繼續(xù)深挖。

資源索引

反編譯得到文件夾,進(jìn)去是看不到索引的。所以一開(kāi)始我們也沒(méi)關(guān)注索引。
而直接解壓是能看到索引的。
解壓apk包得到內(nèi)容如下:

image

AndroidManifest.xml:直接解壓得到二進(jìn)制格式清單文件,打開(kāi)后一堆亂碼,需要將apk反編譯才能看到文本格式。
class.dex :代碼文件。
resources.arsc: 資源索引文件,這個(gè)是重點(diǎn),之前被忽略了。
res:資源,包括圖片,字符串、布局等。
assets: 也是資源文件夾。

assets和res的區(qū)別:

    1. res中的文件會(huì)被映射到R.java文件中,訪問(wèn)的時(shí)候直接使用資源ID即R.id.filename;用來(lái)存放android應(yīng)用程序的9中類型的資源,如圖標(biāo),顏色,動(dòng)畫(huà),布局,數(shù)值,配置等信息。
    1. assets:
      這些文件最終會(huì)被原裝不動(dòng)地打包在apk文件中。assets文件夾下的文件不會(huì)被映射到R.java中,需要使用文件名來(lái)訪問(wèn)。
      this is a good location for textures and game data.
      存放游戲數(shù)據(jù)如聲明,協(xié)議等。
待加入原始apk的渠道文件中包含的資源文件,大多數(shù)資源在res目錄下,是可以生成索引,這是我們優(yōu)化的點(diǎn)。

索引的好處:

為了使得應(yīng)用程序能夠在運(yùn)行時(shí)同時(shí)支持不同尺寸和密度的屏幕和不同的語(yǔ)言,Android應(yīng)用程序資源的組織方式有19個(gè)維度,組織方式如下圖所示。有不同的屏幕和中文語(yǔ)言。

image

resources.arsc是一種二進(jìn)制格式的文件。aapt在對(duì)資源文件進(jìn)行編譯時(shí),會(huì)為每一個(gè)資源分配唯一的id值,程序在執(zhí)行時(shí)會(huì)根據(jù)這些id值讀取特定的資源,而resources.arsc文件正是包含了所有id值的一個(gè)數(shù)據(jù)集合。在該文件中,如果某個(gè)id對(duì)應(yīng)的資源是String或者數(shù)值(包括int,long等),那么該文件會(huì)直接包含相應(yīng)的值,如果id對(duì)應(yīng)的資源是某個(gè)layout或者drawable資源,那么該文件會(huì)存入對(duì)應(yīng)資源的路徑地址。

可見(jiàn),這么多資源目錄,不通過(guò)索引而直接用資源名稱來(lái)查找資源是很費(fèi)時(shí)的。
萬(wàn)不得已要用資源名稱查找時(shí),可以采用下面的方式,據(jù)說(shuō)比前面提到的Resources.getIdentifier() 快4倍。

使用名稱查找資源的更優(yōu)方式:

/**
 * @author Lonkly
 * @param variableName - name of drawable, e.g R.drawable.<b>image</b>
 * @param с - class of resource, e.g R.drawable.class or R.raw.class
 * @return integer id of resource
 */
public static int getResId(String variableName, Class<?> с) {

    Field field = null;
    int resId = 0;
    try {
        field = с.getField(variableName);
        try {
            resId = field.getInt(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return resId;

}

使用:

int id = ResourceMan.getResId("icon", R.drawable.class);

新加入的資源能生成索引嗎?

我們都知道編寫(xiě)代碼時(shí),res中的資源可以直接通過(guò)findViewById(R.XX.XX)的方式獲得。R.jav中存放了資源的id,這是IDE自動(dòng)生成的,其實(shí)IDE也是通過(guò)資源打包工具aapt(android Asset Package Tool)工具來(lái)編譯的,

Android資源打包工具aapt在編譯和打包資源的過(guò)程中,會(huì)執(zhí)行以下兩個(gè)額外的操作:

  1. 賦予每一個(gè)非assets資源一個(gè)ID值,這些ID值以常量的形式定義在一個(gè)R.Java文件中。
  2. 生成一個(gè)resources.arsc文件,用來(lái)描述那些具有ID值的資源的配置信息,它的內(nèi)容就相當(dāng)于是一個(gè)資源索引表。

有了資源ID以及資源索引表之后,Android資源管理框架就可以迅速將根據(jù)設(shè)備當(dāng)前配置信息來(lái)定位最匹配的資源了。

## Android簡(jiǎn)化版打包流程

image

打包流程圖

image
image
  1. 通過(guò)aapt打包res資源文件,生成R.java、resources.arsc和res文件(二進(jìn)制 & 非二進(jìn)制如res/raw和pic保持原樣)
  2. 處理.aidl文件,生成對(duì)應(yīng)的Java接口文件
  3. 通過(guò)Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件
  4. 通過(guò)dex命令,將.class文件和第三方庫(kù)中的.class文件處理生成classes.dex
  5. 通過(guò)apkbuilder工具,將aapt生成的resources.arsc和res文件、未編譯的資源assets文件和classes.dex一起打包生成apk
  6. 通過(guò)Jarsigner工具,對(duì)上面的apk進(jìn)行debug或release簽名
  7. 通過(guò)zipalign工具,將簽名后的apk進(jìn)行對(duì)齊處理。

從完整的打包流程來(lái)看,使用aapt把待加入原始apk的資源文件編譯生成資源索引是關(guān)鍵。

生成索引

AAPT是Android Asset Packaging Tool的縮寫(xiě),它存放在SDK的tools/目錄下,AAPT的功能很強(qiáng)大,可以通過(guò)它查看查看、創(chuàng)建、更新壓縮文件(如 .zip文件,.jar文件, .apk文件), 它也可以把資源編譯為二進(jìn)制文件,并生成resources.arsc, AAPT這個(gè)工具在APK打包過(guò)程中起到了非常重要作用,在打包過(guò)程中使用AAPT對(duì)APK中用到的資源進(jìn)行打包,這里不對(duì)AAPT這個(gè)工具做過(guò)多的討論,只看一下AAPT這個(gè)工具在打包過(guò)程中起到的作用,下圖是AAPT打包的流程:


image

AAPT這個(gè)工具在打包過(guò)程中主要做了下列工作:

  1. 把"assets"和"res/raw"目錄下的所有資源進(jìn)行打包(會(huì)根據(jù)不同的文件后綴選擇壓縮或不壓縮),而"res/"目錄下的其他資源進(jìn)行編譯或者其他處理(具體處理方式視文件后綴不同而不同,例如:".xml"會(huì)編譯成二進(jìn)制文件,".png"文件會(huì)進(jìn)行優(yōu)化等等)后才進(jìn)行打包;
  2. 會(huì)對(duì)除了assets資源之外所有的資源賦予一個(gè)資源ID常量,并且會(huì)生成一個(gè)資源索引表resources.arsc;
  3. 編譯AndroidManifest.xml成二進(jìn)制的XML文件;
  4. 把上面3個(gè)步驟中生成結(jié)果保存在一個(gè).ap_文件,并把各個(gè)資源ID常量定義在一個(gè)R.java中,打入.dex;
**資源加密  
修改aapt,編譯時(shí)對(duì)資源文件名稱壓縮,這樣通過(guò)使用修改過(guò)的AAPT編譯資源并進(jìn)行打包,  
既可以減小包體,又可以對(duì)資源進(jìn)行一定程度的加密。**

aapt 命令參數(shù)

-f 如果編譯出來(lái)的文件已經(jīng)存在,強(qiáng)制覆蓋。
-m 使生成的包的目錄放在-J參數(shù)指定的目錄。
-J 指定生成的R.java的輸出目錄
-S res文件夾路徑
-A assert文件夾的路徑
-M AndroidManifest.xml的路徑
-I 某個(gè)版本平臺(tái)的android.jar的路徑
-F 具體指定apk文件的輸出

命令具體使用方法,這里不贅述,Android知識(shí)博大精深,隨便一個(gè)點(diǎn)可以拿出來(lái)說(shuō)幾篇。

Android資源加載

image

事實(shí)上,當(dāng)程序運(yùn)行時(shí),所需要的資源都要從原始文件中讀取(APK在安裝時(shí)都會(huì)被系統(tǒng)拷貝到/data/app目錄下)。加載資源時(shí),首先加載resources.arsc,然后根據(jù)id值找到指定的資源,沒(méi)有資源索引的就需要通過(guò)資源名稱來(lái)加載。

總結(jié):

打包流程:

  1. 新資源拷貝到原始文件夾相應(yīng)目錄,(這里可能會(huì)有命名沖突,如何解決?)。
  2. aapt編譯資源,重新生成R.java和resources.arsc并覆蓋舊版。
  3. 資源編譯時(shí)順便簡(jiǎn)化資源名稱,實(shí)現(xiàn)減小包體和資源加密的目的。
  4. 后續(xù)流程和之前的打包方式一樣。

資源加載:
有了索引之后就可以直接通過(guò)索引而不是資源名稱訪問(wèn)了,提高效率。

以上內(nèi)容是通過(guò)研究Android打包流程和資源加載時(shí)對(duì)現(xiàn)有打包方案不足之處的思考,僅理論,待實(shí)踐。
本人知識(shí)淺薄,有任何問(wèn)題請(qǐng)指正,不吝賜教!

參考文章:

Android官網(wǎng)

Android應(yīng)用程序資源的編譯和打包過(guò)程分析

Android資源管理框架(Asset Manager)
打包總覽

老羅的Android之旅
Android應(yīng)用程序資源的編譯和打包過(guò)程分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

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

  • Android插件化基礎(chǔ)的主要內(nèi)容包括 Android插件化基礎(chǔ)1-----加載SD上APKAndroid插件化基...
    隔壁老李頭閱讀 7,140評(píng)論 13 48
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,611評(píng)論 25 708
  • 目錄 AAPT解釋,作用 AAPT基本命令 AAPT編譯資源源碼解析 AAPT打包和系統(tǒng)不一致的資源ID AAPT...
    徐正峰閱讀 84,882評(píng)論 7 139
  • 插件化-資源處理 寫(xiě)的比較長(zhǎng),可以選擇跳過(guò)前面2節(jié),直接從0x03實(shí)例分析開(kāi)始。如有錯(cuò)誤,請(qǐng)不吝指正。 0x00 ...
    唐一川閱讀 5,358評(píng)論 2 22
  • 在“張學(xué)友”(小張我的同學(xué)和朋友)里面至少有一半擁有外號(hào),且有好多小伙伴的外號(hào)不止一個(gè)。而我的外號(hào)幾乎和民...
    何處拾珠閱讀 271評(píng)論 4 1