1. 程式人生 > >APK瘦身——更全面的方案

APK瘦身——更全面的方案

前陣子做了一次減包體的工作,過後覺得這項工作還是套路性挺強的,於是綜合當時的經驗以及廣泛瀏覽了網上的各種大牛小牛的部落格,實踐後特總結了以下的一些思路和做法,不求內容最豐富,只求方案更全面。

APK瘦身的目的

瘦身的目的最明顯的一個就是:提高下載轉化率。怎麼理解呢?舉個例子來說,假如你的應用包12MB,有100個潛在使用者想要去下載嘗試使用你的應用,結果有20個使用者嫌棄安裝包太大而直接放棄,有20個使用者在等待下載的過程中取消下載,最終只有60個使用者下載安裝了應用。這時你的應用的實際的下載轉化率其實是 60/100 = 60%。
簡單的總結:安裝包越小,使用者下載等待的時間越短,對手機配置要求的也越小,裝置的體驗愈佳,應用的下載轉化率也就越高。

APK包體的組成

開始瘦身前,需要先了解一下APK都主要由哪些成分組成

  1. classes.dex
    編寫的所有的Java程式碼(包括各種引入的sdk程式碼)最終轉化成在Android虛擬機器上執行需要的位元組碼(和java的位元組碼有一定的區別)

  2. res資料夾
    存放所有資源的資料夾(除了裡面raw資料夾的檔案不會被編譯,其他都會被編譯)

  3. resources.arsc
    編譯後的二進位制資原始檔

  4. assets資料夾
    用於儲存需要保持原始檔案的資原始檔(這部分資源不會被編譯)

  5. lib資料夾
    用於存放應用需要的native庫檔案

  6. AndroidManifest.xml
    程式全域性配置檔案

  7. META-INF資料夾
    存放幾個簽名校驗相關的檔案,用於保證APK的完整性和安全性

  8. 其他
    其他一些配置生成的檔案

分析現APK各成分的比例

瞭解清楚APK的各個組成成分後,就需要有針對性地對自己的APK各個成分做一個比例分析。
工具1:Android Studio 2.2及其以上的版本
Android Studio自2.2版本以來就引入了分析APK各成分的比例的功能,用法也挺簡單的,主要有2種操作方法,如下:

  1. 導航欄的Build →Analyze APK…→選擇APK檔案的路徑→選擇OK開啟即可
    導航欄的Build

    選擇APK檔案的路徑

  2. 可以直接把APK包拖進IDE,也可以得到如下的比例分析構成圖(比例會自動按從大到小排好序呈現)
    比例會自動按從大到小排好序

    可以看出,在其中一般佔比比較大的一般都是dex檔案,res資料夾,assets資料夾,lib資料夾以及resource.arsc檔案。所以接下來的工作就是有針對性地讓這些檔案和資料夾儘量地變小。在開始下一步之前,要簡要介紹另外一個工具。

工具2:NimbleDroid
NimbleDroid是美國哥倫比亞大學的博士創業團隊研發出來的分析Android app效能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析可以分析出APK安裝包中大檔案排行榜,各種知名SDK的大小以及佔程式碼整體的比例,各種型別檔案的大小以及佔排行,各種知名SDK的方法數以及佔所有dex中方法數的比例。這個外國網站的工具目前比Android Studio自帶的功能更加全面和強大。這裡僅作簡要介紹,讀者若有興趣可自行去研究。

有針對性地對各部分做縮減工作

一、減classes.dex檔案——壓縮程式碼

程式碼壓縮通過 ProGuard 提供,ProGuard 會檢測和移除封裝應用中未使用的類、欄位、方法和屬性,包括自帶程式碼庫中的未使用項(這使其成為以變通方式解決 64k 引用限制的有用工具)。ProGuard 還可優化位元組碼,移除未使用的程式碼指令,以及用短名稱混淆其餘的類、欄位和方法。混淆過的程式碼可令您的 APK 難以被逆向工程。
要啟用通過 ProGuard 實現的程式碼壓縮,要在對應moudle(一般情況下是主moudle,build.gradle檔案第一行為apply plugin: ‘com.android.application’)的build.gradle 檔案相應的構建型別中新增 minifyEnabled true
注意,ProGuard會拖慢構建速度,因此應該儘可能避免在除錯版本構建中使用它。另:Android Studio 會在使用 Instant Run 時停用 ProGuard。

如下為構建release版本的build.gradle的示例片段:

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

其中的proguardFiles 屬性:用於定義 ProGuard 規則

getDefaultProguardFile(‘proguard-android.txt’) 為獲取預設的 ProGuard規則檔案,它位於Android SDK tools/proguard/ 資料夾。(提示:要想做進一步的程式碼壓縮,可嘗試使用位於同一位置的 proguard-android-optimize.txt 檔案。它包括相同的 ProGuard 規則,除此之外還包括其他在位元組碼一級(方法內和方法間)執行分析的優化,以進一步減小 APK 大小和幫助提高其執行速度。)

proguard-rules.pro 檔案用於新增自定義的 ProGuard 規則。預設情況下,該檔案位於moudle根目錄(build.gradle 檔案旁)

每次構建時 ProGuard 都會輸出下列檔案,這些檔案儲存在 /build/outputs/mapping/release/

  • dump.txt
    說明 APK 中所有類檔案的內部結構。

  • mapping.txt
    列出了原始的類,方法和欄位名與混淆後代碼間的對映。(這個檔案很重要,當你從release版本中收到一個bug報告時,可以用它來翻譯被混淆的程式碼)

  • seeds.txt
    列出未進行混淆的類和成員

  • usage.txt
    列出從 APK 移除的程式碼

  • resources.txt
    列出resource被保留的資源

自定義混淆規則
對於某些情況,預設 ProGuard 配置檔案 (proguard-android.txt) 足以滿足需要,ProGuard 會移除所有(並且只會移除)未使用的程式碼。不過,ProGuard 難以對許多情況進行正確分析,可能會移除應用真正需要的程式碼。舉例來說,它可能錯誤移除程式碼的情況包括:

  • 當應用引用的類只來自 AndroidManifest.xml 檔案時
  • 當應用呼叫的方法來自 Java 原生介面 (JNI) 時
  • 當應用在執行時(例如使用反射或自檢)操作程式碼時

要修正錯誤並強制 ProGuard 保留特定程式碼,可以在 ProGuard 配置檔案中新增一行 keep 程式碼。例如:
-keep public class MyClass

還可以在想保留的程式碼新增 @Keep 註解。在類上新增 @Keep 可原樣保留整個類。在方法或欄位上新增它可完整保留方法/欄位(及其名稱)以及類名稱。(請注意,只有在使用註解支援庫時,才能使用此註解。)
有關自定義proguard-rules.pro檔案的更多資訊,可以參考 ProGuar手冊.這裡問題排查列出了一些常見的問題。本文不講解這部分知識。

解碼混淆過的堆疊追蹤
本來本章主要講解壓縮程式碼的,一般都不會涉及到如何解碼混淆過的堆疊追蹤的。考慮到網上很多參考資料只是講了如何壓縮混淆程式碼卻沒有告訴我們如何去解碼混淆過的堆疊追蹤以便快速定位出現崩潰的程式碼的。所以當你發現剛好差某樣東西的時候,這恰恰就是你的機會了。
在 ProGuard 壓縮程式碼後,讀取堆疊追蹤變得困難(即使並非不可行),因為方法名稱經過了混淆處理。幸運的是,ProGuard 每次執行都會建立一個 mapping.txt 檔案,其中顯示了與混淆過的名稱對應的原始類名稱、方法名稱和欄位名稱。ProGuard 將該檔案儲存在應用的 /build/outputs/mapping/release/ 目錄中。
請注意,每次使用 ProGuard 建立釋出構建時都會覆蓋 mapping.txt 檔案,因此每次釋出新版本時都要必須小心地儲存一個副本。通過為每個釋出構建保留一個 mapping.txt 檔案副本,就可以在使用者提交的已混淆堆疊追蹤來自舊版本應用時對問題進行除錯。
如果是在Google Play 上釋出應用,可以上傳每個 APK 版本的 mapping.txt 檔案。Google Play 將根據使用者報告的問題對收到的堆疊追蹤進行去混淆處理,以便你在 Google Play Developer Console 中進行檢查。
如果要自行將混淆過的堆疊追蹤轉換成可讀的堆疊追蹤,請使用 retrace 指令碼(在 Windows 上為 retrace.bat;在 Mac/Linux 上為 retrace.sh)。它位於 /tools/proguard/ 目錄中。該指令碼利用 mapping.txt 檔案和您的堆疊追蹤生成新的可讀堆疊追蹤。使用 retrace 工具的語法如下:
retrace.bat|retrace.sh [-verbose] mapping.txt []
例如:
retrace.bat -verbose mapping.txt obfuscated_trace.txt
如果不指定堆疊追蹤檔案,retrace 工具會從標準輸入讀取
其實,最簡單的方法還是自己先檢視混淆後的堆疊追蹤檔案,根據自己的經驗找出最後導致崩潰的追蹤資訊,然後拿這個追蹤資訊去mapping.txt檔案中直接按查詢鍵查詢。例如,假如我最後出錯的程式碼定位的資訊為com.go.cj.b.c(b.java 1234),那我們可以拿com.go.cj.b去mapping.txt檔案中直接按查詢鍵查詢。

對於壓縮程式碼,以上是工具能為我們做的,但是我們在平時的開發中養成一些習慣,也會有利於我們的程式碼縮減,一些良好的習慣建議如下:

  • 移除廢棄功能的程式碼
    不要只是註釋,擔心可能以後會用到。因為一般的開發都會使用版本控制工具VCS,所以這種擔心是多餘的。

  • 定期review專案程式碼
    發現有重複功能實現的程式碼或者框架要及時重構,刪除重複部分的程式碼和框架。出現重複功能程式碼的情景大多如下:已經有了的功能程式碼或框架,團隊成員不知道自己又寫了一套或者引入另外的相同功能的框架。這種情況的發生也反映出你們團隊之間的溝通存在一定的問題。有可能是個人經驗問題,也有可能是整個團隊的規範,共識問題,導致大家都不重視。

  • 謹慎引入第三方框架
    這裡僅從包體大方面來考慮,如果你僅僅是隻需要解析幾個json欄位的值,那麼就沒有必要引入json解析框架,android和java本身封裝的api就可以完全應付需求了。殺雞不用牛刀,其他方面的需求也是一樣的。

  • 選擇小而精的庫,而不是大而全的
    比如減小對 Support 相容包的依賴,Support-V4 包非常大,專案引入無疑會增大 dex 檔案的大小,Google 已經意識到這個問題,所以 Support-V7 一開始就做了拆分,並且開始對 Support-V4 做拆分,雖然目前成果還不明顯,不過還是挺值得期待的,特別是發現你少了 Support-V4 包後,可能就從 2 個 dex 變成 1 個 dex 了。
    又比如,如果只用到了谷歌統計,那麼就不要把整個google play services都整合進來,只整合需要的那部分。現在優秀的庫都是小而精的,不追求什麼大而全。

  • 業務模組採用外掛化框架,程式碼動態從雲端拉取
    外掛化,一種懶載入思想的體現,先讓使用者能夠安裝宿主包,對於一些功能模組做外掛化,在特定的時機再下載安裝。

二、減res資料夾

res資料夾裡面主要就是包括各種佈局檔案,value檔案,圖片檔案,原生檔案。這一塊的處理方案主要採用2種方式,一是壓縮資源,二是刪除未使用的資源。

1. 壓縮資源

壓縮資源又分2種思路,一是對資源進行壓縮,二是使用更小的資源來替換當前的資源。

  • 壓縮圖片
    一般而言圖片壓縮對減小Apk大小所產生的效果佔到你所有減小Apk努力的效果50%以上。下面推薦一款目前所知圖片壓縮效果最好的網站TinyPing。目前除了要求美工在提供圖片的時候就把圖片放上網站去壓縮,我們也可以使用別人利用tinypng提供的jar包做的批量處理本地圖片的tinyPIC gradle plugin。它在build 中插入一個新的tinyPicPlugin task.遍歷尋找專案res中以drawable開頭的資料夾中的圖片資源,呼叫tiny API進行壓縮工作並替換原來的檔案。它的接入方法可以參考這裡TinyPIC Gradle Plugin

  • 有損編碼格式的音訊檔案代替無損格式的音訊檔案
    從下面這篇官方文件Supported Media Formats可以看到 Android 平臺支援的音視訊格式,下面列出有損和無損常用的格式(不要認為有損編碼就是音質很差):
    無損格式:WAV,PCM,ALS,ALAC,TAK,FLAC,APE,WavPack ( WV )
    有損格式:MP3,AAC,WMA,Ogg Vorbis
    實際開發中需要使用音訊檔案儘量採用 MP3、Ogg 這種有損格式,儘量不要用 WAV、PCM 這種無損音訊。

  • 儘量只儲存一份圖片資源
    開發目錄下會有個drawable或者mipmap目錄用於適配不同dpi的螢幕,目前市面上絕大部分機型都處於xxhdpi的適配範圍,所以可以考慮只保留xxhdpi目錄下一份圖片資源,具體保留哪個目錄下的資源和保留幾份資源還得依照應用自身的實際機型分佈決定。至於為什麼可以考慮一份圖片資源,可以去了解一下以下2篇文章:
    Android drawable微技巧,你所不知道的drawable的那些細節
    那些值得你去細細研究的Drawable適配

  • 使用 Drawable XML、Color代替PNG圖片
    一些情況下,我們可以考慮使用 Drawable XML 來代替 PNG,如:漸變的背景圖,用幾行 XML 就可以描繪出來,何必使用幾十到上百K的 PNG 檔案。
    用 Color 代替 PNG,如:純色的背景。
    從效能上看,比起使用圖片資源需要先將其生成 Bitmap 再傳到底層交由 GPU 渲染,用Drawable XML和Color則更加高效,它是直接將 Shape 資訊傳到底層由 GPU 進行渲染,CPU和記憶體的佔用會更少。

  • 不需要透明度時使用JPG代替PNG
    當不需要透明度的圖片時,可以考慮用JPG代替PNG,由於JPG沒有Alpha 通道,所以檔案更小。

  • 考慮使用WEBP圖片資源格式
    WebP是谷歌研發出來的一種圖片資料格式,它是一種支援有失真壓縮和無失真壓縮的圖片檔案格式,如果應用支援到Android 4.0+,那麼我們可以使用WebP格式代替PNG,我們的資源大小能降低50%多。
    不過就目前來說,對於4.0+ 到 4.2.1 ,原生只支援完全不透明的webp圖,4.2.1+ 對於webp的是完全支援的(包含半透明的webp圖)。所以說對於4.2.2(API17)以下的版本,還是需要引入相容庫來解決。但是另外一點,引入相容庫又會導致包體變大。不過這種增量或許和把所有png圖換成webp所帶來的減量比較或許不值得一提,特別是圖片特別多的應用,這種增量幾乎可以不計。另外也要注意的是,某些國產rom會代理類Resource為自己定義的,例如小米2刷成4.xx的手機上,小米機器代理了類Resource為MIUIResource,但是這個MIUIResource未能正確識別webp資源,會導致載入資原始檔失敗而出現崩潰。所以應該考慮自己使用者的機型分佈,考慮使用WEBP圖片資源格式替換png格式。
    谷歌也在致力於推動WEBP圖片資源的使用,目前Android studio 2.3中加入了對圖片壓縮的工具,可以直接將PNG,BMP,JPG和靜態的Gif圖片檔案轉成Webp格式。以下為詳細轉換方法:
    Android studio 2.3 Webp使用
    詳細的使用方法:
    Android Webp 完全解析 快來縮小apk的大小吧

  • 考慮使用SVG格式圖片去替換一些icon
    SVG的全稱是Scalable Vector Graphics,叫可縮放向量圖形。它和點陣圖(Bitmap)相對,SVG不會像點陣圖一樣因為縮放而讓圖片質量下降。它的優點在於體積小,不用考慮螢幕適配問題。Android 5.0中引入了 VectorDrawable 來支援向量圖(SVG),同時還引入了AnimatedVectorDrawable 來支援向量圖動畫。但是5.0以前的版本還是需要引入支援庫,也一定程度上增加了包體。所以說可以考慮使用。

2. 移除無用的資源

這裡的移除無用的資源,主要是指2個方面,一是在工程裡面直接刪除沒有使用的資源,二是不打包沒有使用的資源。

2.1 在工程裡面直接刪除沒有使用的資源

這一點主要是使用lint檢查並清除冗餘資源。如果你的資源是通過資源名稱使用Resources的getIdentifier(String name, String defType, String defPackage)方法去獲取到資源的id來使用資源(這種方式是通過反射的方法根據資源名稱去獲取資源的id),而不是直接通過R檔案自動生成的id來使用資源的,lint會檢測判定你這個檔案並沒有被使用,而作為未使用的檔案列出來。這時候就不能使用一鍵刪除的功能,需要確認後自己手動刪除。例如我要使用doodle.png這個資源,一般情況下我們是通過R.drawable.doodle去使用的,如下程式碼

Resources res = context.getResources();
Drawable doodleDrawable = res.getDrawable(R.drawable.doodle);

但是,有些時候我們需要在程式碼中動態地根據資源名稱去使用資源,這時候就要用到getIdentifier()去獲取到資源的id,然後再使用這個id去獲取資源,示例程式碼如下:

Resources res = context.getResources();
int doodleDrawableId = res.getIdentifier("doodle", "drawable", context.getPackageName());
Drawable doodleDrawable = res.getDrawable(doodleDrawableId);

這種情況就不能使用一鍵刪除的功能,需要確認後自己手動刪除。

具體使用步驟
Android Studio 選中專案右鍵 => Analyze => Run Inspection by Name => 輸入 unused resuroces

2.2 不打包不需要使用的資源

這個方面也有2個可行的思想,一是利用Android Plugin開啟gradle 的Resource shrinking進行構建打包,這時候沒有被使用的資源將不會打進包裡。二是不打包未使用(不需要)的替代資源

(1) 利用Android Plugin開啟gradle 的Resource shrinking
Resource shrinking的使用
Resource shrinking 需要和Code shrinking 一起使用。在程式碼中刪除所有未使用的程式碼後,Resource shrinking才可以知道哪些資源APK程式仍然使用,你必須先刪除未使用的程式碼,Resource才會成為無用的,從而被清除掉。Code shrinking部分在上面的減classes.dex檔案——壓縮程式碼 一節中已經講過了。下面為Resource shrinking的使用具體步驟:
新增shrinkResources true屬性在你的 build.gradle檔案中,相應程式碼塊如下:

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

resource shrinker 目前還不支援移除定義在values/目錄下的資原始檔(strings,dimensions,styles,colors),因為Android Asset Packaging Tool(AAPT)不允許Gradle Plugin指定預定義的版本資源[issue 70869]

指定要忽略的資原始檔或者一定要刪除的資原始檔
如果我們希望保留或丟棄特定的資源,需要在專案中建立一個XML檔案,並使用resources標籤,並使用tools:keep屬性明確指定每個資源保留,或者使用tools:discard屬性明確指定這個資源將要被捨棄移除。兩個屬性都可以使用逗號(,)分隔符宣告資源名稱列表。也可以使用* 作為匹配符,匹配名稱。
例如,XML檔案,命名為keep.xml,這個檔案需要儲存在:res/raw/keep.xml,這樣build的時候該檔案才不會被打包到APK裡面,示例程式碼塊如下:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
           tools:discard="@drawable/home"
           tools:keep="@drawable/doodle, @layout/doodle, @drawable/doodle_*">
</resources>

啟用嚴格的檢測
resource shrinker 也通過搜尋程式碼中是否包含資源名或者具有匹配符相同的名稱來判斷是否在build的時候刪除資源。所以,通常情況下,resource shrinker可以準確地確定資源使用。但是如果你在程式碼中使用Resources.getIdentifier()顯式通過資源名稱動態獲取指定資源的Id,在預設情況下,這樣資源具有匹配名稱的格式為潛在的使用,無法去除。
例如,下面的程式碼將導致所有img_字首的資源都無法去除。

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

這是因為,Gradle 在處理該資原始檔時候的方式,遇到被判斷為潛在使用的情況下,預設的值為 safe。這時候就需要在上面的keep.xml檔案中指定 shrinkMode為strict(只會保留有明確引用的資源,以及處理被 tools:keep 和 tools:discard 標註的資源)。示例程式碼塊如下:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
          tools:shrinkMode="strict">
</resources>

但是這裡面還有另外一種特殊情況就是,當上面程式碼中的檔名name沒有顯式地出現“img_”這個字元的時候,不需要指定 shrinkMode為strict模式構建的時候Gradle也能刪除具有字首為“img_”字元名稱的資源。也就是說,當我把name寫在其他非程式碼類的檔案中,然後在程式碼中讀取的時候,這時候resource shrinker 通過搜尋程式碼是掃描不出相關的資源名稱“img_”字元來的,所以這個時候具有字首為“img_”字元名稱的資源都會被刪除了。所以要特別注意這種特殊情況。

(2) 不打包未使用(不需要)的替代資源
Gradle resource shrinker 只刪除你在程式碼中未使用資源,這意味著它不會刪除不同的裝置配置的可替代資源。如果有必要,可以使用Android Gradle plugin 的resconfigs屬性刪除替代資原始檔。
例如:我們專案中適配10種國家語言,而專案依賴了v7、v4等其他support包裡面包含20種國家語言,那麼我們可以通過resconfigs 刪除剩餘的可替代資原始檔,這對於我們APK大小可減少不少。
以下程式碼說明了如何限制語言資源:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

以上resconfigs 屬性只指定了英語和法語這2個語言的資源會被打進包裡。未指定的語言的任何資源都被刪除。當然如果不設定resconfigs屬性,預設會把所有的語言資源都會打進包裡。
同樣的思想我們可以運用在圖片資源上,但是我們不能做到幾種螢幕密度的資源打在同一個包裡面,另外的幾種不要了。我們只能做到打包一個只有1種螢幕密度圖片資源的包。就是我們下面要講的使用APK Splits構建不同替代資源的APK。

(3)使用APK Splits構建不同替代資源的多個APK
APK Splits比起使用 flavors,能讓應用程式更有效地構建一些形式的多個apk。注意:這裡的多個APK和使用flavors構建出來的多個APK是不同的。使用Splits構建出來的APK是隻含有不同的單套資源但功能用途一樣的APK,而flavors構建出來的APK是含有同樣的資源但卻是功能用途不一樣的APK。
APK Splits多APK只支援以下型別:

  • 螢幕密度

  • ABI

正是由於Splits構建出來的APK只含有單套可替換資源,所以它的適用情景就是我們要根據使用者的手機去提供基於不同螢幕解析度(xxhdpi,mhdpi等),so庫版本的單個APK,並且應用市場支援釋出這種多個APK的功能(即要求應用市場能根據使用者的手機的螢幕解析度,CPU的架構而為使用者選擇對應的版本的APK提供下載)。而目前只有GooglePlay支援這種Multiple APK Support釋出功能,所以你的應用如果不是在GooglePlay上面釋出的話,這種拆分多個APK釋出的做法,你就瞭解一下就行了。下面繼續介紹

按螢幕密度拆分,配置程式碼如下:

android {
  ...
  splits {
    density {
      enable true
      exclude "ldpi", "tvdpi", "xxxhdpi"
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }

enable: 啟用螢幕密度拆分機制
exclude: 預設情況下,不設定這個屬性所有螢幕密度都包括在內,如果設定,則顯式宣告移除一些密度。
include: 表示要包括哪些螢幕密度
reset( ): 重置螢幕密度列表為只包含一個空字串 (這能夠實現,在與include一起使用時可以表示使用哪一個螢幕密度,而不是要忽略哪一些螢幕密度)
compatibleScreens:表示相容螢幕的列表。這將會注入到manifest中匹配的 節點。這個設定是可選的。

構建完成後可以在out/apk/目錄下看到多個版本的APK。

按 ABI 拆分,配置程式碼如下:

android {
  ...
  splits {
    abi {
      enable true
      reset()
      include 'x86', 'armeabi-v7a', 'mips'
      universalApk true
    }
  }
}

enable: 啟用ABI拆分機制
exclude: 不使用這個屬性預設情況下所有ABI都包括在內,可以指明移除一些ABI。
include:指明要包含哪些ABI
reset():重置ABI列表為只包含一個空字串(這可以實現,在與include一起使用來可以表示要使用哪一個ABI,而不是要忽略哪一些ABI)
universalApk:指示是否打包一個通用版本(包含所有的ABI)。預設值為 false。

Splits只支援這2種類型的分類,更多的多版本支援的知識是下一節的內容。

(5)使用多版本的APK釋出
Multiple APK Support是一個在Google Play,可以釋出不同的應用程式,分別針對不同的裝置配置特徵。每個APK是一個完整的、獨立的應用程式版本,但他們分享在Google Play相同的應用程式清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣我們的APK 就可以是分不同版本構建需要資原始檔,從而減小APK的大小。
Multiple APK Support支援以下:

  • 支援不同OpenGL的APK
  • 支援不同的螢幕尺寸和密度的APK
  • 支援不同的裝置功能的APK
  • 支援不同的平臺版本的APK
  • 支援不同的CPU架構,每個apk(如ARM、x86,MIPS等)的APK
  • 支援不同的平臺版本的APK

三、減resources.arsc檔案

簡單介紹下resources.arsc檔案來源與作用:除了assets和res/raw資源被原裝不動地打包進APK之外,其它的資源都會被編譯或者處理。除了assets資源之外,其它的資源都會被賦予一個資源ID。打包工具負責編譯和打包資源,編譯完成之後,會生成一個resources.arsc檔案和一個R.java,前者儲存的是一個資源索引表,後者定義了各個資源ID常量,供在程式碼中索引資源。
所有的png檔案是以STORE的方式儲存到apk裡的,關於zip裡的STORE和DEFLATE,詳見:Zip (file format)
通俗的說,當檔案是STORED的方式儲存到zip,表示這個檔案並沒有經過壓縮,如果是Defl:N的方式,表示通過DEFLATED normal的方式壓縮儲存到zip。
現在業內有一個開源的外掛針對以上原理進行了一定的壓縮,就是下面要講的

微信資源壓縮外掛:AndResGuard
其原理就是:

  • (1)對資源(png, xml, jpg等)名稱混淆,資源路徑名稱混淆以及名稱長度壓縮

  • (2)將原來以STORED形式儲存到zip中的png檔案改成DEFLATED(普通壓縮儲存)方式。

四、減assets資料夾

assets目錄可以存放各種檔案,正常情況下,一般只存放以下幾種檔案:字型檔案、WEB頁面、配置檔案、圖片檔案。
上述幾種檔案除了配置檔案之外,我們都可以進行適當的壓縮處理:
字型檔案:可以使用字型資原始檔編輯神器Glyphs進行壓縮,其壓縮方式其實就是通過刪除不需要的字元從而減少APK的大小。
WEB頁面:可以考慮使用7zip壓縮工具對該檔案進行壓縮,在正式使用的時候解壓
圖片檔案:可以使用tinypng進行圖片壓縮

五、減lib資料夾

lib目錄用於存放通過C或C++編寫編譯生成的so檔案(native庫/JNI開發)。
因為目前市場上主流的架構還主要是arm架構,所以如果不是必要的話,可以考慮不支援x86和mips架構,但這並不意味著CPU是x86或mips架構的手機就不能正常安裝使用APK了,因為放在arm目錄下的so庫是可以相容到其他架構的;
另外arm架構中的eabi-v7a相比於eabi只是在圖形渲染方面有了很大的改進,所以如果so庫對圖形渲染沒有很高的要求的話,完全可以把so庫只存放在arm eabi目錄中,這樣可以大大減小APK的體積。
lib瘦身主要是減小對 CPU 架構的支援,配置起來很簡單,在 build.gradle 使用 abiFilters 配置需要用到的 CPU 架構,並將不需要相容的 so 檔案從專案中移除即可。
示例程式碼塊如下:

defaultConfig {
        ndk{
            // 設定支援的so庫
            abiFilters 'armeabi', 'x86'
        }
}

總結

以上所述的方法,並不需要全部都應用,而是應該根據實際情況來選用其中適合實際情況的最有效的方法。根據筆者使用過的實際情況來說,以下幾種方式對於減少包體是效果最明顯的且施行起來也不難的:

  • 1.對程式碼進行ProGuard混淆

  • 2.對圖片資源進行壓縮

  • 3.使用Gradle resource shrinker 在打包時刪除不需要的資源

  • 4.使用微信資源壓縮外掛對資源進行混淆

  • 5.在可行的情況下使用WEBP圖片資源替換現有的PNG圖片

歡迎大家斧正、交流、補充!

相關推薦

APK——全面方案

前陣子做了一次減包體的工作,過後覺得這項工作還是套路性挺強的,於是綜合當時的經驗以及廣泛瀏覽了網上的各種大牛小牛的部落格,實踐後特總結了以下的一些思路和做法,不求內容最豐富,只求方案更全面。 APK瘦身的目的 瘦身的目的最明顯的一個就是:提高下載轉化率。

android APK全面總結——如何從32.6M到13.6M

前言 之前我簡單介紹了關於svg圖片瘦身的問題,在公司,瘦身這個問題是我提出來的,所以這鍋我背了。公司專案是32.6M,我給自己的要求就是低於20M。上週花了一個星期瘦身,至於為什麼花了一週,主要是svg適配問題我被搞矇蔽了。然後發現還要改大量程式碼,想想也就算了,又換了另一種瘦身方法。 很多人是因

[Android 之美] 那些你不知道的APK ,讓你的APK

[Android 之美] APK 瘦身,減少APK的大小 讓我們的apk檔案儘可能更小,移除那些未使用的程式碼和資原始檔。本章節介紹瞭如何做到讓APK更小,效能更好,下載轉化率會更高,以及如何指定在構建APK過程中保留或移除專案中的程式碼和資源,在我們還

安卓APK

android post 安卓 ons blog view git 用法 strong 之前打包的時候直接就用eclipse或者android studio直接生成簽名文件,並沒有關心大小問題,近期有人問我有沒有對APK進行瘦身。對這方面內容一致沒有關註過,今天試用了

APK優化,減小apk的大小

首先通過Android Studio自帶的工具分析我們的apk 這樣我們就可以很清楚地看到我們的apk中最大一部分是誰,點選對應項就可以檢視它的具體內容,如下圖 這裡我們可以詳細的看到apk中用到的所有的相關庫,可以根據自己的實際情況進行刪減,比如:我在壓縮

apk以及啟動速度優化

一、apk瘦身 1、minifyEnabled true,可以幫助移除那些在程式中使用不到的程式碼。 如下圖: 2、shrinkResources true移除那些在程式中使用不到的資源,幫助減少APP的安裝包大小。   二、apk啟動速度優化: 在app

android apk

頭條APK瘦身之路 隨著版本迭代,功能增加安裝包體積也會慢慢增大。 今日頭條576版本APK達到了25M,通過一系列的優化,到目前的607版本為12M。本文主要是介紹頭條APK瘦身中用到的一些方法。 APK分析 既然是要優化APK的大小,那首先就得看下APK檔案的構成。 Android

Android效能優化之apk技巧

隨著專案迭代,新功能的增加。回導致apk越大。那麼在下載安裝過程中。使用者耗費的流量越多。 安裝等待的時間也會越長。這就意味著下載轉化率會越低。那麼如何apk瘦身呢? 理解APK結構 在討論怎麼減小Apk體積之前,理解一個應用的APK結構是非常有幫助的。一個ap

2M-APK實測可行又便捷的方法 (應用上架 陣前磨槍)

新專案上線,新應用原始apk大小在6.9M左右,然後公司和第三方運營公司合作,加入其提供的第三方SDK,瞬間apk體積增大3M;接著為了應用上線後的安全性,對應用進行了加固處理,套了一層殼之後,應用又增大了1.7M,最後上線之前包的體積已經超過10M,計算器這一工具類的應用包

Android 安裝包大小優化(Apk

目錄 1. 為什麼? APK越大,在下載安裝過程中,耗費的流量會越多,安裝等待的時間也會越長,安裝包的大小對下載的失敗率也有影響。而對於應用本身,就意味著下載轉化率會越低,在競品中,使用者更願意選擇功能多,體驗號,安裝包最小的應用。

APK套路-專案優化篇

1.專案結構瘦身套路 套路一:引入庫的優化 去掉無用的庫        專案中如果apk支援的最低版本是API14,而程式碼中沒有用到高於api14的api就可以考慮去掉整個android supp

Android效能調優;如何讓你的APK88%

隨著業務複雜度的逐漸增加,程式碼、資源也在不斷的增加,此時你的APP大小也在增加。從使用者層面來說,面對動輒幾十兆的APP來說在非WIFI情況下還是會猶豫要不要下載,不下載你就可能因此失去了一個使用者。從公司層面來講,流量就是錢,減少APP的大小就顯得尤為重要。從開發者層面上來講,你掌握了這個手藝也

Android APK/減小包體

隨著應用的長久迭代,各種功能模組的加入,APK包體越來越大,減小包體是必要的。 所以,從最簡單的來。 1.刪除無用資源 應用迭代就了,功能增刪,總會有無用資源殘留,所以,定期刪除無用資源是

Android APK 實踐(減小apk的大小)

因為推廣的需要,公司需要把APK的大小再“減小”一下,4M以內! 當達到4M以內之後,公司建議說,能否再壓壓?2M如何? 瘦身前 因為平時就考慮到大小的限制,所以很多工作已經做過了,如下列舉現在的狀態: 7.3M(Debug版本)和6.5M(Release版本)

Android開發中APK步驟

apk瘦身 想辦法較減少apk大小 必要性 同樣功能,apk越小越好,使用者下載動機更大 瞭解apk的組成 classes.dex:是java原始碼編譯後生成的java位元組碼檔案 resources.arsc:編譯後的佈局檔案

Android 效能優化之記憶體檢測、卡頓優化、耗電優化、APK

導語 自2008年智慧時代開始,Android作業系統一路高歌,10年智慧機發展之路,如今 Android 9.0 代號P  都發布了,Android系統性能已經非常流暢了。但是,到了各大廠商手裡,改原始碼自定系統,使得Android原生系統變得魚龍混雜。另外,到了不同層次的

Apk、混淆、加固

一、 apk的組成元素: resources.arsc:編譯後的佈局檔案 assets:目錄存放一些配置檔案 lib:下的子目錄armeabi存放一些so檔案或者jar包 META-INF目錄下存放的是簽名信息,用來保證apk包的完整性和系統的安全 res:apk

老司機談APK套路-圖片資源篇

       APK作為Android系統安裝包的檔案格式,一直備受廣大使用者的關注,APK越是臃腫肥大,在下載安裝過程中,它們耗費的流量會越多,安裝等待時間也會越長;對於產品本身,意味著下載轉化率會越低(因為競品中,使用者有更多機會選擇那個體驗最好,功能最多,

Android APK之旅

從APK的檔案結構說起 APK在安裝和更新之前都需要經過網路將其下載到手機,如果APK越大消耗的流量就會越多,特別是對於使用行動網路的使用者來講,消耗流量越多就代表需要花更多的錢去購買流量。同時一

Android XML shape 標籤使用詳解(apk,減少記憶體好幫手)

Android XML shape 標籤使用詳解   一個android開發者肯定懂得使用 xml 定義一個 Drawable,比如定義一個 rect 或者 circle 作為一個 View 的背景。但是,也肯定也有人在能使用 Drawable 的地方選擇使用一