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