背景:
?1.為什么要做包體積優(yōu)化
在2018年的Google I/O蝌焚,Google透露了Google Play上安裝包體積與下載轉化率的關系圖。
下載轉化率:一個 100MB 的應用究珊,用戶即使點了下載,也可能因為網(wǎng)絡速度慢纵苛、突然反悔下載失敗剿涮。對于一個 10MB 的應用言津,用戶點了下載之后,在猶豫要不要下的時候已經(jīng)下載完了取试。但是正如上圖的數(shù)據(jù)悬槽,安裝包大小與轉化率的關系是非常微妙的。(10MB 跟 15MB 可能差距不大瞬浓,但是 10MB 跟 40MB 的差距還是非常明顯的)
推廣成本:一般來說初婆,包體積對渠道推廣和廠商預裝的單價會有非常大的影響。特別是廠商預裝猿棉,這主要是因為廠商留給預裝應用的總空間是有限的磅叛。如果你的包體積非常大,那就會影響廠商預裝其他應用萨赁。還有一些街邊推廣需要靠數(shù)據(jù)流量來下載的場景弊琴,更是直接影響用戶的下載意向。
2. 包體積與應用性能的關系
安裝時長:文件拷貝杖爽、Library 解壓敲董、編譯 ODEX、簽名校驗等掂林,甚至包括一些混編的工程來說臣缀,初步解壓轉化就要占據(jù)很長的時間
運行內存RAM:資源越多,加載到內存里的東西越多泻帮,占據(jù)內存越多,就容易越卡頓
物理內存ROM:對于一些32G计寇,64G用戶來說锣杂,可能裝不了幾個大型的app
二、我們大致能做些什么番宁?
簡單來說可以分為三部分:
1:業(yè)務梳理優(yōu)化元莫,隨著時間的堆積,尤其一些大型應用蝶押,業(yè)務線不斷增多踱蠢,功能不斷增多,代碼量就上去了棋电,所以要定期清理一些舊的功能茎截,不用的,效益不好的赶盔,等等企锌,各個維度來降低業(yè)務堆積。
2: 轉變開發(fā)模式于未,配合一些混編模式撕攒,h5陡鹃,小程序分擔一些不必要的需求等方式,來減少原生代碼堆積抖坪。
3:? 技術方式瘦身萍鲸,結合實際情況,打包特性等操作擦俐,來做瘦身脊阴。我們重點說一下這一部分。
三捌肴、瘦身之前先認識apk
?1蹬叭、apk是由哪幾項組成的
?大致是由
dex:代碼相關
res,assets:資源相關
lib:so相關
meta-inf:應用的簽名校驗信息相關
?2状知、分析apk的幾種方式
大致有幾種方式:
apk說到底是一個壓縮包秽五,如果實在不想使用任何工具,可以把后綴改為.zip饥悴,然后直接解壓
一個比較官方的方式apktoolapktool
使用AS坦喘,在菜單欄Build>Analyze apk ,比較清晰的看到結構和每個文件的大小
nibledroid 這個是一個比較完善的工具西设,(我沒有實踐瓣铣,感興趣的小伙伴可以去康康)
android-classyshark 是一個二進制分析工具,可以清晰看到每個包名下代碼占比
四贷揽、開始優(yōu)化
從apk結構可以看出棠笑,dex和lib是apk大小的“大頭”
先說so的優(yōu)化方式:
?so是Android上的動態(tài)鏈接庫,在我們Android應用開發(fā)過程中禽绪,有時候Java代碼不能滿足需求蓖救,比如一些 加解密算法或者音視頻編解碼功能,這個時候就必須要通過C或者是C++來實現(xiàn)印屁,之后生成So文件提供給Java層來調用循捺,在生成So文件的時候就需要考慮生成市面上不同手機CPU架構的文件。目前雄人,Android一共 支持7種不同類型的 CPU 架構从橘,比如常見的armeabi、armeabi-v7a础钠、arm64-v8a恰力、X86等等。理論上來說珍坊,對應架構的 CPU 它的執(zhí)行效率是最高的牺勾,但是這樣會導致 在 lib 目錄下會多存放了各個平臺架構的 So 文件,所以App的體積自然也就更大了阵漏。
現(xiàn)在市場支持64位驻民,32位兩種系統(tǒng)的apk包翻具,打包的時候會拆包進行打包。也是為了合理的包體積優(yōu)化考慮回还。之前是只打入64位的裆泳,64位可以兼容32位,只是效率會差點柠硕,現(xiàn)在應用市場支持分包了工禾,很不錯。
1: 常見的處理方式蝗柔,拆包闻葵,根據(jù)手機架構和位數(shù)來動態(tài)適配
2: so動態(tài)下發(fā),該方案比較徹底癣丧,對包體積優(yōu)化比較友好槽畔,但是兜底方案不好制定,會存在因為下載失敗導致的某些功能暫時不可用
dex主要優(yōu)化方式:
1: 安卓ProGuard(混淆)
提到混淆胁编,大家都想到的是打包時混淆厢钧,其實遠不止此嬉橙,混淆大概有四個作用:
1-1:壓縮(Shrink): 偵測并移除代碼中無用的類、字段市框、方法和特性 1-2:優(yōu)化(Optimize): 對字節(jié)碼進行優(yōu)化,移除無用的指令 1-3:混淆(Obfuscate): 使用a,b,c,d這樣簡短而無意義的名稱枫振,對類祥得、字段和方法進行重命名 1-4:預檢(Preverify): 在Java平臺上對處理后的代碼進行預檢
代碼混淆形式一般有三種:
1:將代碼中的各個元素蒋得,比如類乒疏、函數(shù)额衙、變量的名字改變成無意義的名字。例如將 hasValue 轉換成單 個的字母 a怕吴。這樣窍侧,反編譯閱讀的人就無法通過名字來猜測用途转绷。
2:重寫代碼中的部分邏輯,將它變成功能上等價议经,但是又難以理解的形式。比如它會改變循環(huán)的指令咧织、結構體。
3:打亂代碼的格式习绢,比如多加一些空格或刪除空格闪萄,或者將一行代碼寫成多行,將多行代碼改成一行放航。
混淆規(guī)則:
http://www.reibang.com/p/d5ee2f1f977f
ps:混淆本來就是為了節(jié)約字節(jié)碼站位和反編譯風險为迈,所以盡量不要keep *,這樣就等于無效混淆了搜锰。
優(yōu)化(dontoptimize)-dontoptimize關閉優(yōu)化
壓縮(dontshrink)- dontshrink關閉壓縮
混淆(dontobfuscate)-dontobfuscate 關閉混淆
這里多說一下:
混淆非常重要大家都已經(jīng)知道了耿战,那么細看一下gradle中的配置,其中有一句是:
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'mpProguard.cfg'
x0; 大家有沒有想過proguard-rules.pro這個是自己的文件狈涮,那么getDefaultProguardFile是什么,它在哪里歌馍?它的作用是什么晕鹊?
其實該getDefaultProguardFile文件所在目錄是在android/sdk/tools/proguard/溅话,是Android sdk 為我們提供的一些Android內置的一些混淆規(guī)則,可以理解為是通用配置飞几。當你沒有額外定義混淆規(guī)則時屑墨,就按通用混淆規(guī)則進行混淆纷铣。
其次說一下mapping文件灿里,這個文件也非常重要,這個文件是混淆后的輸出文件儒拂,如果遇到崩潰情況資源找不到情況色鸳,可以由該文件找到資源和混淆后字母的對應關系:
這里有個小tips:如果一個類沒有被引用命雀,是不會參與混淆的。mapping里找不到映射關系吏砂。
像我們這種大體量的app狐血,光mapping文件就一百多M了
2:andResGuard 資源混淆
原生安卓的混淆主要是針對于代碼,而工具AndResGuard主要是針對資源
x0; 3:第三方庫
第三方庫浪默,隨著項目逐漸發(fā)展缀匕,引入的第三方庫越來越多,這時候需要排查是否有功能相同的第三方庫阔加,比如圖片的第三方庫满钟,比較流行的有glide,F(xiàn)resco等,還有處理Json的也容易重復厌蔽,所以我們需要排查第三方庫奴饮,避免引入相同功能的第三方庫择浊。其次逾条,對于一些比較大的第三方,功能比較齊全担孔,有時候我們可能就只用它的一部分功能吃警,可以進行源碼依賴酌心,二次代碼剝離,但是同時也要面對版本管理問題安券。
4:移除無用代碼侯勉,無用類,無用資源
對于刪除代碼通常有兩個問題:
1: 業(yè)務代碼越來越多
2: 代碼越多越不敢刪
其實如果可以確定無用了盛龄,也就敢刪除了芳誓,所以如何確定無用就非常重要:
1:業(yè)務層面,產品直接說匿值,確定是功能下線赂摆,關聯(lián)代碼直接刪除
2:技術手段篩選無用代碼:?
2-1: 統(tǒng)計可以打點的所有page,撈取apk里所有page绊谭,取差值汪拥,就是真正的無用頁面
2-2: Lint掃描, 宪赶,掃描無用資源搂妻, 掃描無用代碼,Lint的弊端是不完全準確邓厕,因為lint的無用資源和無用代碼是割裂開進行掃描的岛蚤,尤其針對無用代碼中引用的無用資源,一般情況下掃描不到单雾,所以需要反復掃描多次她紫。我個人很少遇到誤刪的情況,但是還是需要反復確認一下比較安全渐逃。
2-3: 有一些算法民褂,加載文件處理成String赊堪,掃描文件名是否在其他文件中引用,算法也是同樣有弊端哭廉。
3: 低pv頁面轉web遵绰,低pv,參考業(yè)務埋點數(shù)據(jù)統(tǒng)計乌企。
ps: 工具simian掃描重復代碼的成玫,感興趣的小伙伴可以看一下。
額外:對于代碼編碼方向虽画,盡量少用枚舉 荣病,不要拿google源碼中也有大量枚舉來杠个盆,每減少一個 enum 可以減少大約 1.0 到 1.4 KB 的大小,這個是已經(jīng)被論證了的柴梆。所以盡量少用终惑。
5: 資源文件少配置
語言包只配置英文
圖片只載入一套drawable-xxhdpi
6: 打包再次壓縮(滴滴插件)
?滴滴的出的bosster 是針對打包過程中二次資源整理雹有,重復資源去重,壓縮溜宽,整理resources.arsc文件等處理的比較徹底适揉,添了google對于無用資源處理方式(不移除用一個空白文件替代)
像這個體量的app煤惩,用booster保守估計可以再壓縮5-10M左右盟庞,還是比較可觀的。
7:圖片優(yōu)化
在一些大型app中票彪,圖片資源也是重中之重不狮,所以控制好圖片資源也對包體積優(yōu)化有不可或缺的貢獻。
圖片優(yōu)化大概分幾個方向:
UI層面:盡量使用原生自帶的樣式推掸,或者盡可能統(tǒng)一UI樣式,一些圖片close登渣,back等常用圖片能統(tǒng)一使用
圖片壓縮胜茧,導入包內前用壓縮工具處理壓縮
安卓原生盡量轉webp圖片使用仇味,這里需要注意不是每個png轉webp后都會體積下降,注意下即可
一些純色icon盡量用矢量圖VectorDrawable廊遍,VectorDrawable》webp》png》jpg
8:避免產生Java access方法
java access方法是贩挣,為了提供內部類和外部類直接調用掉對方私有成員變量揽惹,又不違反java的封裝性規(guī)范,Java在編譯時期自動會生成 package 可見性的靜態(tài)方法狭握。因為我們沒有引入ASM疯溺,加上該優(yōu)化方式收益并不是很大囱嫩,所以大家了解一下就好了。
https://mp.weixin.qq.com/s/ZHisCVjO_ZrtvvEWBYUQFQ
9:插件化
插件化開發(fā)能有效的大幅度降低包體積今妄,一些獨立的模塊變?yōu)椴寮_發(fā)鸳碧,從服務器拉取下來家加載,能有效的降低包體積腾仅。
build目錄下推励,這里每個目錄可以結合上一篇gradle里面task來看,這里就是每個task后輸出的對應目錄對應的內容稿黄,最后根據(jù)這些內容進行打包跌造。
參考文檔:
https://time.geekbang.org/column/article/81483
http://t.zoukankan.com/sihaixuan-p-10978222.html
此篇文章由于脫敏問題鼻听,缺失配圖联四。看個大概吧醉拓。