Android開發——減小APK大小
0. 前言
APK的大小對APP的載入速度,使用記憶體大小和消耗功率多少有一定影響。如何減小APK的大小對於Android開發者是一個永恆的話題。
首先就來了解一下APK的組成結構。
1. APK的組成結構
META-INF:包含CERT.SF和CERT.RSA簽名檔案,和MANIFEST.MF清單檔案。
assets:包含APP的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:包含被編譯的類,以DalvikART虛擬機器能理解的dex檔案格式。
AndroidManifest.xml:核心的Android清單檔案。這個檔案羅列了APP名稱,版本,訪問許可權,和APP引用的庫檔案。這個檔案使用Android的二進位制XML格式。
2. 減小APK的大小的方式彙總
2.1 Lint工具
在Android Studio中的一個靜態程式碼工具lint,檢測在你的res目錄下程式碼沒有引用的資源,並沒有掃描assets目錄下的資源。當lint工具在你的專案中發現一個潛在的未使用的資源,它會列印一條如下示例的訊息。只提醒,不主動移除。
- res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]
2.2 ShrinkResourcesGradle以及MinifyEnabled
新增到程式碼中的庫可能包含未使用的資源
- android{
- buildTypes{
- release{
- minifyEnabled true
- shrinkResources true
- proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
- }
- }
- }
為了使用shrinkResources,你必須啟動程式碼壓縮,將minifyEnabled設定為true。在構建的過程中,首先ProGuard會移除沒有使用的程式碼,接著Gradle會移除沒有使用的資源。
在Proguard中,是否保留符號表對APP的大小是有顯著的影響的,可酌情註釋下面這行程式碼,但是建議儘量保留,它可以用於保留除錯資訊。
- -keepattributes SourceFile,LineNumberTable
2.3 使用一套資源
對於絕大對數APP來說,只需要取一套設計圖就足夠了。鑑於現在解析度的趨勢,建議取720p的資源,放到xhdpi目錄。在視覺上差別不大,很多大公司的產品也是如此,但卻能顯著的減少資源佔用大小,這裡不是說把非xhdpi的目錄都刪除,而是強調保留一套設計資源就夠了。
2.4 只保留中文的語言資源
大部分應用其實並不需要支援幾十種語言的國際化支援。強大的gradle支援語言的配置,比如國內應用只支援中文:
- android {
- defaultConfig {
- resConfigs "zh"
- }
- }
2.5 關於PNG圖片和JPG圖片
Android打包本身會對png進行無失真壓縮, TinyPNG使用智慧有失真壓縮技術,在儘量少的損失下來減少PNG檔案的大小。具體TinyPNG的資訊請訪問https://tinypng.com/。
如果對於非透明的大圖,jpg將會比png的大小有顯著的優勢,雖然不是絕對的,但是通常會減小到一半都不止。
在啟動頁,活動頁等之類的大圖展示區採用jpg將是非常明智的選擇。
當然大圖適當縮小對視覺的影響也不大,適當縮小大圖也是可行的。
但是Android的介面能用png最好是用png,因為32位的png顏色過渡平滑且支援透明。jpg是畫素化壓縮過的圖片,質量已經下降了,再拿來做9path的按鈕和平鋪拉伸的控制元件必然慘不忍睹,要儘量避免。
在res下這些icon用的都是png格式,就是說Google推薦使用的是png格式的圖片。
2.6 關於WEBP圖片
相對於jpg、png,webp作為一種新的圖片格式,壓縮比比jpg更高但顯示效果卻不輸於jpg,Android 4.0+才原生支援webp, 但是我們的app是相容2.3+,所以4.0以下的裝置將無法看到圖片但不會崩潰。限於Android的支援情況暫時還沒用在手機端廣泛應用起來。並且直到Android 4.2.1+才支援顯示含透明度的webp,官方評測quality引數等於75均衡最佳。
官方介紹: https://developers.google.com/speed/webp/docs/precompiled
2.7 覆蓋某些圖片
一些aar庫裡面包含根本就沒有用的圖。最典型的是support-v4相容庫中包含一些“可能”用到的圖片,實際上在你的app中不會用到。可以考慮把幾張大一些的圖用1×1的圖片替換,如果9patch圖的話,要做成3×3的9patch圖替換。
同理可用於覆蓋第三方庫中我們用不到的大圖,可以在/build/intermediates/exploded-aar/下的各個aar庫的res目錄查詢檢驗。
2.8 刪除armable-v7和x86包下的so
基本上armable的so也是相容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進,如果沒有這方面的要求,可以精簡。這裡不排除有極少數裝置會Crash,請務必測試周全後再發布。
x86包下的so在x86型號的手機是需要的,如果產品沒用這方面的要求也可以精簡。
建議實際工作的配置是隻保留armable、armable-x86下的so檔案,算是一個折中的方案。
我們可以構建一個 APK,它支援所有的 CPU 型別。但是反過來,我們可以為每個 CPU 型別都單獨構建一個 APK,然後不同 CPU 型別的裝置安裝對應的 APK 即可,當然前提是應用市場得提供使用者裝置 CPU 型別設別的支援,就目前來說,至少 PLAY 市場是支援的。
Gradle 可以通過以下配置生成不同 ABI 支援的 APK(引用自別的文章,沒實際使用過):
- android {
- ...
- splits {
- abi {
- enable true
- reset()
- include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'//select ABIs to build APKs for
- universalApk true//generate an additional APK that contains all the ABIs
- }
- }
- // map for the version code
- project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
- android.applicationVariants.all { variant ->
- // assign different version code for each output
- variant.outputs.each { output ->
- output.versionCodeOverride =
- project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
- }
- }
- }
2.9微信資源壓縮打包
微信中的資源混淆工具主要為了混淆資源ID長度(例如將res/drawable/welcome.png混淆為r/s/a.png),同時利用7zip深度壓縮,大大減少了安裝包體積,同時也增加了逼格,提升了反破解難度。效果非常的好,強烈推薦。
2.10 著色方案以及使用shape背景
相信你的工程裡也有很多selector檔案,也有很多相似的圖片只是顏色不同,通過著色方案我們能大大減輕這樣的工作量,減少這樣的檔案。藉助於androidsupport庫可實現一個全版本相容的著色方案。
在扁平化盛行的當下,很多純色的漸變的圓角的圖片都可以用shape實現,程式碼靈活可控,省去了大量的背景圖片。
2.11 外掛化
外掛化技術支援動態的載入程式碼和動態的載入資源,把APP的一部分分離出來了,對於業務龐大的專案來說非常有用,極大的分解了APP大小。因為外掛化技術需要一定的技術保障和服務端系統支援,有一定的風險,如無必要(比如一些小型專案,也沒什麼擴充套件業務)就不需要了,建議酌情選擇。
2.12 資源的重用
同一個圖片的著色,陰影,或者旋轉版本等等,可以重用同一個資源集合和定製它們。
比如避免幀動畫的使用就是一個很好的例子(因為幀動畫每一幀都必須是一張明確的圖片檔案)。
2.13 向量圖的使用
向量圖在Android中以VectorDrawable物件代表(Android L版本引入)。使用VectorDrawable物件,允許開發人員以純程式碼方式生成類似繪製的效果。一個100-byte檔案可以生成一個螢幕大小清晰影象。
然而,每個VectorDrawable物件的渲染明顯的消耗系統時間,並且更大的圖片需要更長的時間來展示在螢幕上。因此僅僅在顯示小的圖片的時候考慮使用向量圖。
具體使用請檢視:http://mobile.51cto.com/news-478709.htm
2.14 移除列舉
一個列舉會增加你的app的class.dex檔案大約1.0到1.4K的大小。對於複雜的系統或者共享庫這種情況會更加明顯。如果可能的話,考慮使用@IntDef和ProGuard來剝離列舉,並轉換成Integer。這種型別轉換保留了所有列舉的安全效益。