開發者掌握什麼手藝才能顯得逼格滿滿?你必須要懂的APK瘦身知識!

隨著業務複雜度的逐漸增加,程式碼、資源也在不斷的增加,此時你的APP大小也在增加。
從使用者層面來說,面對動輒幾十兆的APP來說在非WIFI情況下還是會猶豫要不要下載,不下載你就可能因此失去了一個使用者。
從公司層面來講,流量就是錢,減少APP的大小就顯得尤為重要。從開發者層面上來講,你掌握了這個手藝也會略顯逼格滿滿。
廢話不多說了,開始正題。
01 APK結構的那些事
知己知彼,方能百戰不殆。瞭解應用程式APK的結構對於我們來說很有幫助。APK檔案由一個ZIP存檔組成,其中包含組成應用程式的所有檔案。這些檔案包括Java類檔案,資原始檔和包含編譯資源的檔案。
APK包含以下目錄:
-
META-INF/
:包含CERT.SF
和CERT.RSA
簽名檔案以及MANIFEST.MF
清單檔案。 -
assets/
:包含應用可以使用 AssetManager物件檢索的應用資源。 -
res/
:包含未編譯到的資源resources.arsc
。 -
lib/
:包含特定於處理器軟體層的編譯程式碼。該目錄包含了每種平臺的子目錄,像armeabi
,armeabi-v7a
,arm64-v8a
,x86
,x86_64
,和mips
。 -
resources.arsc
:包含已編譯的資源。該檔案包含res/values/
資料夾所有配置中的XML內容。打包工具提取此XML內容,將其編譯為二進位制格式,並將內容歸檔。此內容包括語言字串和樣式,以及直接包含在resources.arsc
檔案中的內容路徑 ,例如佈局檔案和影像。 -
classes.dex
:包含以Dalvik / ART虛擬機器可理解的DEX檔案格式編譯的類。 -
AndroidManifest.xml
:包含核心Android清單檔案。該檔案列出應用程式的名稱,版本,訪問許可權和引用的庫檔案。該檔案使用Android的二進位制XML格式。
來看看淘寶APP的unzip之後的檔案目錄

一般來講APK結構中比較大的部分一般是
classes.dex、lib、res、assets
這些檔案或者目錄。所以接下來將會針對這四種情況進行講解。
另外,我們透過APK Analyser 可以分析 APK

02 減小 classes.dex
classes.dex 包含了所有 Java 程式碼。當你編譯你的應用時,gradle 會將你的所有模組裡的 .class 檔案轉換成 .dex 檔案並將這些檔案合成一個 classes.dex 檔案。
單個的 classes.dex 檔案可以容納大約 64K 方法。如果你達到了這個限制,你必須要在你的工程中啟用 multidexing。這將會建立另一個 classes1.dex 檔案去儲存剩下的方法。所以 classes.dex 檔案數目由你的方法數而定。
減少第三庫的使用
隨著業務的頻繁變更以及複雜度的增加,我們往往會使用第三方Libaray,有時候我們可能僅僅用到了很少一部分的功能,這個時候就需要慎重考慮完全引用。從我的開發經驗上來講,寧願參照自己去實現,也不願意多引入一個第三方庫。
避免列舉
一個列舉可以為您的應用程式的
classes.dex
檔案新增大約1.0到1.4 KB的大小 。這些新增可以快速累積到複雜系統或共享庫。如果可能,請考慮使用
@IntDef
註釋,這種型別轉換保留了列舉的所有型別安全優勢。
使用ProGuard
下面這段來自 build.gradle 檔案的程式碼用於為釋出構建啟用程式碼壓縮:
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } ... }
除了 minifyEnabled 屬性外,還有用於定義 ProGuard 規則的 proguardFiles 屬性:
getDefaultProguardFile('proguard-android.txt') 方法可從 Android SDK tools/proguard/ 資料夾獲取預設的 ProGuard 設定。
提示:要想做進一步的程式碼壓縮,請嘗試使用位於同一位置的 proguard-android-optimize.txt 檔案。它包括相同的 ProGuard 規則,但還包括其他在位元組碼一級(方法內和方法間)執行分析的最佳化,以進一步減小 APK 大小和幫助提高其執行速度。
proguard-rules.pro 檔案用於新增自定義 ProGuard 規則。預設情況下,該檔案位於模組根目錄(build.gradle 檔案旁)。
03 最佳化assets和res中的資原始檔
題外話
res/raw和assets的相同點:
- 兩者目錄下的檔案在打包後會原封不動的儲存在apk包中,不會被編譯成二進位制。
res/raw和assets的不同點:
-
res/raw
中的檔案會被對映到R.java檔案中,訪問的時候直接使用資源ID即R.raw.filename;assets資料夾下的檔案不會被對映到R.java中,訪問的時候需要AssetManager類。 -
res/raw
不可以有目錄結構,而assets則可以有目錄結構,也就是assets目錄下可以再建立資料夾。
針對不同的情況,對於資原始檔有不同的最佳化策略。一般來講,對於res/drawable-hdpi中的png資源可以進行壓縮。
圖片資源最佳化策略
格式壓縮
使用TinyPng或者 Guetzli進行壓縮。Guetzli的使用可以參見我之前寫的博文 在Mac上使用Google圖片壓縮工具Guetzli
使用WebP檔案格式
定位Android 3.2(API級別13)或更高階別時 ,您也可以使用 WebP檔案格式來製作影像,而不是使用PNG或JPEG檔案。WebP格式提供有失真壓縮(如JPEG)以及透明度(如PNG),但可以提供比JPEG或PNG更好的壓縮。
Android 4.0 (API level 14) 支援有失真壓縮的WebP格式,Android 4.3 (API level 18) 開始支援無損透明WebP影像。
看下圖:

壓縮效率極高,僅為PNG格式的12%。驚喜不驚喜。。。
使用向量圖形
您可以使用向量圖形來建立與解析度無關的圖示和其他可伸縮媒體。使用這些圖形可以大大減少您的APK足跡。向量影像在Android中表示為 VectorDrawable物件。透過一個 VectorDrawable物件,一個100位元組的檔案可以生成一個與螢幕尺寸一致的清晰影像。
但是,系統渲染每個 VectorDrawable物件需要很長時間,而較大的影像需要更長的時間才能顯示在螢幕上。因此,只有在顯示小影像時才考慮使用這些向量圖形。
其它策略
有時候我們可能對一張圖片進行重複利用,比如一張圖片僅僅是整體顏色的變換可以使用
setColorFilter
或者
tint
。儘量減少使用幀動畫,那可是一堆圖片呀。
壓縮資源
要啟用資源壓縮,請在 build.gradle 檔案中將 shrinkResources 屬性設定為 true。
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
資源壓縮器目前不會移除 values/ 資料夾中定義的資源(例如字串、尺寸、樣式和顏色)。這是因為 Android 資源打包工具 (AAPT) 不允許 Gradle 外掛為資源指定預定義版本。
同時,我們也可以指定哪些資源可以保留下來。
例如,將下邊的程式碼儲存在 res/raw/keep.xml。構建不會將該檔案打包到 APK 之中。
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools=" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
resources
有以下屬性:
tools:keep 指出哪些資源會保留 tools:discard 指定哪些資源需要剔除 tools:shrinkMode 資源壓縮模式,有兩種取值strict和safe,預設為safe
safe
和
strict
的最佳化策略:
safe可以簡單理解為安全模式,它會盡最大努力檢查程式碼中可能會使用到的資源進行保留,避免執行時錯誤。
如果你的程式碼呼叫 Resources.getIdentifier(),這就表示你的程式碼將根據動態生成的字串查詢資源名稱。當你執行這一呼叫時,預設情況下資源壓縮器會採取防禦性行為,將所有具有匹配名稱格式的資源標記為可能已使用,無法移除。
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
img_ 字首的資源標記為已使用。
在strict模式下,img_字首的資源會做未使用的處理,因此你需要使用
tools:keep
手動進行已使用標識。
移除未使用的備用資源
我們知道google給我們的apk提供了國際化支援,如適應不同的螢幕解析度的drawable資源,還有適應不同語言的字串資源等等,但是在很多情況下我們只需要一些指定解析度和語言的資源就可以了,這個時候我們可以使用resConfigs方法來配置。
defaultConfig { // 對於國際化支援只打包中文資源, resConfigs "zh-rCN" }
04 lib中資源最佳化
這裡我們主要講一下lib中動態連結庫的最佳化策略,也就是SO檔案。如果你有NDK的開發經驗可能會更容易理解一些。
為了支援不同指令集的情況,應用可能會包含armeabi、armeabi-v7a、x86的SO檔案等。
目前主流的機型都是支援armeabi-v7a的,並且armeabi-v7a相容armeabi。所以在一般的開發中我們只需要使用armeabi-v7a 進行ABI支援。
有些SO庫可以採用網路下載,把負擔放到使用者安裝完應用之後。對於哪些SO檔案可以放到網路中載入,還需要看具體業務情況。
題外話,如果執行時找不到SO的話,會導致應用崩潰。
java.lang.UnsatisfiedLinkError: Couldn't load stlport_shared from loader dalvik.system.PathClassLoader: findLibrary returned null at java.lang.Runtime.loadLibrary(Runtime.java:365) at java.lang.System.loadLibrary(System.java:535) at com.your.app.NativeClass.<clinit>(Native.java:16) ... 63 more Caused by: java.lang.UnsatisfiedLinkError: Library stlport_shared not found at java.lang.Runtime.loadLibrary(Runtime.java:461) at java.lang.System.loadLibrary(System.java:557) at com.your.app.NativeClass.<clinit>(Native.java:16) ... 5 more
我們也是有辦法應對的,可以參見這個開源專案
ReLinker
另外關於SO的最佳化我會單獨拿出來講一講,附上我的GitHub:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2677528/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JavaScript必須要掌握的知識-作用域JavaScript
- 必須懂的mysql知識MySql
- 必須掌握的Linux使用者組知識Linux
- JavaScript必須要掌握的知識-作用域編寫提升JavaScript
- 前端必須掌握的知識點前端
- Linux新手入門必須要掌握的10個知識點!Linux
- Android開發者要換多少次工作,才能心滿意足?Android
- Android 之 Notification 必須掌握知識點Android
- Gradle:你必須掌握的開發常見技巧Gradle
- 什麼人直播能賺得盆滿缽滿
- java開發必須要掌握的20個核心技術Java
- 外貿小白必須掌握的基礎知識
- JAVA程式設計師“黃金5年”必須要掌握的知識技能Java程式設計師
- 爬蟲必須得會的預備知識爬蟲
- 影片直播原始碼提高使用者滿意度必須要具有的特性原始碼
- 牛逼程式設計師必須要掌握金字塔思維程式設計師
- 必須要掌握的重要目錄
- JavaScript大師必須掌握的12個知識點JavaScript
- JVM-Java工程師必須掌握的知識點JVMJava工程師
- 為什麼學習web前端開發,必須要掌握好JavaScript這門程式語言?Web前端JavaScript
- aardio 開發桌面應用,這幾點必須要掌握!
- 你必須掌握的視覺化大屏開發模式視覺化模式
- Git中~你必須掌握的!Git
- 你必須知道的Java基礎知識Java
- 前端學習,除了掌握學習路線之外,必須要注意的知識要點!前端
- 前端必須懂的計算機網路知識—(TCP)前端計算機網路TCP
- 前端必須懂的計算機網路知識—(HTTP)前端計算機網路HTTP
- 關於資料庫索引,必須掌握的知識點資料庫索引
- 程式設計師生存指南:你必須要掌握的兩點!程式設計師
- Web前端主要學什麼?這些知識要掌握Web前端
- 資料分析師要掌握什麼知識和技能?
- 說說你做過讓你覺得最滿意的專案是什麼?為什麼?
- 什麼樣的ERP軟體才能滿足貿易公司發展需求?
- Web前端要學什麼語言?需掌握哪些知識?Web前端
- 你必須懂的前端效能優化前端優化
- 對於MySQL你必須要了解的鎖知識MySql
- 軟體測試必須掌握的 http 網路協議知識HTTP協議
- 軟體測試必須掌握的http網路協議知識HTTP協議