Android 優(yōu)化 (apk瘦身/打包優(yōu)化)
update time 2019年12月11日14:29:56
該文章為學(xué)習 如下參考文章的 學(xué)習筆記,多有雷同。
參考文章
工程分析
如果真機運行過后,我們可以通過 Android Studio Build -> Analyze APK - > 選擇 app/build/output 下的apk文件 debug 或者 relase 兩個文件夾下的 apk文件。下圖為demo工程的 分析結(jié)構(gòu)圖 (忽略raw 文件為啥那么大 -,-)
由上圖可知一個APK主要包含如下文件夾(當然有些文件可能我這個Demo APK沒有包含):
res:包含了一些不會被編譯到resources.arsc的資源文件。如drawable文件、layout文件、mipmap文件、anim文件等。
lib:包含了一些區(qū)分于處理器的編譯代碼,主要是SO文件,eg:armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips。
assets:包含了一些通過AssetManager能夠檢索到的資源。如MP3、字體、webp等資源文件。
META-INF:包含了CERT.SF和CERT.RSA簽名文件,還有MANIFEST.MF文件 (因為秘鑰文件 或簽名文件大小不大,所以這里暫時沒有優(yōu)化的點)
文件
- resources.arsc:包括了所有可以被編譯的位于res/values/目錄下的XML資源。打包工具在打包過程中會把XML的內(nèi)容編譯成二進制的形式,亦或者把相關(guān)資源的引用路徑編譯成二進制,然后整合到該文件里面。例如string文件、layout的路徑、圖片的路徑等。
- classes.dex:包含了所有的Java文件編譯后的class文件,class文件最終轉(zhuǎn)化成該dex文件。一般文件都比較大,有的App有幾個dex文件,這是因為單個DEX文件限制方法數(shù)在65536,所以當代碼量過大時,就需要通過multiDex進行分包,拆分成多個dex文件,解決這個問題。
- AndroidManifest.xml:如果多module,內(nèi)包含多個module的AndroidMainifest文件的權(quán)限、聲明等配置文件。
瘦身優(yōu)化
Res 目錄優(yōu)化
Android設(shè)備在加載圖片時會優(yōu)先加載對應(yīng)分辨率文件夾下的圖片,如果對應(yīng)分辨率文件下沒有所要的圖片,則找高分辨率對應(yīng)文件夾下的圖片。目前不同分辨率對應(yīng)優(yōu)先加載的文件夾中圖片如下,如果是針對國內(nèi)用戶的App可以只保留xxhdpi目錄(19201080 -> xxhdpi),而如果是東南亞市場的App則可以只保留xhdpi (1280720 -> xhdpi)。
PNG圖片壓縮 (詳細辦法在下一個小節(jié)詳細講述)
lint檢測出無用的資源文件,可以直接在AS里面使用。(Android Studio 打開 Analyze -> Run Inspection by Name -> 輸入:Unused resources ->跳出彈框選擇范圍即可)注意:lint檢查出來的資源都是無直接引用的,所以如果我們通過getIdentifier()方法引用文件時,lint也會標記為無引用,所以刪除時注意不要刪除通過getIdentifier()引用的資源。
shrinkResources:在編譯過程中用來檢測并刪除無用資源文件,也就是沒有引用的資源,minifyEnabled 這個是用來開啟刪除無用代碼,比如沒有引用到的代碼,所以如果需要知道資源是否被引用就要配合minifyEnabled使用,只有兩者都為true時才會起到真正的刪除無效代碼和無引用資源的目的。在build.gralde文件里面打開即可:
android {
buildTypes{
release {
// 混淆
minifyEnabled true
// 移除無用的資源
shrinkResources true
}
}
}
Andorid PNG圖片壓縮
以PNG資源為例,PNG 圖片相對于 JPEG 圖片來說,它是一種無損的圖像存儲格式,同時多了一條透明度A通道,所以一般情況下,PNG 圖片要比 JPEG 圖片要大,如何縮小圖片所占內(nèi)存大概分為三種辦法(個人推薦度依次遞減):
-
將PNG圖片通過 AndroidStudio 轉(zhuǎn)換工具將PNG/PSD圖片轉(zhuǎn)為 SVG圖片 (具體AndroidStudio步驟:右鍵res下的drawable 文件 -> 選擇New ->Vector Asset -> Local file 將本地圖片導(dǎo)入轉(zhuǎn)換為 SVG xml文件)。導(dǎo)入后建議使用 AppCompatImageView 顯示SVG圖片,不過只要依賴 AppCompatActivity ,xml中的imageview 會自動轉(zhuǎn)為 AppCompatImageView 處理(support包中所有含有AppCompat前綴的控件均受相同處理)。
eg:app:srcCompat="@drawable/svg_ic_arrow_right"
關(guān)于 PNG 的壓縮算法有很多,這里我們只說兩種比較常用的:Indexed_color 和 Color_quantization。
Indexed_color :將具體的 ARGB 顏色存儲轉(zhuǎn)換成索引下表,來減少文件的大小。ARGB 中,每個通道的存儲都需要 8 位,也就是 1 字節(jié),一個 ARGB 存儲就需要 4 字節(jié),而索引的存儲只需要 1 字節(jié)。而索引指向的顏色會存放在一個叫 palette(調(diào)色板)的數(shù)組里面。優(yōu)點:減少文件大小 缺點:調(diào)色板的大小通常只支持 4,16,256 所以png的顏色不能超過 256個
Color_quantization:通過使用相似顏色來減少圖像中使用的顏色種類,再配合調(diào)色板,來達到減少圖片文件大小的目的,這是一種有損的壓縮算法
說完我上述的資源優(yōu)化后(個人觀點),我們簡單的分析一下 AAPT2 打包資源的源碼思路。(6.0源碼)
// Check if image is really grayscale
if (isGrayscale) {
f (rr != gg || rr != bb) {
// ==>> Code 1
isGrayscale = false;
}
}
// Check if image is really opaque
if (isOpaque) {
if (aa != 0xff) {
// ==>> Code 2
isOpaque = false;
}
}
// Check if image is really <= 256 colors
if (isPalette) {
col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
bool match = false;
for (idx = 0; idx < num_colors; idx++) {
if (colors[idx] == col) {
match = true;
break;
}
}
if (!match) {
if (num_colors == 256) {
// ==>> Code 3
isPalette = false;
} else {
colors[num_colors++] = col;
}
}
}
其實看到源碼中部分的判斷 已經(jīng)知道AAPT2 使用的壓縮為 Indexed_color 方式,通過判斷將 PNG圖片分為四種壓縮方式
- PNG_COLOR_TYPE_PALETTE
使用調(diào)色板模式,最終圖片的大小就是 一個像素 1 字節(jié) + 調(diào)色板中一個顏色 4 字節(jié)
- PNG_COLOR_TYPE_GRAY
灰度模式,這種是最節(jié)省的模式,一個像素 1 字節(jié)
- PNG_COLOR_TYPE_GRAY_ALPHA
灰度模式,同時存在透明通道,一個像素 2 字節(jié)
- PNG_COLOR_TYPE_RGB
RGB 模式,刪除了透明通道,一個像素 3 字節(jié)
- PNG_COLOR_TYPE_RGB_ALPHA
ARGB 模式,一個像素 4 字節(jié)(沒有壓縮)
在不損壞圖片畫質(zhì)的情況下進行壓縮 也算是比較保險的壓縮方式,當然也可以使用 第三方的一些壓縮方式: pngcrush、pngquant(相對推薦 壓縮后的提及其他人測試會更小一點)、tinypng 這里暫時不展開一一說明了。工具用就完了......
Assests 目錄優(yōu)化
- 對于中文字體文件,字體文件包含了好幾千個漢字,但是實際上在App中并不會全部都使用,這時候我們就可以把字體文件進行刪減,在Github上面有一個字體提取工具FontZip,刪除不用的字體文件
- 如果Assests下有 mp3、mp4等大的資源,可以再使用到的時候 從服務(wù)端下載緩沖下來。如果不聯(lián)網(wǎng)的話,可以通過7z 、zip 壓縮資源到本地,壓縮 -> 打包 -> 安裝使用 -> 解壓縮
libs 目錄優(yōu)化
Android系統(tǒng)現(xiàn)在支持很多種CPU架構(gòu)(如mips、arm、x86等),市面上主流機型都是arm架構(gòu)。所以可以有選擇地保留某些架構(gòu)的so,從而降低lib文件夾的大小。一般只保留armeabi或者armeabi-v7a即可。操作也是比較簡單,只需要在根目錄的build.gradle下配置:
android {
buildTypes {
ndk {
abiFilters "armeabi-v7a"
}
}
}
resources.arsc文件壓縮
個人感覺這里的優(yōu)化文件名稱壓縮 收效甚微。部分數(shù)據(jù)看下圖
如果應(yīng)用不需要支持多種語言的情況下(只保留zh 國內(nèi)的文字),我們只需要在build.gralde里面進行如下配置即可完成無用語言資源的刪除,這樣在打包的時候就會排除私有項目、android系統(tǒng)庫和第三方庫中非中文的資源文件了,效果還是比較顯著的。
android {
defaultConfig {
// 只保留中文
resConfigs "zh"
}
}
- AndResGuard:縮小APK大小的工具,他的原理類似Java Proguard,但是只針對資源。他會將原本冗長的資源路徑變短,例如將res/drawable/xxxxxxx 變?yōu)?r/d/x。在build.gradle文件中進行如下配置:
andResGuard {
mappingFile = null
use7zip = true
useSign = true
keepRoot = false
whiteList = [
//圖標
"R.drawable.icon",
...
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"resources.arsc"
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.1.9'
}
}
dex壓縮
Dalvik是Android平臺運行時的環(huán)境,但是Dalvik虛擬不支持直接執(zhí)行Java的字節(jié)碼,所以會對編譯生成的 .class 文件進行翻譯、重構(gòu)、解釋、壓縮等處理,這個處理過程是由 dx 進行處理,處理完成后生成的產(chǎn)物會以 .dex 結(jié)尾,稱為Dex文件。如果單個Dalvik Excutable(DEX)字節(jié)碼文件內(nèi)的方法數(shù)不可以超過65536個,所以需要DEX分包配置來避免這個限制,使應(yīng)用能夠構(gòu)建并讀取DEX文件。
這里就推薦 官方提供的優(yōu)化方案:Proguard代碼混淆
在build.gradle里面設(shè)置minifyEnabled為ture,同時在proguardFiles指向proguard的規(guī)則文件即可(如下代碼)。
android {
buildTypes{
minifyEnabled true
proguardFiles 'proguard.cfg'
}
}