主目錄見: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.SF
和CERT.RSA
簽名文件,以及MANIFEST.MF
manifest 文件. -
assets/:包含了應(yīng)用的
assets
,應(yīng)用可以通過 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)容包含了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會把resConfig
和resConfigs
flavors 和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
, xxhdpi
和 xxxhdpi
,雖然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: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_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圖片你可以利用 packJPG和guetzli.
使用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 Splits和Maintaining Multiple APKs