如何給你的Android 安裝檔案(APK)瘦身,androidapk

來源:互聯網
上載者:User

如何給你的Android 安裝檔案(APK)瘦身,androidapk

本文源地址:如何給你的Android 安裝檔案(APK)瘦身

Android的apk檔案越來越大了這已經是一個不爭的事實。在Android 還是最初版本的時候,一個app的apk檔案大小也還只有2 MB左右,到了現在,一個app的apk檔案大小已經升級到10MB到20MB這個範圍了。apk檔案大小的爆炸式增長主要是因為使用者對app品質的期待越來越高以及開發人員的開發經驗增長,具體體現在以下幾個方面:

  • Android裝置 dpi 的多樣化 ([l|m|tv|h|x|xx|xxx]dpi)
  • Android平台的進化,開發工具的改進以及開源類庫生態系統的豐富
  • 使用者對高品質UI的期待
  • 其他原因

Android開發人員在設計一個app的時候應該將最終發布一個輕量級app作為一個最佳實務來考慮。為什嗎?首先這就意味著你擁有了一個簡潔,易維護代碼基礎。其次,官方市集對超過50MB的apk設定了拓展包檔案下載選項,apk檔案在50MB以下更容易讓使用者下載。最後,我們的應用程式環境是一個頻寬有限,儲存空間有限的環境,apk安裝檔案越小,下載就會越快,安裝也會更快,良性迴圈,最後說不定使用者因為這個給好評。

在大部分情況下,apk大小的增長是為了滿足消費者的需要和期待。然而,我認為apk大小的增速已經超過了使用者對app期待的增速。所以,很大程度上,官方市集裡面的那些程式可以瘦身至它們現在大小的一半甚至更多。在這篇文章裡面,我將寫下一些關於如何給apk檔案瘦身的招式,希望你們能夠喜歡。

 

APK 檔案格式

在說如何給apk瘦身之前,讓我們先來看看apk檔案內部的結構到底是怎麼一回事。說簡單點,一個apk檔案就是包含一些檔案的壓縮包。作為開發人員,我們通過使用 unzip 命令解壓這個apk檔案一探apk的內部結構。下面的檔案結構就是我們使用 unzip <your_apk_name>.apk1這個命令所獲得的:

/assets/lib  /armeabi  /armeabi-v7a  /x86  /mips/META-INF  MANIFEST.MF  CERT.RSA  CERT.SF/resAndroidManifest.xmlclasses.dexresources.arsc

我們可能對上面大部分的檔案和目錄都很熟悉。它們和我們在實際開發app的時候所看到得項目結構一樣,包含了:  /assets/lib/resAndroidManifest.xml. 還有一些檔案可能是我們第一次看到。一般說來, classes.dex, 它包含了我們所寫的Java代碼經過編譯後class檔案; resources.arsc 包含了先行編譯之後的資源檔(比如values檔案,XML drawables 檔案等。)。

由於apk檔案只是一個簡單地壓縮檔,這就意味著它有兩種大小:即壓縮前的大小和壓縮後的大小。這篇文章我將主要關注壓縮後的大小。

如何減少apk檔案大小

減少apk檔案大小可以從幾個方面入手。由於每個app都是不同的,所以沒有什麼絕對規則來給apk檔案瘦身。作為apk檔案的三個重要組成部分,我們可以考慮從它們開始入手:

  • Java 原始碼
  • 資源檔(resources/assets)
  • native code

所以接下來的招式都是由減少這些組件的大小出發,進而減少整個app的大小。

掌握良好的編碼習慣

這是減少apk檔案至關重要的第一步。你要對自己的代碼了如子掌。你要移除掉所有無用處的dependency libraries,讓你的代碼一天比一天優秀,持續地最佳化你的代碼。總而言之,保持一個簡潔,最新的代碼基礎是減少apk檔案至關重要的一環。

當然,從零開始一個項目並為這個項目保持一份簡潔的代碼基礎很容易。項目越老,這個工作就越困難。事實上,擁有一大段曆史背景的項目必須要去處理各種無作用程式碼和無用代碼。還好有許多的開發工具可以幫我們來做這些事情……

使用 Proguard

Proguard 是一個很強悍的工具,它可以幫你在代碼編譯時間對代碼進行混淆,最佳化和壓縮。它有一個專門用來減少apk檔案大小的功能叫做 tree-shaking。Proguard 會遍曆你的所有代碼然後找出無用處的代碼。所有這些不可達(或者不需要)的代碼都會在產生最終的apk檔案之前被清除掉。Proguard 也會重新命名你的類屬性,類和介面,然整個代碼儘可能地保持輕量級水平。

也許現在你會認為 Proguard 是一個相當有效地工具。但是能力越大,責任也就越大。現在許多開發這認為Proguard有點讓人不省心,因為它會重度依賴反射。哪些類或者屬性需要被處理或者不能處理都要開發人員對 Proguard 進行配置。

廣泛使用 Lint

Proguard 只會對 Java 代碼起作用,那麼對哪些資源檔呢?比如一張圖片 my_image 在 res/drawable 檔案夾中,沒有被使用,Proguard 只會移除掉 R 類中的引用,但是圖片依然還在檔案夾中。

Lint 一個靜態程式碼分析器,你只需通過調用 ./gradlew lint這個簡單地命令它就能幫你檢查所有無用的資源檔。它在檢測完之後會提供一份詳細的資源檔清單,並將無用的資源列在“UnusedResources: Unused resources” 地區之下。只要你不通過反射來反問這些無用資源,你就可以放心地移除這些檔案了。

Lint 會分析資源檔(比如 /res 檔案夾下面的檔案) ,但是會跳過 assets 檔案 ( /assets 檔案夾下面的檔案)。事實上assets 檔案是可以通過它們的檔案名稱直接存取的,而不需要通過Java引用或者XML引用。因此,Lint 也不能判定某個 asset 檔案在項目中是否有用。這全取決於開發人員對這個檔案夾的維護了。如果你沒有使用某個asset 檔案,那麼你就可以直接清除這個檔案。

對資源檔進行取捨

Android 支援多種裝置。Android的系統設計讓它可以支援裝置的多樣性:螢幕密度,螢幕形狀,螢幕大小等等。到了Android 4.4,它支援的螢幕密度包括: ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi and xxxhdpi。但是你要知道的一點是,Android 支援這麼多的螢幕密度並不意味著你需要為每一個螢幕密度提供相應的資源檔。

如果你知道某些螢幕密度的裝置只有很少部分使用者在使用,那麼你就可以直接不需要使用相應螢幕密度的資源檔。就我個人而言,我只會為我的應用程式提供 hdpi, xhdpi and xxhdpi2 這幾個螢幕密度的支援。如果某些裝置不是這幾個螢幕密度的,不用擔心,Android 系統會自動使用存在的資源為裝置計算然後提供資源檔。

我這麼做得原因很簡單。首先,這些裝置螢幕密度就能覆蓋我 80% 的使用者。其次,xxxhdpi 這個螢幕密度只是在為未來的裝置做準備,但是未來還未到來。最後,我真的不怎麼關心低螢幕密度(比如mdpi 和 ldpi),無論我為這幾個螢幕密度努力,結果都是令人傷心地,還不如直接讓Android系統對 hdpi 資源檔進行適當地縮放來匹配相應地低端機型。

同樣地,在 drawable-nodpi 檔案夾裡面維持一個檔案也能節省空間的。當然前提是你覺得對這個檔案進行相應地縮放之後呈現的效果你能接受或者這個檔案出現的幾率很少。

資源檔最少化配置

Android 開發經常會依賴各種外部開原始碼庫,比如Android Support Library, Google Play Services, Facebook SDK 等等。但是這些庫裡面並不是所有的資源檔你都會用到。比如, Google Play Services 裡面會有一些為其他語種提供翻譯,而你的app又不需要這個語種的翻譯,而且這個庫裡面還包含了我的app中不支援的 mdpi 資源檔

還好從Android Gradle Plugin 0.7 開始,你可以配置你的app的build系統。這主要是通過配置resConfig 和 resConfigs 以及預設的配置選項。下面的 DSL (Domain Specific Language)就會阻止 aapt(Android Asset Packaging Tool)打包app中不需要的資源檔。

defaultConfig{    // ...    resConfigs"en","de","fr","it"    resConfigs"nodpi","hdpi","xhdpi","xxhdpi","xxxhdpi"}



壓縮圖片

Aapt(Android Asset Packaging Tool)就內建了 保真映像壓縮演算法。例如,一個只需 256 色的真彩PNG圖片會被aapt 通過一個顏色調色盤轉化成一個 8-bit PNG 檔案。這可以協助你減少圖片檔案的大小。當然你還可以通過Google尋找相應的最佳化工具,比如 pngquant, ImageAlpha 和 ImageOptim 等。你可以從中選擇一個適合你的工具。

還有一種只在Android平台上存在的圖片檔案也可以最佳化,它就是 9-patches。就我目前所知道,我還沒發現有這個檔案的最佳化工具。然而你只需要求你的設計師將它的可擴充地區和內容地區儘可能地減少即可。這不但可以減少資源檔的大小,還能使得以後資源檔的維護變得更加簡單。

限制app支援的cpu 架構的數目

一般說來Android 使用Java 代碼即可以滿足大部分需求,不過還是有一小部分案例需要使用一些 native code。就像之前對資源檔那樣opinionated,你可以這些 native code opinionated。 在當前的Android 生態系統中,讓你的app支援 armabi 和 x86 架構就夠了。這裡有一篇相當不錯的關於如何瘦身native 程式碼程式庫的文章,你可以參考參考。

儘可能地重用

重用資源可能是你在進行移動開發時需要瞭解的最重要的最佳化技巧之一。比如在一個 ListView 或者 RecyclerView,重用可以協助你在列表滾動時保持介面流暢。重用還可以幫你減少apk檔案的大小。例如,Android 提供了幾個工具為一個asset檔案重新著色,在Android L中你可以使用 android:tint 和 android:tintMode 來達到效果,在老版本中則可以使用 ColorFilter 。

如果系統中有兩種圖片,一種圖片是另一種圖片翻轉180°得到的,那麼你就可以移除一種圖片,通過代碼實現。比如你現在有兩種圖片分別命名為 ic_arrow_expand 和ic_arrow_collapse :

 

你可以直接移除掉 ic_arrow_collapse 檔案,然後在ic_arrow_expand 的基礎上建立一個 RotateDrawable 。這個方法也可以讓你減少設計人員的工作:

<?xmlversion="1.0"encoding="utf-8"?><rotatexmlns:android="http://schemas.android.com/apk/res/android"    android:drawable="@drawable/ic_arrow_expand"    android:fromDegrees="180"    android:pivotX="50%"    android:pivotY="50%"    android:toDegrees="180"/>

 

在合適的時候使用代碼渲染映像

在某些情況下,直接使用Java 代碼渲染映像也能獲得不錯的效果。比如逐幀動畫就是一個很好的例子。最近我都在嘗試一些Android Wear 的開發,瞭解了一下Android wearable support library。就像其他的Android support library 一樣,這個庫裡面也有一些工具來處理穿戴裝置的。

不過讓我吃驚的是,當我簡單地構建了一個 “Hello World”樣本,最後得到的apk檔案竟然有1.5MB。於是我快速地研究了一下 wearable-support.aar 檔案,發現這個庫有兩個逐幀動畫,並分別支援了3種不同的螢幕密度:一個 “success” 動畫 (31 frames) 和一個 “open on phone” 動畫 (54 frames)。

 

這個逐幀success動畫是被一個叫做 AnimationDrawable 所定義的:

<?xmlversion="1.0"encoding="utf-8"?><animation-listxmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="true">    <itemandroid:drawable="@drawable/generic_confirmation_00163"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00164"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00165"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00166"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00167"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00168"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00169"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00170"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00171"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00172"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00173"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00174"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00175"android:duration="333"/>    <itemandroid:drawable="@drawable/generic_confirmation_00185"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00186"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00187"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00188"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00189"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00190"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00191"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00192"android:duration="33"/>    <itemandroid:drawable="@drawable/generic_confirmation_00193"android:duration="33"/></animation-list>


這樣做得好處就是 (我當然在諷刺) 每幀顯示33ms,這使得整個動畫保持在30fps的頻率。如果每幀16ms這將會導致整個庫是之前的兩倍大。如果你去看源碼你會發現很有趣。在 generic_confirmation_00175 這一幀 (15 行) 將持續顯示 333ms。 generic_confirmation_00185 緊跟著它。這個最佳化節省了9個類似的幀 (包含了從176 幀到 184 幀) 。不過最後神奇的是 wearable-support.aar 竟然神奇的包含了這個9個完全無用的幀,而且還以3中螢幕密度展示。3

在代碼中來渲染這樣的動畫明顯會很花時間。然而當你維持動畫運行在60fps這樣的頻率可以大幅度的減少apk的大小。在寫這篇博文的時候,Android還沒提供工具來渲染這樣的動畫。但是我希望Google正在開發新一代的輕量級即時渲染系統來保證material design的細節呈現。當然“Adobe After Effect to VectorDrawable” 之類的設計工具也能提供很多方便。

如何更進一步?

上面所有的招式都集中在app或者library 開發人員。也許我們還可以在app分發渠道方面為apk大小做出一些改變?我想可以在app 散發者端做一些改進,或者在官方市集。例如,我們可以期待官方市集在使用者安裝app的時候為裝置綁定相應的native 庫而摒棄那些無用的。

同樣地,我們還可以想象只根據目標裝置的配置來打包應用。不幸的是,這可能破壞Android 生態一個重要的功能特性:配置熱置換。事實上,Android一開始就是位處理各種即時的配置更改(語言,螢幕轉向)而設計的。如果我們移除掉與目標螢幕不相容的資源檔,這可以極大的減少檔案大小。不過Android需要處理即時的螢幕密度更改。即便我們假設廢除這種功能,我們仍然需要處理為不同的螢幕密度設計的圖片以及其他配置(比如螢幕朝向,最小寬度等)。

伺服器端的apk打包看起來很強大。但這樣會冒很大得風險,因為最終傳送給使用者的apk會於開發人員發給的伺服器的完全不同。分發一些缺失資源檔的apk可能會導致app崩潰。

總結

設計就是在一個約束集裡面找出最好的方案。顯然apk檔案的大小就是一個約束。不要害怕為了讓多個方面變得更好而放鬆一個方面的約束。例如,當你要降低UI的渲染效果時,不要猶豫,因為這可以讓apk的大小減小,同時使得app的運行也更加流暢。你99%的使用者是感受不到UI品質變低的,但是他們會注意到apk檔案變小了,運行也更加流暢了。總之,你需要將app各方面進行整體考慮,而不是僅僅幾個方面的斟酌。

本文翻譯自:Putting Your APKs on Diet

原作者:Cyril Mottier


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.