1.概述
在 2018 年的 Google I/O,Google 透露Google Play 上安裝包體積與下載轉(zhuǎn)化率的關(guān)系如下圖:從圖中我們可以看出來隨著apk的體積增大醉箕,轉(zhuǎn)化率逐漸下降羽德。而包體對應(yīng)用的影響主要有以下幾點(diǎn):
- 下載轉(zhuǎn)化率:一個(gè)近100M的應(yīng)用用戶在點(diǎn)擊下載之后,因?yàn)榫W(wǎng)速過慢神郊,用戶會在猶豫的過程中取消下載肴裙,而一個(gè)10M的應(yīng)用在用戶猶豫的過程中卻已經(jīng)下完了。
- 推廣成本:
- 應(yīng)用的預(yù)裝對單個(gè)apk的體積有限制涌乳,apk的體積會影響到應(yīng)用的單價(jià)蜻懦。
- 一些移動(dòng)開發(fā)者會把a(bǔ)pk置于官網(wǎng)下載,體積過大不僅使得用戶浪費(fèi)流量夕晓,也使得開發(fā)者服務(wù)器流量浪費(fèi)宛乃。
- 應(yīng)用市場:
蘋果已經(jīng)限制當(dāng)應(yīng)用體積大于150M的時(shí)候只能WIFI下載,而谷歌市場要求當(dāng)應(yīng)用apk大于100M的時(shí)候使用 apk擴(kuò)展文件上傳(可參照之前的文章了解)运授,此也為應(yīng)用商店對帶寬壓力的一方面考慮烤惊。
在現(xiàn)如今眾多的超級app隨著業(yè)務(wù)的不但增加,需求的不但演進(jìn)apk體積在不斷增加吁朦,apk體積優(yōu)化閑得尤為重要柒室。google play上有一款應(yīng)用via瀏覽器,因?yàn)槠鋺?yīng)用體積小逗宜,功能相對齊全雄右。優(yōu)勢明顯比其他同類品牌的產(chǎn)品強(qiáng)出不是零星半點(diǎn)空骚。
2. apk打包流程
既然是優(yōu)化apk體積,追本溯源擂仍,當(dāng)然是從apk產(chǎn)生的過程來分析囤屹,在網(wǎng)上我們可以查到apk的組成主要由資源,代碼和第三方庫逢渔,如 so 庫等部分組成肋坚。開發(fā)過中主要是通過ant、gradle等方式編譯生成安裝包肃廓,具體流程如下:有此圖我們大致可以看出apk體積的優(yōu)化我們可以從resources氯葬,dex和其他資源庫三個(gè)方向去優(yōu)化容诬。將apk解壓或者通過Android Studio等工具來解壓市俊,主要關(guān)注以下幾個(gè)方面:
- dex個(gè)數(shù)和每個(gè)dex方法數(shù)的情況
- 沒有alpha通道的png圖惊窖,可壓縮成jpg減少體積;
- 超過一定數(shù)值的大文件哀蘑,特別是圖片資源可采用有損壓縮诚卸;
- 新增文件、減少文件绘迁,文件大小發(fā)生變化的情況合溺;
下面我們從上述流程中提到的幾個(gè)方面去分析apk瘦身的方法。
3. Resources優(yōu)化
Android資源管理框架實(shí)際是由AssetManager和Resources兩個(gè)類來實(shí)現(xiàn)的脊髓。其中辫愉,Resources類可以根據(jù)ID來查找資源,而AssetManager類根據(jù)文件名來查找資源将硝。事實(shí)上恭朗,如果一個(gè)資源ID對應(yīng)的是一個(gè)文件,那么Resources類是先根據(jù)ID來找到資源文件名稱依疼,然后再將該文件名稱交給AssetManager類來打開對應(yīng)的文件的痰腮。而Resources是通過resources.arsc把Resource的ID轉(zhuǎn)化成資源文件的名稱,然后交由AssetManager來加載的律罢。關(guān)于resources.arsc的格式可以在Android源碼“/frameworks/base/include/androidfw/ResourceType.h”下查看膀值。
- 刪除無用資源:
在不停的迭代中,或多或少會出現(xiàn)無用的資源误辑,包括但不限于xml沧踏、png、id巾钉、string翘狱。查找無用資源主要使用lint的UnusedResources以及UnusedIds兩個(gè)檢查規(guī)則,但是針對多l(xiāng)ibrary結(jié)構(gòu)砰苍,官方的lint在某些方面不符合我們的要求潦匈,所以我們修改了一些地方阱高。對于使用gradle的編譯的項(xiàng)目我們可以使用以下方式打包:
android {
...
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
- 圖片處理
- 對于體積特別大(超過50k)的圖片資源可以考慮有損壓縮,jpg采用優(yōu)圖壓縮茬缩,png嘗試采用pngquant壓縮赤惊,輸出視覺判斷是否可行;
- 對assets中的圖片資源也使用aapt的crunch做圖片預(yù)處理凰锡;
- crunch有可能會使圖片變大未舟,在這種情況,我們可以替換成原圖掂为。需要注意的是對于.9.png处面,由于crunch過程中去除了黑邊,所以不能替換菩掏;
- 對于沒有透明區(qū)域的png圖片,可以轉(zhuǎn)成jpg格式昵济。
- 采用如webp智绸,svg等格式文件。
關(guān)于圖片壓縮可以采用一些第三方的網(wǎng)站壓縮如熊貓推薦一個(gè)自己寫的腳本ImageProcess通過該網(wǎng)站批量壓縮圖片访忿。
- 字符編碼
resources.arsc中的會有一個(gè)去重過的字符串資源池(相同的兩個(gè)字符串其實(shí)用的是同一份)瞧栗,每個(gè)String使用偏移值來獲取資源池中的數(shù)值。ResourceTable的編碼對于resources.arsc的體積有很大影響海铆。這樣的好處就是處理純英文等直接通過ascii存儲語言的國家資源文件將會更小迹恐,而對于中文、日文這些國家的資源文件有可能會變大卧斟。 - 指定文件的壓縮方式與7zip壓縮
安裝包是一個(gè)壓縮文件殴边,我們可以指定里面的文件采用哪種壓縮方式,比如一些文件我們可以采用一些極致壓縮到最小珍语,然后安裝成功后再解壓锤岸,或者安裝完成后從服務(wù)器下載再解壓,但此舉會對啟動(dòng)速度有一定的影響板乙。 - 語言包動(dòng)態(tài)加載
- 資源混淆是偷,我們常常用proguard來混淆代碼,同樣資源也是可以混淆的募逞,會生成a,b,c命名的png 和xml蛋铆,讓反編譯更加難受,不過這其中需要注意資源映射問題放接。
- 產(chǎn)品做減法刺啦。
4. Dex 優(yōu)化
對于大部分應(yīng)用來說Dex的個(gè)數(shù)和Dex的體積是整個(gè)應(yīng)用的大部頭,因此Dex的優(yōu)化在對包體優(yōu)化起到了關(guān)鍵作用透乾,主要有以下幾個(gè)部分:
- ProGuard 混淆
你可以通過下面的方法輸出 ProGuard 的最終配置洪燥,需要注意各種的 keep *磕秤,很多情況下我們只需要 keep某個(gè)包名,方法名捧韵,類名即可市咆。
-printconfiguration configuration.txt
但我們可以加大力度混淆,比如對非export的四大組件和View進(jìn)行混淆再来,但我們要注意以下兩個(gè)方面的問題
xml
清單文件中的xml需要同步修改
代碼替換
代碼中不可出現(xiàn)運(yùn)算拼接出來的類名
// 情況一:變量
public String activityName = "com.sample.TestActivity";
// 情況二:方法體
startActivity(new Intent(this, "com.sample.TestActivity"));
// 情況三:通過運(yùn)算得到蒙兰,不支持
startActivity(new Intent(this, "com.sample" + ".TestActivity"));
餓了么曾經(jīng)實(shí)現(xiàn)過一款混淆四大組件的庫Mess,具備一定的參考價(jià)值。
Android Studio 3.0推出了Android新Dex編譯器D8與新混淆工具R8可以在編譯過程中生成的dex大概減少3%芒篷,但此功能還在測試階段搜变。
- Dex 分包
正如我們所知同一個(gè)應(yīng)用可能會存在多個(gè)Dex包,每個(gè)Dex 包的也會存在相互之間的引用针炉,因此在Dex的分包規(guī)則上我們同樣具備優(yōu)化的方法挠他,比如避免Dex包之間的冗余和Dex包生成的算法優(yōu)化。具體的我們需要先了解Dex文件的格式和生成規(guī)則篡帕,其中主要是優(yōu)化Dex的格式中debugItems模塊殖侵。
5. 其他資源優(yōu)化
一個(gè)應(yīng)用除了Java代碼,resource等資源之外镰烧,還存在一個(gè)Native library等相關(guān)資源拢军。這塊主要是針對一些Native 資源進(jìn)行優(yōu)化。
- 減少冗余
例如一些so庫中C++運(yùn)行庫大多使用靜態(tài)編譯方式怔鳖,使用stlport_shared方式可減小APK包大小茉唉,相當(dāng)于把大家公有的代碼提取出來放一份,減少冗余结执。同時(shí)也會節(jié)省一點(diǎn)內(nèi)存度陆,加載so的時(shí)候動(dòng)態(tài)庫只會加載一次,靜態(tài)庫則隨著so的加載被加載多份內(nèi)存映像昌犹。主要為了減少冗余模塊坚芜。大家都用到的一些基礎(chǔ)功能,應(yīng)該抽成基礎(chǔ)模塊斜姥。 - so庫壓縮
我們可以針對一些so庫進(jìn)行特殊壓縮鸿竖,需要時(shí)解壓,甚至可以從網(wǎng)絡(luò)獲取到本地進(jìn)行加載铸敏。
以上為對apk體積優(yōu)化的一些總結(jié)缚忧,當(dāng)然會還有一些其他的可以優(yōu)化的點(diǎn),歡迎大家補(bǔ)充杈笔。
針對資源壓縮主要是目前瀏覽器引用微信的AndResGuard有著明顯的效果闪水。
參考
極客時(shí)間Android開發(fā)高手課
Dex文件格式詳解
美團(tuán)Android App包瘦身優(yōu)化實(shí)踐
支付寶Android 包大小極致壓縮
微信Android安裝包相關(guān)知識匯總
FaceBook Redex