Android減包 - 減少APK大小

本文是對Google官方文檔 Reduce APK Size 的翻譯l留與備用

用戶經(jīng)常會避免下載看起來體積較大的應用,特別是在不穩(wěn)定的2G、3G網(wǎng)絡或者在以字節(jié)付費的網(wǎng)絡。這篇文章描述了怎樣減少你的APK大小,這會讓更多的用戶愿意下載你的應用。

理解APK的結(jié)構(gòu)

在討論怎樣減少應用大小之前,先了解APK的結(jié)構(gòu)是有用的。一個APK文件就是ZIP包,其中包含了組成你的應用的所有文件,比如Java類文件,資源文件,和一個包含被編譯資源的文件。

一個APK包含了以下目錄:

META-INF/: 包含CERT.SF和CERT.RSA簽名文件,也包含了MANIFEST.MF文件。(譯注:校驗這個APK是否被人改動過)

assets/: 包含了應用的資源,這些資源能夠通過AssetManager對象獲得。

res/: 包含了沒被被編譯到resources.arsc的資源。

lib/: 包含了針對處理器層面的被編譯的代碼。這個目錄針對每個平臺類型都有一個子目錄,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips。

一個APK也包含了以下文件,其中只有AndroidManifest.xml是強制的:

resources.arsc: 包含了被編譯的資源。該文件包含了res/values目錄的所有配置的XML內(nèi)容。打包工具將XML內(nèi)容編譯成二進制形式并壓縮。這些內(nèi)容包含了語言字符串和styles,還包含了那些內(nèi)容雖然不直接存儲在resources.arsc文件中,但是給定了該內(nèi)容的路徑,比如布局文件和圖片。

classes.dex: 包含了能被Dalvik/Art虛擬機理解的DEX文件格式的類。

AndroidManifest.xml: 包含了主要的Android配置文件。這個文件列出了應用名稱、版本、訪問權(quán)限、引用的庫文件。該文件使用二進制XML格式存儲。(譯注:該文件還能看到應用的minSdkVersion, targetSdkVersion等信息)

譯注:使用APK Analyzer能夠清晰地看出以上文件的內(nèi)容,具體請看:使用APK Analyzer分析你的APK。

減少資源個數(shù)和尺寸

APK的大小會影響應用加載的速度,使用的內(nèi)存大小,消耗的電量大小。一個最簡單的縮小APK大小的方式是減少資源的個數(shù)和大小。特別地,你能移除應用中不再使用的資源,你也能使用可縮放的Drawable對象代替圖片文件。這節(jié)討論一些通過減少資源從而減少APK大小的方法。

譯注:減少資源個數(shù)和縮小資源大小的效果是很顯著的,比如有一天發(fā)現(xiàn)我組里的項目中還包含了舊版本的引導頁視頻(1.5M),一下就就減少了1.5M,想想為了減少1.5M你得刪多少代碼才能辦到。

移除不使用的資源

lint是Android Studio中的一個靜態(tài)代碼分析工具,檢測在“res/”目錄中你的代碼沒有引用的資源。當lint工具發(fā)現(xiàn)了項目中潛在的未使用的資源,它會打印以下類似信息:

res/layout/preferences.xml:Warning:The resource R.layout.preferences appears to be unused[UnusedResources]

注意:lint工具不會掃描“asset/”目錄,這個目錄是通過反射引用的資源,或者鏈接到應用中的庫文件。還有,lint不會移除資源,只會發(fā)出警告。

被引用的庫中可能會包含沒使用的資源。如果你在build.gradle文件中啟用shrinkResources,則Gradle能自動移除這些資源。

為了使用shrinkResources,你必須要啟用代碼混淆。在構(gòu)建過程中,首先proguard移除了未使用的代碼,然后gradle移除未使用的資源。

譯注:lint工具還能夠檢查出未使用的類、類中未使用的方法或變量。

更多關于通過代碼混淆和其他方式減包,請看Shrink Your Code and Resources。

在Gradle插件0.7或更高版本,你能申明應用支持的配置。Gradle通過傳遞resConfigs和defaultConfig給構(gòu)建系統(tǒng),構(gòu)建系統(tǒng)會防止不支持的配置出現(xiàn)在APK中,從而減少APK大小。更多信息請看Remove unused alternative resources。

譯注:在hello world工程里,resConfigs配置為“zh”和不配置resConfigs,resources.arsc文件相差了80K。

最小化第三方庫中資源的使用

當開發(fā)Android應用時,你經(jīng)常使用第三方庫提升應用的可用性和靈活性。比如,你引用Android Support Library提升舊設備的用戶體驗,或者使用Google Play服務實現(xiàn)文字自動翻譯。

如果一個第三方庫原本是為服務器或普通電腦設計,會引入許多不需要的對象和方法。為了只引入應用需要的庫中的那部分,你可以編輯庫文件(如果庫的license允許你這么做)。你也能使用另外的針對手機的實現(xiàn)同樣功能的庫。

注意:代碼混淆能清除庫中不被使用的代碼,但是他不能移除庫的大量內(nèi)部依賴。

只支持部分屏幕密度

Android支持很多設備集,其中包含了各種不同的屏幕密度。在Android 4.4及更高版本,框架支持不同的密度:ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi和xxxhdpi。盡管Android支持所有這些屏幕密度,但你不需要為每個密度都配置相應的資源。

如果你知道某種特定屏幕密度已經(jīng)很少有用戶使用了,那么你可以考慮是否需要為這個屏幕密度配置資源。如果你不包含針對特定屏幕密度的資源,那么Android會自動縮放原本針對其他密度的已有資源。

如果你的應用只需要縮放的圖片,你甚至可以把圖片存放在drawable-nodpi目錄,從而節(jié)省更多空間。我們推薦每個應用都應該至少包含xxhdpi的圖片。

更多關于屏幕密度的信息,請看Screen Sizes and Densities。

減少動畫幀數(shù)

使用幀動畫會大大增加APK的大小。圖1顯示了目錄中構(gòu)成幀動畫的多個PNG文件。每個圖片都是動畫的一幀。

對于加入動畫的每幀,你都增加了APK中圖片的個數(shù)。圖1中,幀動畫的幀率是30 FPS。如果幀率降到15 FPS,圖片數(shù)量將減少一半。


譯注:還有一個常見的減包方案是刪除幀動畫中重復的圖片資源,比如第1幀和第3幀的圖片一樣,那么只保留一個。

使用Drawable對象

一些圖片不需要靜態(tài)的圖片資源,框架能在運行時動態(tài)地繪制圖像。Drawable對象(XML的<shape>)只需要占用APK中的一點空間。另外,XML形式的Drawable對象能夠產(chǎn)生遵循Material Design設計規(guī)范的圖像。

重用資源

你能包含一張圖片的很多變種,比如染色、陰影、旋轉(zhuǎn)的版本。但是,我們推薦在運行時復用一張圖片來定制化他們。

Android提供了很多方式改變資源的顏色。對于Android 5.0及以上,使用android:tint和tintMode屬性。對于更低版本,使用ColorFilter類。

你也能夠刪除那些只是對另一個資源做旋轉(zhuǎn)的資源。下面的代碼片段提供了對一個箭頭旋轉(zhuǎn)180度。

<?xml version="1.0"encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android"android:drawable="@drawable/ic_arrow_expand"android:fromDegrees="180"android:pivotX="50%"android:pivotY="50%"android:toDegrees="180"/>

通過代碼繪制

你也能通過代碼繪制圖像,從而減少APK大小。代碼方式繪制圖像不需要任何空間因為你不再需要在APK中存儲圖像文件。

壓縮PNG文件

AAPT工具能夠在構(gòu)建過程中通過無損壓縮優(yōu)化res/drawable/中的圖片資源。比如aapt工具能將需要顏色少于256色的PNG變?yōu)?位PNG圖,這樣能夠在保證圖片質(zhì)量的同時減少內(nèi)存使用。

需要注意aapt有以下局限性:

aapt工具不會壓縮asset目錄的PNG文件。

通過aapt的優(yōu)化,圖片文件會使用少于256色。

aapt工具可能會影響已經(jīng)被壓縮過的PNG文件。為了防止這種情況,你可以在gradle文件中設置cruncherEnabled為false禁用aapt對PNG的壓縮。

aaptOptions{cruncherEnabled=false}

譯注:建議把cruncherEnabled設為false,然后通過tinypng手工壓縮PNG圖片。

壓縮PNG和JPEG文件

你能使用一些工具(比如pngcrush, pngquant, zopflipng)在不降低圖像質(zhì)量的前提下減少PNG文件大小。所有這些工具都能保留圖像質(zhì)量的情況下減少PNG文件大小。

pngcrush工具特別有效:這個工具通過迭代png過濾器和zlib參數(shù),使用每種過濾器和參數(shù)的組合壓縮圖像,并選擇最小的那個作為最后的輸出。

對于JPEG文件,能使用packJPG壓縮JPEG文件。

譯注:guetzli是Google最近推出的JPEG編碼器,官方宣稱相同圖片質(zhì)量時,比libjpeg生成的圖片小20–30%。

使用WebP文件格式

你也能使用WebP文件格式存儲圖片而不是PNG或者JPEG。WebP格式是有損壓縮(像JPEG)且有透明通道(像PNG),且壓縮率高于JPEG或PNG。

使用WebP文件格式也有一些缺點。第一,低于Android 3.2的版本不支持WebP,第二,WebP的解碼時間比PNG長。

注意:Google Play的APK的應用啟動圖標只能使用PNG格式,而不支持其他格式。

在Android Studio中,能將BMP,JPG,PNG或者靜態(tài)GIF圖片轉(zhuǎn)換成WebP格式。更多信息,請看Create WebP Images Using Android Studio。

使用向量圖

你能使用向量圖去創(chuàng)建一個分辨率無關的圖標。使用向量圖能夠顯著減少APK大小。在Android中向量圖是以VectorDrawable對象形式存在的。使用VectorDrawable對象,一個100B的文件能生成一個屏幕大小的清晰圖片。

但是,系統(tǒng)需要很長時間渲染VectorDrawable對象,更大的圖片需要更長的時間顯示在屏幕上。因此只有小圖片才考慮使用向量圖。

更多關于VectorDrawable對象的信息,請看Working with Drawables。

減少Native和Java代碼

有許多方法能夠減少Java和Native的代碼量。

減少不必要的生成代碼

確保理解任何自動生成的代碼。比如,許多protocol buffer工具生成了過多的方法和類,這會讓你的應用大小翻倍。

移除枚舉

一個枚舉能讓classes.dex文件增加1–1.4K。枚舉的加入會快速增加應用體積。我們可以使用@IntDef注解和Proguard代替枚舉,它能提供和枚舉一樣的類型安全轉(zhuǎn)換。

減少Native庫的大小

如果你的應用使用了Native代碼和Android NDK,你也能通過優(yōu)化代碼減少應用體積,這里介紹的兩個技巧是刪除調(diào)試符號和避免抽取Native庫。

移除調(diào)試符號

如果應用在開發(fā)中并且仍需要調(diào)試,那么我們能理解使用調(diào)試符號。使用Android NDK提供的arm-eabi-strip工具,能從Native庫中刪除不必要的調(diào)試符號,之后你再編譯release包。

避免抽取Native庫

在APK中存儲未壓縮的so文件,并且在Manifest文件的<application>中設置android:extractNativeLibs為false,這會防止在安裝時PackageManager將APK中的so文件拷貝到文件系統(tǒng),避免這種拷貝會讓應用在做增量更新時的更新包更小。

維持多個小的APK包

你的APK會包含用戶下載了但從未使用的內(nèi)容,比如地區(qū)或語言信息(譯注:比如我是中國人,我就不會用到其他語種的資源)。為了給用戶創(chuàng)建小的下載包,你能把你的應用拆分成多個APK,這些APK的差別在于一些因素(比如屏幕大小或者GPU紋理支持)。

當一個用戶下載了應用,設備根據(jù)自身的特性和設置獲取正確的APK。這種方式能夠讓設備不獲取設備不需要的資源。比如,如果設備是hdpi的,那么他就不需要xxxhdpi的資源。

更多信息請看Configure APK Splits和Maintaining Multiple APKs。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容