1. 程式人生 > >Android開發——減小APK大小

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工具在你的專案中發現一個潛在的未使用的資源,它會列印一條如下示例的訊息。只提醒,不主動移除。 

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

2.2 ShrinkResourcesGradle以及MinifyEnabled

新增到程式碼中的庫可能包含未使用的資源

。在APP的build.gradle檔案中啟動shrinkResourcesGradle,它能自動替你刪除這些資源。

  1. android{   
  2.     buildTypes{   
  3.         release{   
  4.             minifyEnabled true
  5.             shrinkResources true
  6.             proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
  7.        }   
  8.    }   
  9. }   

為了使用shrinkResources,你必須啟動程式碼壓縮,將minifyEnabled設定為true。在構建的過程中,首先ProGuard會移除沒有使用的程式碼,接著Gradle會移除沒有使用的資源。

在Proguard中,是否保留符號表對APP的大小是有顯著的影響的,可酌情註釋下面這行程式碼,但是建議儘量保留,它可以用於保留除錯資訊。

  1. -keepattributes SourceFile,LineNumberTable  

2.3 使用一套資源

對於絕大對數APP來說,只需要取一套設計圖就足夠了。鑑於現在解析度的趨勢,建議取720p的資源放到xhdpi目錄。在視覺上差別不大,很多大公司的產品也是如此,但卻能顯著的減少資源佔用大小,這裡不是說把非xhdpi的目錄都刪除,而是強調保留一套設計資源就夠了。

2.4 只保留中文的語言資源

大部分應用其實並不需要支援幾十種語言的國際化支援。強大的gradle支援語言的配置,比如國內應用只支援中文

  1. android {  
  2.     defaultConfig {  
  3.         resConfigs "zh"
  4.     }  
  5. }  

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更高但顯示效果卻不輸於jpgAndroid 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-v7x86包下的so

基本上armable的so也是相容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進,如果沒有這方面的要求,可以精簡。這裡不排除有極少數裝置會Crash,請務必測試周全後再發布。

x86包下的sox86型號的手機是需要的,如果產品沒用這方面的要求也可以精簡。

建議實際工作的配置是隻保留armablearmable-x86下的so檔案,算是一個折中的方案。

我們可以構建一個 APK,它支援所有的 CPU 型別。但是反過來,我們可以為每個 CPU 型別都單獨構建一個 APK,然後不同 CPU 型別的裝置安裝對應的 APK 即可,當然前提是應用市場得提供使用者裝置 CPU 型別設別的支援,就目前來說,至少 PLAY 市場是支援的。
Gradle 可以通過以下配置生成不同 ABI 支援的 APK(引用自別的文章,沒實際使用過):

  1. android {  
  2.     ...  
  3.     splits {  
  4.         abi {  
  5.             enable true
  6.             reset()  
  7.             include 'x86''x86_64''armeabi-v7a''arm64-v8a'//select ABIs to build APKs for
  8.             universalApk true//generate an additional APK that contains all the ABIs
  9.         }  
  10.     }  
  11.     // map for the version code
  12.     project.ext.versionCodes = ['armeabi'1'armeabi-v7a'2'arm64-v8a'3'mips'5'mips64'6'x86'8'x86_64'9]  
  13.     android.applicationVariants.all { variant ->  
  14.         // assign different version code for each output
  15.         variant.outputs.each { output ->  
  16.             output.versionCodeOverride =  
  17.                     project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode  
  18.         }  
  19.     }  
  20.  }  

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。這種型別轉換保留了所有列舉的安全效益。