[譯] Android效能最佳實踐:減少您的APK大小看這一篇就夠了

愛學園發表於2018-11-13

大家都知道開發中應用程式的效能是非常重要的,但是這也是優化提升的難點,本章針對 Android效能實踐——從減少APK的大小開始,提升使用者的體驗。

原文地址 developer.android.com/topic/perfo…

本文由 愛學園平臺 進行聯合編輯整理輸出

原作者:愛學園——莫比烏斯環

使用者經常會避免下載看起來太大的應用程式,尤其是在裝置連線到2G和3G網路或付費網路的應用市場內部。這篇文章講述如何減少您的應用程式APK的大小,以便使更多的使用者下載您的應用程式。

理解APK檔案結構

在討論如何降低您的應用程式的大小之前,理解一個應用程式的APK的檔案結構它是非常有幫助的。一個APK檔案包含一個ZIP歸檔,主要包含您的應用程式的所有檔案。這些檔案包括Java類檔案,資原始檔,和包含編譯的檔案資源。

一個APK包含如下目錄:

  • META_INF/:包含CERT.SFCERT.RSK 簽名檔案,以及MANIFEST.MF清單檔案。
  • assets/:包含應用程式的資產,應用程式可以使用一個AssetManager物件檢索。
  • res/:包含不能被編譯進resources.arsc檔案的資源。
  • lib/:包含編譯後的程式碼,是特定於處理器的軟體層。這個目錄包含為每個平臺型別分配的子目錄,像armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,和mips

APK還包含以下檔案。其中,只有AndroidManifest.xml是強制性的。

  • resources.arsc:包含編譯的資源。這個檔案包含來自資料夾res/values/下所有配置的XML內容。包裝工具提取這個XML內容,將它編譯成二進位制形式並且歸檔這些內容。這個內容包括語言字串和樣式以及路徑下不存在於resources.arsc檔案的內容,比如佈局檔案和圖片。
  • classes.dex:包含DEX檔案中編譯的類可以被 Dalvik/ART 虛擬機器理解的格式。
  • AndroidManifest.xml:包含Android的核心清單檔案。這個檔案列出了應用的名稱,版本,訪問許可權,以及引用的庫檔案的應用程式。檔案使用Android的二進位制XML格式。

減少資源的數量和大小

APK的大小影響應用程式的載入速度,對記憶體的使用,和它對電量的消耗。一個使您的APK較小的簡單方式是減少它包含資源的數量和大小。特別是,您可以移除應用程式不再使用的資源,和您可以使用可拉伸的Drawable影像檔案。本節討論這些方法以及其他幾個方法都是通過減少您的應用程式使用的資源來減少您的APK總體規模。

移除不使用的資源

Android Studio 內部包含一個靜態程式碼分析器lint工具,使用它可以檢索資原始檔夾res/,找出您的程式碼不引用的資源。當lint工具在您的工程中發現一個潛在的未被使用的資源,它會列印一條訊息就像下面的例子。

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
    to be unused [UnusedResources]
複製程式碼

⚠️:lint工具不能掃描assets/目錄,通過反射引用assets,或您連線庫檔案到您的應用。同時,它不會刪除資源;只提醒你他們的存在

您的程式碼依賴的庫檔案或許包含不被使用的資源。如果在您的build.gradle構建檔案中啟用shrinkResources,它表示Gradle會自動刪除這些資源。

android {
    // Other settings

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
複製程式碼

為了使用shrinkResources,你必須啟用程式碼壓縮。在構建過程中,首先ProGuard刪除未被使用的程式碼但是閒置未被使用的資源。然後Gradle移除未被使用的資源。

關於ProGuard和其它Android Studio提供的途徑,幫助您減少APK大小的更多的資訊,可以參照Shrink Your Code and Resources

在Android Gradle外掛0.7和更高版本上,您可以宣告您的應用程式支援的配置。Gradle通過使用resConfigresConfigs類別以及預設配置defaultConfig選項,去構建系統。然後構建系統可以防止不受支援的配置的資源,出現在APK檔案中,從而減少APK檔案的大小。關於此功能的更多資訊,可以檢視 Remove unused alternative resources

最小化來自於外部庫資源的使用

當我們開發一個Android應用程式時,您通常使用外部庫來提高應用程式的可用性和多功能性。例如,您可以引用Android Support Library來改善應用程式在舊裝置上的使用者體驗,或者您可以在您的應用程式內部使用Google Play Services來檢索文字,對其進行自動翻譯。

如果一個庫被設計用於一個伺服器或桌面。它可能包括許多您的應用程式不需要的物件和方法。為了做到只包括您需要的部分,如果庫的證照允許您可以編輯該庫的檔案。您還可以使用一個替代的方法,移動指定的功能模組到您的應用程式。

⚠️:ProGuard可以清除一些不必要的程式碼匯入庫,但是不能移除一個庫龐大的內部依賴關係。

僅僅支援特定密度

Android支援一組非常大的裝置,包括各種各樣的螢幕密度。在Android 4.4(API級別19)和更高版本,該框架支援各種密度:ldpimdpitvdpihdpixhdpixxhdpixxxhdpi。即使Android支援所有的這些密度,您也不需要為每個密度匯出相應的資源。

如果您知道只有一小部分使用者的裝置擁有特定的密度,這些密度是否需要包括到您的應用程式中那是值得考慮的。對應於一個特定的螢幕密度,如果您不包括相應的資源,Android會根據其它螢幕密度,找尋與之相近的現有資源。

如果您的應用程式只需要按比例縮小的影像,在drawable-nodpi/檔案下放置唯一影像即可,這樣您可以節省更多的空間。我們建議每一個應用程式包括至少一個xxhdpi影像變體。

關於螢幕密度的更多資訊,可以參見Screen Sizes and Densities

使用drawable物件

一些影像不要求一個靜態的圖片資源;框架可以在執行時動態畫出影像。Drawable物件(<shape>在XML內)在您的APK內部可以佔用少量的空間。另外,XMLDrawable物件產生的單色影像符合材料設計(material design)的指導方針。

減少資源

對於一個影像的變化,您可以包括一個單獨的資源,如著色、陰影或旋轉版本相同的影像。不管怎樣,我們建議您重用相同的一組資源,在執行時根據需要自定義它們。

Android 提供了一些工具來改變資源的顏色,在Android5.0(API21)和更高的版本上可以使用android:tint和tintMode屬性。對於較低的平臺版本,可以使用ColorFilter類。

對於一種資源經過旋轉可以得到另一種資源,您可以只匯入一個。下面的程式碼片段提供了一個示例:將一個“thumb up”繞軸旋轉180度轉變成“thumb down”:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_thumb_up"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="180" />
複製程式碼

影像通過程式碼渲染

您還可以通過程式渲染您的圖片,進而減少您的APK大小。程式渲染騰出空間,因為您不需要儲存圖片到你的APK檔案中。

Crunch PNG 檔案

在構建過程中,aapt工具可以使用無失真壓縮方式優化放置在res/drawable/目錄下的影像資源,進而降低圖片空間佔用。例如,the aapt tool can convert a true-color PNG that does not require more than 256 colors to an 8-bit PNG with a color palette(這句不好翻譯).這樣轉換之後,相同質量的影像會佔用更小的記憶體。

請注意,aapt擁有以下限制:

  • aapt不能壓縮asset/目錄下的PNG檔案。
  • aapt工具優化影像檔案需要使用256或更少的顏色。
  • aapt工具可能多次優化已經壓縮過的PNG檔案。為了避免這個您可以在Gradle檔案中使用cruncherEnabled標誌禁用對PNG檔案的處理。
aaptOptions {
    cruncherEnabled = false
}
複製程式碼

壓縮 PNG 和 JPEG 檔案

您可以在不丟失影像質量的情況下使用工具減少 PNG 檔案的大小,像 pngcrush, pngquant, or zopflipng。所有這些工具可以減少PNG檔案的大小,同時保留感知的影像質量。

pngcrush工具是特別有效的:這個工具遍歷PNG過濾器和zlib(Deflate)引數,使用過濾器和引數的組合壓縮影像。然後選擇壓縮效果最好的配置輸出(It then chooses the configuration that yields the smallest compressed output)。

為了壓縮 JPEG 檔案,你可以使用工具比如: packJPG and guetzli.

使用 WebP 格式檔案

針對Android 3.2(API級別13)和更高版本,您還可以使用WebP影像格式檔案,而不是使用 PNG 和 JPEG 檔案。WebP格式提供了有失真壓縮(如JPEG)以及透明度(如PNG),相較於JPEG或PNG它可以提供更好的壓縮。

使用Android Studio,您可以轉換存在的 BMP,JPG,PNG 或 靜態的GIF影像為 WebP格式。更多的資訊,請參考 Create WebP Images Using Android Studio

⚠️:谷歌市場接受APK的啟動圖示只能是PNG格式。

使用向量圖

您可以使用向量圖形建立解析度無關圖示和其他可伸縮的媒體。使用這些圖形可以大大減少你的APK大小。在Android中,向量圖表示為VectorDrawable物件。VectorDrawable物件,一個100位元組的檔案可以用於呈現螢幕大小的影像。

然而,系統需要大量的時間來呈現每個VectorDrawable物件,且影像越大呈現到螢幕上所需要的時間越長。因此,考慮只有當顯示小影像時使用這些向量圖形。

更多的關於VectorDrawable物件如何工作的資訊,可以參考Working with Drawables

使用向量圖形動畫影像

不能使用 AnimationDrawable物件去建立幀動畫,因為這樣做對於每一幀動畫都需要包括一個單獨的bitmap檔案,這大大增加APK的大小。

您應該使用AnimatedVectorDrawableCompat去建立向量動畫

減少 native 和 Java 程式碼

這裡有幾種方法可以用來減少在您的應用程式中 Java 和 native 程式碼庫的大小。

移除不必要生成的程式碼

確定瞭解由工具自動生成的任何程式碼的足跡(Make sure to understand the footprint of any code which is automatically generated)。例如,許多協議緩衝工具生成過多的方法和類,它可以使您的應用程式大小提升兩倍或三倍。

避免使用列舉

一個列舉可以使您的應用程式的classes.dex檔案增加約 1.0 到 1.4 KB大小。對於複雜系統或共享庫這些新增可以迅速增大此檔案。如果可能的話,可以考慮使用@IntDef註解和ProGuard去剝離列舉,取出將它們轉換為整數。這種型別的轉換儲存所有的列舉型別是安全可靠的。

減少本地二進位制檔案的大小

如果您的應用程式使用原生程式碼和Android NDK,你也可以減少你的應用程式的釋出版本的大小,以優化你的程式碼。兩個有用的技術去除除錯符號而不是提取本地庫。

移除除錯符號

使用除錯符號的意義,在於您的應用程式在開發過程中仍然需要除錯。使用arm-eabi-strip工具(Android NDK 提供),從本地庫刪除不必要的除錯符號。 之後,您可以編譯釋出構建。

避擴音取本地庫

在構建應用程式的釋出版本時,如果您在你的應用程式的清單檔案中,元素標籤下設定屬性如下android:extractNativeLibs="false",則APK包將不會壓縮.so檔案。禁用這個標誌可以在應用的安裝過程中,阻止PackageManager從您的APK拷貝.so檔案到檔案系統,這樣做的好處是讓您更新您的應用程式更小。

維護多個精簡的 APKS

APK可能包含一些使用者下載但從未使用的內容,如區域或語言資訊。為使用者建立一個最小的下載,你可以將您的應用程式分割成幾個apk,分化的因素如螢幕大小或GPU硬體的支援。

當使用者下載您的應用程式,他們的裝置基於裝置的功能和設定接收到正確的APK。這種方式,裝置沒有接收到資產裝置沒有的功能。例如,如果使用者擁有一個hdpi裝置,他們不需要相對於更高密度的裝置包括的xxxhdpi資源。

更多相關資訊,請參考 Configure APK SplitsMaintaining Multiple APKs

相關文章