性能優(yōu)化(6)-減小APK體積

主目錄見:Android高級進階知識(這是總目錄索引)
[譯]Reduce APK Size
?個人感覺這篇文章寫得還是比較全的,所以這里就來翻譯一下這篇文章,希望大家看了能有所收獲。這篇是谷歌自己的文章,應(yīng)該來說還是比較權(quán)威的,APK的體積變小意味著用戶下載時候需要的流量也比較少,而且下載時間相對縮短,這是一個不錯的用戶體驗。

一.目標

今天翻譯這篇文章主要是為了大家了解有這么多可以縮小apk包體積的方法,在自己寫代碼的時候可以適當?shù)刈⒁猓越裉炷繕巳缦拢?br> 1.了解減小apk體積的若干方法;
2.能在實際場景中考慮和使用到里面的方法。

二.譯文

?用戶通常會避免下載大應(yīng)用,特別是市場上的設(shè)備連接到2G和3G或者按字節(jié)流量付費。這篇文章重點描述了怎么減少你應(yīng)用Apk的體積,讓更多的用戶能下載它。

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

?在討論怎么減小Apk體積之前,理解一個應(yīng)用的APK結(jié)構(gòu)是非常有幫助的。一個apk文件就是由一個zip壓縮包組成,這個zip包含了所有組成你應(yīng)用的文件。這些文件包含了java的字節(jié)碼文件,資源文件和一個包含了編譯后資源的文件。
?一個apk包含了如下的目錄:

  • META-INF/: 包含了CERT.SFCERT.RSA簽名文件,以及MANIFEST.MFmanifest 文件.
  • assets/:包含了應(yīng)用的assets,應(yīng)用可以通過 AssetManager對象來獲取這些資源.
  • res/:包含了沒有被編譯成resources.arsc的所有資源.
  • lib/:包含了用于軟件處理的編譯后的代碼,這個目錄還包含了針對不同平臺類型的子目錄 armeabi, armeabi-v7a, arm64-v8a, x86, x86_64mips.

一個APK還包含了如下的文件,但是其中只有AndroidManifest.xml是必須的。

  • resources.arsc:包含了編譯后的資源。這個文件包含了res/values/文件夾下面的所有XML文件內(nèi)容,打包工具抽取了XML文件內(nèi)容,并把它編譯成二進制文件格式,并且進行壓縮。該內(nèi)容包含了language strings(語言相關(guān)的字符串)和styles,并且包括沒有直接放在resources.arsc文件中的內(nèi)容路徑,比如layout文件以及圖片文件.
  • classes.dex:包含了class文件編譯成的dex文件,這是可以被Dalvik/ART虛擬機識別的文件格式。
  • AndroidManifest.xml:包含了Android核心manifest文件。這個文件羅列了應(yīng)用的名字,版本,訪問權(quán)限和引用的library庫。該文件采用Android的二進制XML格式。

2.減小資源數(shù)量和體積

?你apk的體積直接影響了你程序的加載速度,占用內(nèi)存大小,消耗的電量。一個簡單有效的方法是減小apk包含的資源的數(shù)量和體積。特別是,你可以移除一些不再使用的資源,或者你可以使用一些可擴展的 Drawable對象來替代圖片資源。這部分主要討論這些方法以及另外可以減少資源占用的方法。

移除無用的資源

?使用Lint工具,這是一個Android Studio中的靜態(tài)代碼分析工具,可以檢測 res/文件夾中沒有被引用的資源(其實Lint工具不僅僅有這個功能)。當Lint檢測到工程中潛在的不被使用的資源,它就會打印出如下消息:

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

注意:Lint不會掃描assets/文件夾,assets資源是通過反射來引用的,或者應(yīng)用中引用的其他library庫。他只是給你一個警告,并不會幫助你移除這些無用的資源。

同時你添加到代碼中的library庫可能包含無用的資源,如果你在app里的build.gradle中啟用了shrinkResources,那么Gradle 會自動幫你移除這些無用的資源。

 android {
    // Other settings

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

為了使用shrinkResources,你必須開啟 code shrinking。在build階段,ProGuard會移除無用的代碼但是會保留無用的資源。然后Gradle過后會移除無用的資源。

更多的關(guān)于ProGuard 和使用Android Studio 幫助你減少Apk體積的方法,參閱Shrink Your Code and Resources.

在Android Gradle Plugin 0.7或者更高的版本,你可以聲明app支持的所有配置,Gradle會把resConfigresConfigsflavors 和defaultConfig等選項信息傳遞給構(gòu)建系統(tǒng)。構(gòu)建系統(tǒng)會防止一些不受支持的資源出現(xiàn)在你的apk中,從而減少你的apk體積。想要了解更多這方面的特性,參考Remove unused alternative resources

最小化Library庫中資源使用

?在開發(fā)android應(yīng)用的時候,你經(jīng)常會使用外部的library庫來提升你app的可用性和擴展性。例如,你會引入Android Support Library來提升用戶在舊機型上面的體驗,或者你會使用Google Play Services來自動翻譯你app中的文本。
?如果一個library庫被設(shè)計為一個服務(wù)或者桌面,那么它就會包含很多你應(yīng)用沒有用到的對象和方法。為了只包含你工程中用到的部分,你可以修改這個庫文件,如果license許可的話,同時你也可以使用另外的移動端友好的庫來給你的app增加特定的功能。
注意:ProGuard能清除導(dǎo)入庫中一些無用的代碼,但是不能清除庫中大的內(nèi)部依賴。

只支持特定的分辨率

?Android支持了非常多的不同分辨率的設(shè)備,在Android 4.4 (API level 19)或者更高版本,框架支持了不同的分辨率:ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpixxxhdpi,雖然Android支持了這些所有的分辨率,但是你并不需要導(dǎo)出所有柵格化的assets到各個分辨率。
?如果你知道只有一小部分用戶使用特定分辨率的設(shè)備,請考慮是否需要支持這些分辨率。如果你沒包含特定屏幕分辨率的資源,那么Android會自動縮放其他分辨率的資源來支持。
?如果你的app只需要縮放的圖片,那么為了節(jié)省空間你可以使用單個版本在drawable-nodpi/中的圖片,我們建議,每個應(yīng)用都至少有一個xxhdpi版本的圖片。
想要更多關(guān)于屏幕分辨率的信息,請參照Screen Sizes and Densities

使用drawable對象

?一些圖片不需要一個靜態(tài)圖片資源:框架能在運行期動態(tài)繪制一張圖片,Drawable對象(<shape> in XML)只在你的apk中占用非常小的控件。另外,XML Drawable對象能生成符合material design指南的單色圖。

重用資源

?你可能會針對一張圖片的不同變形而提供單獨的資源,比如圖像的著色(tinted),陰影(shaded)或者旋轉(zhuǎn)(rotated)。我們建議就算這樣,你可以重用這個資源,在運行期時候根據(jù)需要進行自定義。
?Android提供了不同的工具來改變一個asset的顏色,比如在Android 5.0 (API level 21) 或者更高版本上面使用android:tinttintMode屬性 ,在低版本平臺上則可以使用 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_thumb_up"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="180" />
從代碼中渲染

?同時你也可以在程序上渲染你的圖片以減少apk的體積,程序渲染將會釋放你的很多空間,因為你的程序中不會存儲一張圖片資源。

處理PNG文件

?在build階段,aapt工具能利用無損壓縮來優(yōu)化放在res/drawable/中的圖片資源。例如,aapt工具能利用顏色調(diào)色板(color palette)將不需要多于256種顏色的真彩色PNG轉(zhuǎn)化成一個八位PNG。這樣做會產(chǎn)生一個質(zhì)量相同但是占用空間小的圖片。
?但是要記住appt工具有如下的限制:

  • aapt工具不會壓縮包含在asset/中的PNG文件
  • 圖片文件需要256個或者更少的顏色來給aapt工具優(yōu)化
  • aapt工具可能會使已經(jīng)壓縮過的PNG文件變大,為防止這種情況發(fā)生,你可以在Gradle中使用cruncherEnabled 標識來禁用PNG文件的處理。
aaptOptions {
    cruncherEnabled = false
}
壓縮PNG和JPEG文件

?你能使用工具來減少PNG圖片的體積但是不損失他的質(zhì)量,例如: pngcrush,pngquant或者zopflipng.這些工具都可以減少PNG的體積但是不損失他的質(zhì)量。

pngcrush工具是非常高效地:這個工具會迭代所有的PNG文件過濾器和zlib (Deflate) 參數(shù)。利用過濾器和參數(shù)的組合來壓縮一張圖片,然后他會選擇產(chǎn)生最小壓縮輸出的配置。

壓縮JPEG圖片你可以利用 packJPGguetzli.

使用WebP文件格式

?在Android 3.2 (API level 13)或者更高版本上面,你同時可以使用WebP文件格式來替代你的PNG和JPEG文件。WebP格式提供了有損壓縮(如JPEG)和透明度(如PNG),但是他能提供更好的壓縮比。

?你可以用Android Studio來將BMP,JPG,PNG 或者靜態(tài)GIF 圖片轉(zhuǎn)化為WebP格式,想要更多信息,請參考Create WebP Images Using Android Studio

注意:Google Play只接受launcher icons為PNG格式的apk。

使用矢量圖片

?你可以使用矢量圖片來創(chuàng)建分辨率獨立的圖標或者其他可伸縮媒體。使用這些圖形可以大大減少你的apk體積,矢量圖片可以在 VectorDrawable展示,使用VectorDrawable對象可以用100字節(jié)的文件就產(chǎn)生一張屏幕大小的文件。

但是不足的是,他需要大量時間來渲染VectorDrawable對象,如果圖片更大,需要的時間可能會更多。所以在展示小圖片的時候我們可以考慮使用矢量圖片。

想要了解更多使用VectorDrawable對象的信息,請看Working with Drawables

使用矢量圖來替換動畫圖片

?不用使用 AnimationDrawable來創(chuàng)建逐幀動畫,因為這么做需要你為每一幀動畫都包含一張bitmap圖片,這樣會大大增加你的apk體積。

作為替代,你可以使用 AnimatedVectorDrawableCompat來創(chuàng)建 animated vector drawables

3.減少Native和java的代碼

這里有幾種減少你Native和java代碼庫的方法:

減少不必要的生成的代碼

?確保你能夠理解任何自動生成的代碼的部分。例如,一些protocol buffer工具能生成一些多余的方法和類,這無疑會是你的app體積變成兩倍三倍。

避免使用枚舉

一個獨立的枚舉能增加1.0到1.4KB的大小到你的classes.dex文件,對于一些復(fù)雜系統(tǒng)或者共享庫可能增加的會更快。如果可能,你可以使用@IntDef注解和ProGuard來去掉枚舉并將它們轉(zhuǎn)化為整型。這種類型轉(zhuǎn)換保留了枚舉的所有類型安全的好處。

減少本地二進制文件的大小

如果你的程序使用native代碼或者android NDK,那么你也可以優(yōu)化你的代碼來減少apk體積,兩個有用的方式是刪除debug標記,不提取本地庫。

1.移除debug標記
如果你的應(yīng)用在開發(fā)中并且需要調(diào)試,那么讓你的debug 標記有意義。使用android NDK中提供的arm-eabi-strip工具來移除native庫中不需要的debug標記。之后,再編譯你的release版本。

2.避免抽取native庫
.so文件未壓縮存儲于apk中,然后在你app manifest中的<application>設(shè)置android:extractNativeLibs標記為false,這樣會防止 PackageManager在安裝過程中從apk中拷貝出來.so文件,而且還會帶來一個好處就是會使你的app差分更新變得更小。

保持多個精簡版apk

你的apk可能會包含有用戶下載了但是未使用到的內(nèi)容,比如區(qū)域或者語言信息。為了給你用戶創(chuàng)建最小化的下載,你可以將你的app分出多個apk,并且根據(jù)屏幕尺寸和GPU紋理支持等因素來細分。

當用戶下載你的應(yīng)用的時候,他的設(shè)備就會根據(jù)設(shè)備特征和配置來獲取正確的apk。這樣,設(shè)備不會接收設(shè)備沒有的功能的資源。例如,用戶有hdpi的設(shè)備,他們就不需要為更高分辨率設(shè)備準備的xxxhdpi資源。

想要更多信息的話,請參考 Configure APK SplitsMaintaining Multiple APKs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,237評論 25 708
  • 1、 前言 如果你對App優(yōu)化比較敏感,那么Apk安裝包的大小就一定不會忽視。關(guān)于瘦身的原因,大概有以下幾個方面:...
    未來的理想閱讀 11,448評論 4 39
  • 是四月末,熬人的月份最后幾天,好在放假了,離開學(xué)校時一直膽戰(zhàn)心驚,說是下雨的天氣縱使再是晴,我也不敢昂首。那晚的天...
    劉賦閱讀 256評論 0 0
  • 談一個三流大學(xué)的大學(xué)新生的生活,今天是十月二十八號一個月有這么不知不覺的過去了,說實話我覺得自己過得很稀爛,為什...
    柒易先生閱讀 329評論 1 0