.dex文件更小并且擁有更好的語言用方運(yùn)行時(shí)性能
包含java 8語言支持的處理
使用方式(AS3.1默認(rèn))
gradle.properties android.enableD8=true
R8
和混淆類似,使用隨機(jī)無意義的支持名稱
內(nèi)聯(lián),避免對象分配
在刪除未使用的理使類、字段、語言用方防范上更好
與相比,支持R8能使apk減小約10%,理使體積更小
使用方式
android.enableR8=trueandroid.enableR8.libraries=true
d、語言用方移除無用代碼
隨著業(yè)務(wù)增加,支持許多之前使用的理使類可能并沒有用到,然后又想著以后萬一又能用到呢?反正就一個(gè)文件,語言用方刪不刪除無所謂,支持占內(nèi)存又不大,理使長此以往,語言用方apk就越來越大了。支持
我平時(shí)都用 自帶的理使 Lint 檢測工具無效代碼
慕課網(wǎng)的性能優(yōu)化視頻里講到過一種方案,通過(簡單入門)對某個(gè)包下的類就行構(gòu)造函數(shù)切面
@After("execution(org.jay.launchstarter.Task.new(..)")public void newObject(JoinPoint point) { LogHelper.i(" new " + point.getTarget().getClass().getSimpleName());}
感覺這個(gè)確實(shí)能打印出來使用的類,可以搜集起來,那還得寫一個(gè)腳本找出沒使用得類,刪除,有點(diǎn)風(fēng)險(xiǎn)。
我覺得還是提出的方案簡單有效,通過 插件 基于線上用戶上報(bào)無用代碼分析
2、資源瘦身
首先我們了解一下APK解壓后,資源相關(guān)的目錄
a、資源混淆
混淆優(yōu)點(diǎn)
采用方案 微信的
b、無用資源刪除
android{ .... buildTypes{ release{ shrinkResources true minifyEnable true } }}
一般我們都會(huì)在build.里面配置上述代碼,并通過 自帶的Lint檢測工具 進(jìn)行 刪除無用資源,這里注意,筆者操作過程中,發(fā)現(xiàn)用Lint檢測出來的無用資源,很多其實(shí)都引用了的。
注意,這里配置 true 是真的將無用資源去除了嗎?答案:不是的
理由有兩點(diǎn)
綜上,所以說系統(tǒng)目前的這種做法并沒有真正減少文件數(shù)量,.arsc、簽名信息、以及ZIP文件信息依然沒有改善。
為什么系統(tǒng)不做刪除呢?因?yàn)?arsc和R文件的資源ID默認(rèn)是連續(xù)的,刪除無用資源文件,資源ID會(huì)改變,這個(gè)時(shí)候代碼中已經(jīng)替換過的ID就會(huì)出現(xiàn)資源找不到的情況,所以只是替換成了空文件。
思路方案:刪除文件,同時(shí)keep住R文件和.arsc中的ID
還有一種,利用 - 分析,具體可以查看
c、圖片壓縮
這個(gè)比較好實(shí)現(xiàn),筆者將項(xiàng)目中大于10k的圖片都壓縮了一下,體積減小了3%,筆記使用了cwebp 工具,轉(zhuǎn)換成webp
# coding=utf-8import osfrom pathlib import Path?'''file_directory : 目錄modules : 變長參數(shù) 指定模塊才解析'''??def findAndConvert(file_directory, *modules): if len(modules) == 0: print("modules must Designated") return if file_directory.is_file(): print("this is a file , need a directory") return if os.path.exists(file_directory): print("has this directory") for homes, dirs, files in os.walk(file_directory): # print(homes) # print(dirs) # print(files) for module in list(modules): check_path = os.path.join(file_directory.absolute(), module)? if check_path in homes: # print("============== convert this path " + check_path + " ================== ") # 尋找 src/main/res/darwable開頭的文件 # 或者直接找png jpg圖片 if "src\\main\\res" in homes and "drawable" in homes: # print(homes) for file in files: if file.endswith(".png") or file.endswith(".jpg"): file_path = Path(os.path.join(homes, file)) if file_path.is_file() and os.path.getsize(file_path.absolute()) >10 * 1024: # print(file) # print(file[:-4]) current_file = os.path.join(homes, file) target_file = os.path.join(homes, file[:-4] + ".webp") cmd = "cwebp " + current_file + " -o " + target_file result = os.system(cmd) if result == 0: # os.remove(file_path.absolute()) os.system("svn add " + target_file) os.system("svn del " + current_file) # else: # print("please check this path " + check_path)?? else: print("this directory has not exists")??if __name__ == '__main__': path = Path("D:/WorkSVN/項(xiàng)目Prject/") # 指定包名 變長參數(shù)必填 findAndConvert(path, "模塊名","模塊名2")
d、資源文件規(guī)范化
3、SO瘦身
這個(gè)不多說了,直接上代碼
defaultConfig { ndk { abiFilters "armeabi" }}
現(xiàn)在市面上大多都是架構(gòu),例如,微信APK分析一下,也能發(fā)現(xiàn)只有,雖然微信對加載做了CPU 架構(gòu)的適配,什么時(shí)候加載的哪個(gè)so文件,都有適配。
詳細(xì)可以了解 為何大廠APP如微信、支付寶、淘寶、手Q等只適配了-v7a/?
這里給出一個(gè)abi工作規(guī)則圖
從圖中可以得出另一個(gè)規(guī)則
三、進(jìn)階瘦身方案1、代碼瘦身
a、三方庫處理
其實(shí)這個(gè)都不能算做進(jìn)階瘦身方案,但在實(shí)際操作中,我確實(shí)沒有想到對三方庫進(jìn)行處理,導(dǎo)致沒必要的依賴加載進(jìn)來的,畢竟將所有模塊代碼看一遍,很多模塊不是自己寫的,優(yōu)化起來丟三落四的,所以放到進(jìn)階方案里了。
在選擇第三方庫時(shí),需要評審一下,以性能為第一指標(biāo),包體積為第二指標(biāo),盡量選擇性能好,體積小的第三方SDK,并且在使用到第三方SDK某個(gè)功能時(shí),盡量繼承某個(gè)功能依賴,而不是全部,例如只需要的某個(gè)webp功能,那就只集成webp依賴就好了。
我按照方案,將項(xiàng)目里所有依賴都過了一遍,發(fā)現(xiàn)圖片相關(guān)Glide和都使用了,GG,并不能選擇一個(gè)用,改動(dòng)太大,涉及測試也很多,只能將這種方案在組內(nèi)會(huì)議規(guī)范一下。(PS:加載長圖會(huì)顯示空白的)
b、去除debug信息與行號信息
dex文件結(jié)構(gòu)圖(摘自極客時(shí)間開發(fā)高手課包體積優(yōu)化)
一般,我們都會(huì)在混淆配置中以下面的方式保留行號信息
-keepattributes SourceFile, LineNumberTable
如果不保留行號信息,dex大約可以減少5%的體積,但是Crash上報(bào)后,會(huì)拿不到行號怎么辦?問題該怎么定位?同學(xué),莫慌,支付寶 包大小極致壓縮方案 幫你解決。
當(dāng)然,支付寶這個(gè)方案是參考的的 ReDex ,這個(gè)只支持linux、和mac。因?yàn)槲抑傲私膺^ReDex,所以在接到壓縮體積包時(shí),也調(diào)研過,可是編譯階段硬是沒有編譯過去,后面試了兩次,還是有點(diǎn)問題,就沒有使用該方案。
ReDex 有六個(gè)優(yōu)點(diǎn)
c、Dex分包優(yōu)化
上面講到了ReDex的一個(gè)優(yōu)點(diǎn),基于反饋的Class字節(jié)碼布局,這是啥意思呢?
類字節(jié)碼在單個(gè) Dex 中的布局是根據(jù)編譯順序而不是運(yùn)行時(shí)行為決定,即便 會(huì)將 App 定義的組件以及部分啟動(dòng)需要的類放在 Main Dex 中,但依然不是根據(jù)運(yùn)行時(shí)順序布局,這會(huì)導(dǎo)致程序啟動(dòng)時(shí)需要查找隨機(jī)分布在 Dex 中的類。
ReDex 會(huì)將 APK 在 Lab 中試運(yùn)行,并跟蹤啟動(dòng)時(shí)哪些類需要加載,然后將這些類字節(jié)碼放到 Dex 前部,減少啟動(dòng)時(shí)從閃存中尋找類的時(shí)間,從而提高 App 啟動(dòng)速度。
好的,大致了解了,那你可能會(huì)問,這個(gè)跟Dex分包優(yōu)化有半毛錢關(guān)系?
在我們使用分包后,此時(shí)的每一個(gè)Dex可能會(huì)調(diào)用到其它的Dex中的方法,這種跨Dex調(diào)用的方式會(huì)造成許多冗余信息,具體有兩點(diǎn)
為了保證Dex有效率在80%以上,通過以下公式來衡量優(yōu)化效果
Dex 信息有效率 = define methods數(shù)量 / reference methods 數(shù)量
為了實(shí)現(xiàn)上述所說的信息冗余去除,ReDex配置一下就可以
{ "redex" : { "passes" : [ "InterDexPass", "RegAllocPass" ] }, "InterDexPass" : { "minimize_cross_dex_refs": true, "minimize_cross_dex_refs_method_ref_weight": 100, "minimize_cross_dex_refs_field_ref_weight": 90, "minimize_cross_dex_refs_type_ref_weight": 100, "minimize_cross_dex_refs_string_ref_weight": 90 }, "RegAllocPass" : { "live_range_splitting": false }, "string_sort_mode" : "class_order", "bytecode_sort_mode" : "class_order"}
d、Dex壓縮
你可以分析一下 APP的dex文件,他把真正的代碼放到了下面,導(dǎo)致只有一個(gè)700多kb的.dex,他通過XZ Utils將所有dex壓縮成了一個(gè)。
XZ 壓縮算法 和 7-Zip 一樣,內(nèi)部使用的都是 LZMA 算法。對于 Dex 格式來說,XZ 的壓縮率可以比 Zip 高 30% 左右。但是這套方案存在一些問題
上面的講解摘自極客時(shí)間開發(fā)高手課,這里僅當(dāng)了解了一下,因?yàn)檫€需要分版本適配,臣妾做不到,難度對于目前的我來說很高。
2、資源瘦身
真正去除無用資源
利用 - 分析,具體可以查看
重復(fù)資源優(yōu)化
這個(gè)在項(xiàng)目開始時(shí)就需要制定好規(guī)范,但是也避免不了,多個(gè)地方用到的圖片或資源存放在不同的模塊下,畢竟這是團(tuán)隊(duì)開發(fā)。這個(gè)問題在任務(wù)開展過程中,確實(shí)不好搞,因?yàn)榇嬖谔嘞嗤址Q不同,文案一樣,然后圖片又有相同的,命名又不一樣,難搞。
具體實(shí)施方案查看
3、SO瘦身
a、對的壓縮與合并、裁剪
跟Dex一樣,可以通過XZ Utils壓縮
在默認(rèn)的lib目錄中,我們只需要加載少數(shù)啟動(dòng)過程相關(guān)的 ,其它放在首次啟動(dòng)時(shí)解壓。對于壓縮率來說,可以比ZIP壓縮高30%,效果很好。缺點(diǎn)和Dex類似
實(shí)施方案,配合采用的一個(gè)開源庫
合并
在 4.3 之前,進(jìn)程加載的數(shù)量是由限制的,可以點(diǎn)擊查看
然后又出來方案了 和 Demo
裁剪
又是的方案
原理:分析代碼中的JNI方法以及不同方法調(diào)用,找到?jīng)]有無用的導(dǎo)出,然后刪除掉,這樣在編譯的時(shí)候也會(huì)把對應(yīng)的無用代碼同時(shí)刪除掉。
四、長效治理APK包體積
筆記一完結(jié)