轉(zhuǎn)Android安裝包相關(guān)知識(shí)匯總

WeMobileDev | 2015-09-24 13:34

用戶抱怨安裝包越來越大?印度友人反饋裝不上微信?歡迎來到本期的走進(jìn)科學(xué)--安裝包速成記。做一個(gè)有節(jié)操的安裝包,我們希望它越小越好,并且確保用戶都能安裝的上。

Android的安裝包,簡單來說就是一個(gè)壓縮包,首先我們了解一下它的生成過程。

一、安裝包編譯過程

一般我們使用ant、gradle等方式編譯生成安裝包,它一般包含以下幾個(gè)步驟。

但是對(duì)于多l(xiāng)ibrary的結(jié)構(gòu)下,ant與gradle的并行度并不足夠。當(dāng)前微信已經(jīng)切換到Facebook的開源編譯工具buck(相關(guān)介紹http://facebook.github.io/buck/),編譯速度得到了大大提升。

在buck的基礎(chǔ)上我們主要做了以下兩個(gè)工作:

EclipseToBuck,開發(fā)者無須關(guān)心buck腳本的編寫,buck腳本使用代碼自動(dòng)生成

微信使用多l(xiāng)ibrary結(jié)構(gòu),每次編譯后監(jiān)控每個(gè)模塊的線性內(nèi)存、方法數(shù)、資源大小。

二、減少安裝包大小的Tips

現(xiàn)在我們開始第一項(xiàng)主要內(nèi)容,如何減少安裝包的體積?

1. 安裝包監(jiān)控

我們做安裝包優(yōu)化,首先要對(duì)我們安裝包每一部分有一個(gè)詳細(xì)的了解,知道安裝包大在什么地方,與上一版本有著怎么樣的變化。所以我們首先實(shí)現(xiàn)了一個(gè)安裝包檢測(cè)工具:

監(jiān)控

1. 每個(gè)dex方法數(shù)的變更情況;

2. 每個(gè)模塊線性內(nèi)存的變化情況;

3. 沒有alpha通道的png圖,可壓縮成jpg減少體積;

4. 超過一定數(shù)值的大文件,特別是圖片資源可采用有損壓縮;

5. 安裝包的大小、文件數(shù)變化;

6. 新增文件、減少文件,文件大小發(fā)生變化的情況;

現(xiàn)時(shí)微信的安裝包監(jiān)控包括兩個(gè)維度,一是每日的監(jiān)控,采用的是最后一個(gè)包與昨日的最后一個(gè)包對(duì)比;二是版本之間的對(duì)比,發(fā)布前需要用待上線版本與線上版本對(duì)比。我們希望若發(fā)現(xiàn)問題,能立刻警報(bào),提交到bug系統(tǒng)。

2. 刪除無用資源

在產(chǎn)品的大錘下,每個(gè)模塊不修改個(gè)三五十次,都不好意思說自己是微信的開發(fā)。在不停的迭代中,或多或少會(huì)出現(xiàn)無用的資源,包括但不限于xml、png、id、string。

查找無用資源主要使用lint的UnusedResources以及UnusedIds兩個(gè)檢查規(guī)則,但是針對(duì)多l(xiāng)ibrary結(jié)構(gòu),官方的lint在某些方面不符合我們的要求,所以我們修改了一些地方。

對(duì)于使用gradle的童鞋,可以采用:

android {? ? ...? ? buildTypes {? ? ? ? release {? ? ? ? ? ? minifyEnabledtrueshrinkResourcestrueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}? ? }}

3. 圖片處理

.png、.9.png、.jpg、.gif資源是每一個(gè)安裝包永遠(yuǎn)的痛,追求美感的設(shè)計(jì)師希望每個(gè)像素都是完美的。這邊的tip有以下幾點(diǎn):

詳細(xì)描述

1.對(duì)于體積特別大(超過50k)的圖片資源可以考慮有損壓縮,jpg采用優(yōu)圖壓縮,png嘗試采用pngquant壓縮,輸出視覺判斷是否可行;

2.對(duì)assets中的圖片資源也使用aapt的crunch做圖片預(yù)處理;

3.crunch有可能會(huì)使圖片變大,在這種情況,我們可以替換成原圖。需要注意的是對(duì)于.9.png,由于crunch過程中去除了黑邊,所以不能替換;

4.對(duì)于沒有透明區(qū)域的png圖片,可以轉(zhuǎn)成jpg格式。

4. 字符串編碼

為了節(jié)省空間,resources.arsc中的會(huì)有一個(gè)去重過的字符串資源池(相同的兩個(gè)字符串其實(shí)用的是同一份),每個(gè)String使用偏移值來獲取資源池中的數(shù)值。ResourceTable的編碼對(duì)于resources.arsc的體積有很大影響。

從Android 2.2 API Level8開始APK文件的資源resources.arsc的編碼有了小幅的改變,過去使用的是UTF-16編碼方式被轉(zhuǎn)換成了UTF-8編碼。這樣的好處就是處理純英文等直接通過ascii存儲(chǔ)語言的國家資源文件將會(huì)更小,而對(duì)于中文、日文這些國家的資源文件有可能會(huì)變大。

bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);

}

當(dāng)然如果你發(fā)現(xiàn)使用UTF-8后resources.arsc反而變大,你可以強(qiáng)制使用UTF-16編碼。只需要在aapt中指定--utf16參數(shù),也就是指定mWantUTF16為true(下面的注釋似乎跟代碼有出入)。

" --utf16\n"

" changes default encoding for resources to UTF-16. Only useful when API\n"

" level is set to 7 or higher where the default encoding is UTF-8.\n"

微信5.2.1把minSdkVersion更改到8,使用utf8編碼后,resources.arsc減少了將近1M。

5. 指定文件的壓縮方式與7zip壓縮

安裝包是一個(gè)壓縮文件,我們可以指定里面的文件采用哪種壓縮方式。假若我們輸入下面的命令:

aapt l

參數(shù):

-v:會(huì)以table的形式輸出目錄,table的表目有:Length、Method、Size、Ratio、Date、Time、CRC-32、Name。

其中Method表示壓縮形式,有:Deflate及Stored兩種,即該Zip目錄采用的算法是壓縮模式還是存儲(chǔ)模式;可以看出resources.arsc、*.png采用存儲(chǔ)模式,而其它采用壓縮模式。

有時(shí)候我們?yōu)榱税涯硞€(gè)數(shù)據(jù)文件不壓縮,而把它的后綴名改成png(其實(shí)可以指定appt參數(shù) -0 )。其實(shí)aapt對(duì)于某些壓縮率不會(huì)太高的文件都默認(rèn)使用了Stored模式。具體如下:

/* these formats are already compressed, or don't compress well */
static const char* kNoCompressExt[] = {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv"};

文件如果采用Deflate方式存儲(chǔ),意味著通過AssetManager讀取時(shí)需要解壓,耗費(fèi)的時(shí)間與文件的壓縮比成比例。但大家記得這里面有一個(gè)坑,在Android 2.3以前的任何壓縮的資源的原始大小超過1M,AssetManger讀取時(shí)會(huì)拋出異常。這里面的Tip有:

Tips

1. 對(duì).png、.jpg強(qiáng)制壓縮,但是的確普遍壓縮率在3%-5%,收益不是特別高;

2. 假若你的resources.arsc小于1M,可以對(duì)它進(jìn)行壓縮,這邊可壓縮50-70%。需要注意的是,如果你的resources.arsc是壓縮的,程序需要把它讀到內(nèi)存,也就是會(huì)增大運(yùn)行內(nèi)存;

3. 對(duì)于安裝包中的jar可以指定不壓縮,其實(shí)二次壓縮的壓縮率非常低;

此外,7z壓縮算法號(hào)稱優(yōu)化了字典,完全兼容zip,使用7zip的最大壓縮模式比傳統(tǒng)zip方式的確有所提升,但是這里需要注意兩個(gè)問題:

上文kNoCompressExt說到的流媒體文件,是通過mediaplay直接拿文件句柄(沒有像assetmanager會(huì)先解壓文件,要求文件是seekable)也不能壓縮。

那么說一般來說只有.jpg、.jpeg、.png、.gif可以壓縮,對(duì)于不需要兼容低版本或者resources.arsc小于1M的apk,可選擇壓縮resources.arsc。

6. C++的So庫

5.1. C++運(yùn)行時(shí)庫統(tǒng)一使用stlport_shared

之前微信中的C++運(yùn)行庫大多使用靜態(tài)編譯方式,使用stlport_shared方式可減小APK包大小,相當(dāng)于把大家公有的代碼提取出來放一份,減少冗余。同時(shí)也會(huì)節(jié)省一點(diǎn)內(nèi)存,加載so的時(shí)候動(dòng)態(tài)庫只會(huì)加載一次,靜態(tài)庫則隨著so的加載被加載多份內(nèi)存映像。

5.2. 把公用的C++模塊抽成功能庫

其實(shí)與上面的思路是一致的,主要為了減少冗余模塊。大家都用到的一些基礎(chǔ)功能,應(yīng)該抽成基礎(chǔ)模塊。

7. 語言包動(dòng)態(tài)加載

由于微信是一個(gè)國際化軟件,我們?cè)赟tring中添加了20多種語言支持。這也導(dǎo)致我們的resources.arsc有5M多之巨,嘗試過假如只留下默認(rèn)的中文,resources.arsc可減少超過一半。事實(shí)上,大多數(shù)的語言我們并沒有使用到,這里提到的一個(gè)思路是動(dòng)態(tài)下發(fā)語言包,程序中繼承Resource實(shí)現(xiàn)getString方式讀取即可。

假若你的apk并不要求聯(lián)網(wǎng),要求用戶動(dòng)態(tài)下發(fā)語言包似乎不work。這里在研究buck編譯的時(shí)候看到另外一個(gè)思路,即把大部分的語言二進(jìn)制存放在assets。這個(gè)由于是普通數(shù)據(jù)文件,采用Deflate壓縮方式,會(huì)有比較大的壓縮率(與數(shù)據(jù)存儲(chǔ)方式也有關(guān)系)。

8. 資源混淆

我們常常用proguard來混淆代碼,那我們有沒有想過資源是否可以混淆?而混淆資源能帶來什么樣的好處?

建議

1. 比較酷,讓反編譯的人更加難受一下。面對(duì)一大堆以a,b,c,d命名的png、xml;

2. 由于resources.arsc需要記錄id與name的鍵值對(duì),資源混淆對(duì)減少安裝包體積也有幫助。具體數(shù)值與編碼方式、id數(shù)量、平均減少命名長度有關(guān)(其實(shí)可以自己估量一下)。

微信的資源混淆工具不依賴源碼,只要輸入一個(gè)apk就能得到一個(gè)混淆之后的apk,大家有興趣的話,可以后續(xù)單獨(dú)來講。通過資源混淆微信大約能減少1M左右的大小,效果如下:

9. 持續(xù)交付

持續(xù)交互的終極目標(biāo)就是讓使用某個(gè)功能的人才會(huì)真正去下載某個(gè)模塊,這首先需要我們代碼結(jié)構(gòu)的支持,即模塊化以及支持動(dòng)態(tài)加載。微信對(duì)gpserver、打飛機(jī)等功能試用動(dòng)態(tài)加載方式。其實(shí)這塊的核心思想就是將一些邊緣的,不常用的功能,都盡量采用這種方式加載。但需要與產(chǎn)品大大們激烈PK,用戶至上嘛。

記住,砍功能永遠(yuǎn)是減少安裝包的第一法寶!

三、安裝包是否能安裝

這里討論第二個(gè)主要問題,如何確保我們的安裝包用戶都能安裝的上,特別是2.3以下的爺們(希望有不需要考慮它們的一天)。

1. Dex的65536高壓線

原因你懂得,超過請(qǐng)減少方法數(shù),或自行拆多dex。其實(shí)在這里,我們這邊有一點(diǎn)積累,但不在這篇文章的討論范圍。具體可參考km中的一些文章:

計(jì)算某個(gè)dex的方法總數(shù),可使用:

function dex-method-count() {

cat $1 | head -c 92 | tail -c 4 | hexdump -e '1/4 "%d\n"'

}

想看到所有的方法,用dexdump吧。

2. 任何壓縮的資源的原始大小不能超過1M

具體參考減少安裝包大小tips中的第五點(diǎn)。

3. 線性內(nèi)存限制

其實(shí)這個(gè)往往才是決定安裝包是否能安裝的上的阿喀琉斯之踵(用完這個(gè)名詞,頓時(shí)高大上)。

關(guān)于線性內(nèi)存的限制,可參考文章:

https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920

簡單來說就是在dexopt過程中,系統(tǒng)限制一個(gè)5-16M的線性內(nèi)存,如果在讀取dex數(shù)據(jù)過程中超過了這塊內(nèi)存,就會(huì)出現(xiàn)dexopt失敗。

由于dalvik加載系統(tǒng)模塊時(shí)需要占用部分內(nèi)存,facebook的推薦值我們自身dex的最大值是4M,但我們發(fā)現(xiàn)在某些2.3的機(jī)器超過3.5M之后依然會(huì)dexopt失敗。微信計(jì)算線性內(nèi)存使用的是buck中asm-debug-all中提供的方法,計(jì)算class中會(huì)被dexopt中會(huì)讀入線性內(nèi)存的總大?。?/p>

(buck源碼:https://github.com/facebook/buck)

通過baksmali反編譯dex,也能得到相同的效果。在上面的那篇Facebook文章中,它們通過hack的方式更改系統(tǒng)線性內(nèi)存的值,這涉及兼容性的問題,但按它們說只在三星的某些機(jī)型需要適配,so名稱是:

(庫地址:http://asm.ow2.org/)

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

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