本篇文章翻譯自Reduce APK Sizehtml
用戶一般不會去下載體積過大的應用程序,特別是當本身的設備鏈接的是 2G/3G 或者按字節付費的網絡。這篇文章描述瞭如何縮減 APK 的體積大小,以使得更多用戶願意下載你開發的應用。android
瞭解APK結構
在討論如何縮減你應用的體積以前,瞭解 APK 結構是很是有益處的。一個 APK 文件包含了一個 ZIP 文件,該 ZIP 文件包含了組成你應用的全部文件,這些文件包括 Java 字節碼文件、資源文件和已編譯資源的文件。 APK 包含下列目錄:git
- <code>META-INF/</code>:包含了<code>CERT.SF</code>、<code>CERT.RSA</code>簽名文件以及<code>MAINFEST.MF</code>mainfest文件
- <code>assets/</code>:包含了應用程序的資源,應用程序能夠經過 AssetManager 檢索資源
- <code>res/</code>:包含了沒有編譯到<code>resources.arsc</code>的資源
- <code>lib/</code>:包含了特定處理器的軟件層的編譯代碼,該目錄包含了每一個平臺類型的子目錄,例如<code>armeabi</code>、<code>armeabi-v7a</code>、<code>arm64-v8a</code>、<code>x86</code>、<code>x86_64</code>和<code>mips</code>
APK 也包含了下列文件,在這些文件之中,只有<code>Mainfest.xml</code>是強制的github
- <code>resources.arsc</code>:包含了編譯的資源,該文件包含了來自<code>res/values/</code>目錄下全部配置的XML內容。打包工具提取此XML內容,將其編譯爲二進制格式,並歸檔內容。此內容包含了語言字符串和樣式,以及未直接包含在<code>resources.arsc</code>文件中的內容路徑,好比佈局文件和圖像。
- <code>classes.dex</code>:包含了能被<code>Dalvik/ART</code>虛擬機識別的 DEX 文件格式編譯的類
- <code>AndroidManifest.xml</code>:包含了 Android 核心的 mainfest 文件。該文件羅列了應用程序的名字、版本、權限和引用的第三方庫。該文件使用 Android 的二進制 XML 格式
減小資源數量和大小
APK 的大小會影響到應用程序啓動的速度、使用的內存和消耗的電量。縮減應用程序大小最簡單的方式之一就是減小它所包含的資源數量和大小。特別是你能夠移除你的應用中再也不使用的資源,或者使用可拓展的 Drawable 對象來替代圖像文件。這部分討論的這些方法以及一些其餘的方式能夠減小你應用程序中的資源從而在總體上減小 APK 體積的大小。web
<br/> ### 移除無用的資源 Android Studio中的靜態代碼檢查工具——[lint](https://developer.android.com/studio/write/lint.html)能夠檢測<code>res/</code>目錄下沒有引用的資源。當 lint 檢查工具發如今你項目中可能存在一個沒有使用的資源,它將會打印出相似以下的信息: ``` res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources] ``` >注意:lint 檢查工具沒有掃描<code>assets/</code>目錄,assets 資源是經過反射的方式,或者連接到應用程序的庫文件來發現引用的。此外,lint 檢查工具並不刪除這些資源,它只是提醒你它們的存在。安全
你使用的一些庫可能包含了一些無用的資源,若是你在 <code>build.gradle</code>文件中開啓了 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 體積大小的方法,能夠查看壓縮代碼和資源網絡
<br/> ### 最小化庫中資源的使用 當開發一個 Android 應用的時候,一般會使用一些第三方庫來提升應用程序的可用性和多功能性。好比,可能使用了 [Android Support Library](https://developer.android.com/topic/libraries/support-library/index.html) 來改善在舊機型上的用戶體驗,或者使用 [Google Play Services](https://developers.google.com/android/guides/overview) 爲應用程序提供自動翻譯。 若是一個庫是爲服務器或者桌面設計的,那麼它一般包含了許多你的應用程序用不到的對象和方法,若是這個庫所使用的協議容許,那麼你能夠修改這個庫文件。固然,你也可使用其餘一些對於移動端友好的庫來爲你的應用程序添加特定功能。 >注意:ProGuard 能夠清理第三方庫中對你應用非必須的代碼,可是它不能移除第三方庫的大型內部依賴項。app
<br/> ### 只支持特定的分辨率 Android 支持很是大的設備集,擁有着各式各樣的分辨率。在 Android4.4(API level 19)或者更高的系統版本,其框架支持許多分辨率:<code>ldpi</code>、<code>mdpi</code>、<code>tvdpi</code>、<code>hdpi</code>、<code>xhdpi</code>、<code>xxhdpi</code>。儘管 Android 支持全部這些分辨率,可是你並不須要適配每一種分辨率。 若是你知道你的用戶羣中只有一小部分使用具備特定分辨率的設備,考慮你是否須要適配這些分辨率。若是你沒有爲特定分辨率準備資源文件,那麼 Android 將自動縮放最初爲其餘屏幕分辨率設計的現有資源。 若是你的應用程序只須要縮放的圖片,你能夠經過在 <code>drawable-nodpi</code>目錄中使用圖片的單個版原本節省更多的空間。咱們建議每一個應用程序至少包含一個<code>xxhdpi</code>圖片版本。 更多有關屏幕分辨率的信息,能夠查看[屏幕尺寸和密度](https://developer.android.com/about/dashboards/index.html#Screens)框架
<br/> ### 使用drawable對象 一些圖像並不須要一個靜態的圖像資源,frameworker 能夠在運行時動態的繪製出圖像。[Drawable](https://developer.android.com/reference/android/graphics/drawable/Drawable.html) 對象佔用APK中少許空間。此外,XML形式的 [Drawable](https://developer.android.com/reference/android/graphics/drawable/Drawable.html) 對象能夠產生符合<code>Material Design</code>準則的單色圖像。
<br/> ### 重用資源 你能夠爲圖像的不一樣分辨率添加一個單獨的資源,好比同一圖像的着色、陰影或者旋轉版本。可是咱們強烈建議你重用相同的資源集,在運行時根據須要定製它們。 Android 提供了幾個實用程序來更改 asset 的顏色,在 Android5.0(API level 21)或以上版本,可使用<code>android:tint</code>和<code>tintMode</code>屬性。對於較低的系統版本,使用 [ColorFilter](https://developer.android.com/reference/android/graphics/ColorFilter.html) 類 你能夠忽略等價於其餘資源的資源。下列的代碼片斷提供了一個例子,經過在圖像中間旋轉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" /> ```
<br/> ### 從代碼中呈現 咱們還能夠經過程序化渲染圖像來縮減 APK 的體積大小。程序化渲染能夠釋放空間,由於你再也不在APK中保存圖像文件。
<br/> ### 壓縮PNG文件 <code>aapt</code>工具在編譯的過程能夠無損壓縮存放在<code>res/drawable/</code>目錄下的資源。例如,<code>aapt</code>工具能夠將不須要超過256種顏色的真彩色PNG轉換爲具備調色板的8位PNG。 這樣會產生質量相同的圖像,但內存佔用空間更小。 記住aapt有如下限制: - aapt工具不能壓縮<code>asset</code>目錄下的PNG文件 - aapt工具只能優化使用很少於256位顏色的圖像文件 - aapt工具可能會填充已經壓縮過的PNG圖像,爲了防止這種狀況,你可使用 Gradle 中的<code>cruncherEnabled</code>標誌來禁用 PNG 文件的這個過程。 ``` aaptOptions { cruncherEnabled = false } ```
<br/> ### 壓縮PNG和JPEG文件 你可使用相似 [pngcrush](https://pmt.sourceforge.io/pngcrush/)、[pngquant](https://pngquant.org/) 或者 [zopflipng](https://github.com/google/zopfli) 等工具來無損壓縮 PNG 文件。全部這些工具均可以壓縮 PNG 同時保持圖像質量。 pngcrush 工具特別有效:這個工具經過使用過濾器和參數的各類組合來壓縮圖像,在 PNG 過濾器和 zlib(Deflate) 參數上迭代。它選擇最小壓縮輸出的配置。 對於 JPEG 圖像,你可使用相似 [packJPG](http://www.elektronik.htw-aalen.de/packjpg/) 和 [guetzli](https://github.com/google/guetzli) 的工具來壓縮。
<br/> ### 使用WebP文件格式 在Android 3.2(API level 13)或更高版本,除了使用 PNG 或 JPEG 格式的圖像文件,你還可使用 [WebP](https://developers.google.com/speed/webp/) 文件格式的圖像。WebP 格式提供有損壓縮(相似於JPEG)以及透明度(相似於PNG),可是能更好的提供比 JPEG 或 PNG 更好的壓縮效果。 你可使用 Android Studio 轉換 BMP、JPG、PNG或者靜態GIF的圖像爲 WebP 格式。獲取更多信息,查看[建立WebP圖像](https://developer.android.com/studio/write/convert-webp.html) >注意:Google Play 只接受[啓動圖標](https://material.io/guidelines/style/icons.html#)爲 PNG 格式的 APKs
<br/> ### 使用矢量圖形 你可使用矢量圖形建立獨立於分辨率的圖標和其餘可伸縮圖片。 使用這些圖形能夠大大減小APK的大小。矢量圖形在 Android 中表示爲 [VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html) 對象。使用 [VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html) 對象,100字節的文件能夠生成屏幕大小的清晰圖像。 可是系統將會花費大量時間去渲染 [VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html) 對象,對於大的圖像須要更長的時間才能出如今屏幕上。所以,在展現小圖像的時候再考慮矢量圖。 獲取更多有關 [VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html) 對象的信息,查看[使用圖片](https://developer.android.com/training/material/drawables.html)
<br/> ### 使用矢量圖形制做動畫圖像 不要使用 [AnimationDrawable](https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html) 去建立逐幀動畫,由於這樣作須要爲動畫的每一個幀添加單獨的位圖文件,這會增長 APK 的體積大小。 你可使用 [AnimatedVectorDrawable](https://developer.android.com/reference/android/support/graphics/drawable/AnimatedVectorDrawableCompat.html) 爲[矢量圖片添加動畫](https://developer.android.com/training/material/animations.html#AnimVector)
<br/> ## 減小Native和Java代碼 你可使用下列幾種方式去減小應用程序中的 Java 和 native 代碼庫。
<br/> ### 移除沒必要要的生成代碼 確保瞭解自動生成的代碼的足跡。例如,許多協議緩衝工具生成過多的方法和類,這可能使應用程序的大小增長一倍或者兩倍。
<br/> ### 避免枚舉 一個枚舉能夠爲你的應用程序的<code>classes.dex</code>文件增長1.0至1.4KB的大小,對於複雜系統或者共享庫,這些將快速累積。若是可能的話,考慮使用<code>@IntDef</code>註解和 [ProGuard](https://developer.android.com/studio/build/shrink-code.html) 來除去枚舉並將它們轉換爲整數。這種類型轉換保留了枚舉的全部類型安全的好處。
<br/> ### 減少本地二進制文件的大小 若是你的應用程序使用 native code 和 Android NDK,你能夠經過優化代碼來減小應用程序的體積大小。兩種有用的技術是刪除調試符號和避免提取本地庫。 - 刪除調試符號 若是你的應用程序正在開發而且須要調試,那麼使用調試符號是頗有意義的。使用 Android NDK 提供的<code>arm-eabi-strip</code>工具去移除本地庫中沒必要要的調試符號,以後再進行 release版本的編譯。 - 避免提取本地庫 將<code>.so</code>文件保存在 APK 中未壓縮的文件,設置<code> application </code>中的元素<code>android:extracNativeLibs</code>爲false,這將防止 [PackageManager](https://developer.android.com/reference/android/content/pm/PackageManager.html) 在安裝過程當中將<code>.so</code>文件從APK複製到文件系統,而且有一個額外的好處,使得應用程序的增量更新更小。
維護多個精簡版APK
你的 APK 能夠包含用戶下載但從未使用的內容,例如區域或語言信息。爲了讓用戶下載儘可能小的應用程序,你能夠將你的應用程序根據屏幕尺寸或GPU紋理支持等因素細分爲多個 APKs。 當用戶下載你的應用程序的時候,根據他們的設備特色以及設置詳情,他們會接收到正確的 APK,這樣,設備不會接收設備沒有的功能的資源。例如,一個用戶有<code>hdpi</code>的設備,他們不須要爲更高分辨率的設備提供的<code>xxxhdpi</code>的資源。 獲取更多相關信息,能夠查看 Configure APK Splits 和 Maintaining Multiple APKs