前言
包大小的重要性已經不需要多說,包大小直接影響用戶的下載,留存,甚至部分廠商預裝強制要求必須小于一定的值,但是隨著業務的迭代開發,應用會越來越大,安裝包會不停的膨脹,因此包大小縮減是一個長期持續的治理程序,
- 提升下載轉化率,安裝包越小,轉化率越高,
- 降低渠道推廣成本,
- 降低安裝時間,檔案拷貝、Library解壓、編譯ODEX、簽名校驗這些,包體積越大越耗時,
- 降低運行時記憶體等等,
環境
- Android Studio Arctic Fox | 2020.3.1 Patch 2
- AGP 7.0
- 專案地址:wanandroid_jetpack
優化前

4.7MB,4.2MB是google play下載的大小,會有壓縮,
除了AS自帶的Analyzer之外,還有ApkChecker、ClassyShark等工具,
APK的組成
| 檔案 | 描述 |
|---|---|
| lib | so檔案,不同的cpu架構 |
| res | 編譯后的資源檔案,drawable、layout等 |
| assets | 應用程式的資源、字體、音頻檔案等 |
| classes(n).dex | dx編譯后的java檔案 |
| META-INF | 簽名資訊相關 |
| resources.arsc | 二進制資源檔案 |
| kotlin | 編譯后的kotlin檔案 |
| AndroidManifest.xml | 清單檔案 |
APK構建流程

這是官方新版的打包流程,雖然省略了一些步驟,但是大致的流程還是比較清晰的,
再次簡化一下:
資源檔案、Java檔案 > dex檔案 > APK
優化思路
APK本質是一個壓縮檔案,是打包后的產物,那可以作為切入點的階段就是打包前、以及打包中,
- 打包前,即減少打包的檔案,比如無用的資源、代碼;
- 打包中,對打包中的產物進行壓縮,比如資源檔案、So檔案;
關鍵詞:減少、壓縮,
常規操作
1.Lint檢測無用資源檔案
Analyze > Run Inspection by Name > Unused resources


檢測結果:

確定無用洗掉即可,
注意:
因為lint是本地靜態掃描,所以動態參考的資源檔案并不會識別出來,也會出現在檢測串列里,
2.Lint檢測代碼
Analyze > Inspect code

檢測結果:

因為這個專案是用kotlin寫的,所以直接看kotlin目錄下的檢測結果,
注意:
因為lint是本地靜態掃描,所以反射、動態參考的class并不會識別出來,也會出現在檢測串列里,
3.圖片壓縮
推薦使用tinypng在線壓縮,
4.TinyPngPlugin
手動壓縮畢竟不高效,可以使用TinyPngPlugin一鍵壓縮,
plugins搜索TinyPng安裝即可,(新版AS安裝完plugin已經不需要重啟了)
壓縮結果:

9張圖片,可以看到效果還是非常可觀的,
如果圖片多,效果更加明顯,
經過上面的操作,包體積減小4%,這還只是一個4.7MB的APK而已,
5.WebP
那這9張圖還能繼續優化嗎?
可以,WebP格式的體積更小,而已AS也提供了一鍵轉換支持,

以ic_avatar.png為例:
| ic_avatar.png | 優化后 |
|---|---|
| 原始大小 | 113.09KB |
| TingPng壓縮 | 36.85KB |
| WebP | 8.66KB |
可以看到,轉WebP之后,較原始大小減少了近93%,恐怖如斯~
6.開啟混淆
minifyEnabled true,默認啟用R8代碼縮減功能,
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
慎用R8,因為:
R8 會忽略試圖修改默認優化行為的所有 ProGuard 規則,例如 -optimizations 和 - optimizationpasses,
可以開啟混淆,而不使用R8,
android.enableR8=false
android.enableR8.libraries=false
混淆參考:Android混淆從入門到精通
7.縮減資源
shrinkResources true
假如有一些資源檔案不確定還用不用,也不敢刪,或者不確定需求是否會變更,所以先留著,那這種情況怎么辦呢?
可以使用shrinkResources來縮減資源,
buildTypes {
debug {
minifyEnabled false
}
release {
shrinkResources true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
要配合混淆minifyEnabled一起使用才行,原理也很簡單,代碼移除之后,參考的資源也就變成無用資源了,才可以進一步縮減,
8.so檔案縮減
比如集成了一個三方的直播或者瀏覽器,可能會提供很多so檔案,起初可能是一股腦的copy進專案,但并不一定都用的到,
比如各種cpu架構的so:
app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── arm64-v8a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── x86/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
└── x86_64/
├── libgameengine.so
├── libothercode.so
└── libvideocodec.so
目前市面上的手機cpu都是arm架構的,所以保留arm的一種即可(定制的除外),armeabi-v7a或armeabi都可,其他直接洗掉,
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a'
}
}
}
如果開發需要模擬器除錯,就加上x86的架構,正式包記得去掉,或者在local.properties中用變數控制一下,
這塊如果之前沒有優化過,而又有很多so檔案的話,或許可以減少30%以上,恐怖如斯!
9.移除未使用的備用資源

很多出海的應用會做國際化,但也適配不了這么多的語言,
除了自己app的之外,還有一些官方的、三方的,可以統一配置支持的語言,
defaultConfig {
resConfigs("en","zh","zh-rCN")
}
資源檔案同理
defaultConfig {
resConfigs("xxhdpi","xxxhdpi")
}
10.小結
針對上面的操作做個小結,看看目前效果如何,

2MB,包體積減少57%,恐怖如斯!
如果是大型專案,收益非常可觀,
author:yechaoa
進階操作
上面只是一些常規操作,下面看一些進階操作,
1.resources.arsc資源混淆
資源混淆就是將原本冗長的資源路徑變短,例如將res/drawable/wechat變為r/d/a,
開源工具AndResGuard,
2.移除無用的三方庫
引入之后未使用的,或者是功能下架之后未移除的,
3.功能重復的三方庫整合
比如glide和picasso,都是圖片庫,保留其一即可,
4.ReDex
dex檔案是打包中的產物,redex是facebook開源的分包優化方案,
可以參考:ReDex,
5.so動態加載
前面已經做了so檔案縮減,但是可能so檔案占比還是比較大,可以考慮除了首次啟動外的so檔案做動態下發,
也就是插件化的思想,按需加載,但是收益很大的同時,風險也很大,有很多case需要考慮到,比如下載時機、網路環境、執行緒行程,加載失敗是否有降級策略等等,
可以參考facebook開源的SoLoader,
6.插件化
按需加載,收益越大風險越大,風險同上,
極致操作
那如果我想做到極致,還有哪些騷操作呢,ok,繼續,
1.原生改用H5或小程式等方案
有些功能可能原生做就顯得太重,比如各種促銷活動,需要加載各種大圖,原生既重又不夠動態化,這個時候H5是一種很好的替代方案,
但是如果你原本就不支持H5或者小程式的話,接入這種能力可能反而會加大包體積,做好對比,
2.砍功能
有些功能可能想的很美好,但上線之后收益并不大,是否需要重新思考價值點,最好找到資料依托,再跟產品打架,
3.修改三方庫的原始碼,不需要的代碼剔除
比如引入了一個功能很齊全的三方庫utils,但實際只用到幾個,對原始碼進行抽取也能減少包體積,同時還能減少網路下載的編譯時間,
弊端就是升級成本較大,
4.圖片網路化
即把圖片上傳到服務器,通過動態下載的方式減少包體積,弊端就是首次加載的時候依賴網路環境,對加載速度、流量需要做一個平衡,
圖片可以預加載,但是流量消耗是無法避免了,如果比較在意流量指標,需要權衡了,
5.DebugItem
DebugItem 里面主要包含兩種資訊:
- 除錯的資訊,函式的引數變數和所有的區域變數,
- 排查問題的資訊,所有的指令集行號和源檔案行號的對應關系,
去除debug資訊與行號資訊,如果不是極致,不推薦,
可以參考支付寶的這篇 支付寶 App 構建優化決議:Android 包大小極致壓縮,
6.R Field行內
行內R Field可以解決R Field過多導致MultiDex 65536的問題,而這一步驟對代碼瘦身能夠起到明顯的效果,
美團代碼片段:
ctBehaviors.each { CtBehavior ctBehavior ->
if (!ctBehavior.isEmpty()) {
try {
ctBehavior.instrument(new ExprEditor() {
@Override
public void edit(FieldAccess f) {
try {
def fieldClassName = JavassistUtils.getClassNameFromCtClass(f.getCtClass())
if (shouldInlineRField(className, fieldClassName) && f.isReader()) {
def temp = fieldClassName.substring(fieldClassName.indexOf(ANDROID_RESOURCE_R_FLAG) + ANDROID_RESOURCE_R_FLAG.length())
def fieldName = f.fieldName
def key = "${temp}.${fieldName}"
if (resourceSymbols.containsKey(key)) {
Object obj = resourceSymbols.get(key)
try {
if (obj instanceof Integer) {
int value = ((Integer) obj).intValue()
f.replace("\$_=${value};")
} else if (obj instanceof Integer[]) {
def obj2 = ((Integer[]) obj)
StringBuilder stringBuilder = new StringBuilder()
for (int index = 0; index < obj2.length; ++index) {
stringBuilder.append(obj2[index].intValue())
if (index != obj2.length - 1) {
stringBuilder.append(",")
}
}
f.replace("\$_ = new int[]{${stringBuilder.toString()}};")
} else {
throw new GradleException("Unknown ResourceSymbols Type!")
}
} catch (NotFoundException e) {
throw new GradleException(e.message)
} catch (CannotCompileException e) {
throw new GradleException(e.message)
}
} else {
throw new GradleException("******** InlineRFieldTask unprocessed ${className}, ${fieldClassName}, ${f.fieldName}, ${key}")
}
}
} catch (NotFoundException e) {
}
}
})
} catch (CannotCompileException e) {
}
}
}
同時可以參考位元組開源的shrink-r-plugin,還有滴滴開源的booster,
7.圖片著色器
針對同圖不同色的處理,可以使用tint,比如原本是一個黑色的回傳icon,現在另一個頁面要用白色了,就不需要兩張圖了,而是使用tint來修改為白色即可,
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_back_black"
android:tint="@android:color/white" />
8.減少ENUM的使用
每減少一個ENUM可以減少大約1.0到1.4 KB的大小,
包體積監控
包體積監控應該作為發布流程的一個環節,最好是做到平臺化、流程化,否則很難持續,沒幾個版本包體積又漲上來了,
大致思想:當前版本與上一個版本的包大小做對比,超過200KB需要審批,臨時審批需要給出后續優化方案等等,
參考檔案
- Improve your code with lint checks
- Shrink, obfuscate, and optimize your app
- Android App包瘦身優化實踐
- ReDex
- SoLoader
- 支付寶 App 構建優化決議:Android 包大小極致壓縮
- AndResGuard
- 深入探索 Android 包體積優化
- Android開發高手課包體積優化
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/306276.html
標籤:其他
上一篇:基于 Vmware 搭建 Ubuntu Server 編譯環境(Samba + SSH + Git)
下一篇:Android隱藏選單欄
