Apk 極限壓縮(說點不一樣的)

Marno發表於2019-03-12

1.引言


Readhub+ 釋出後,後臺有人留言要原始碼的,還有人問 Apk 怎麼壓縮的。但是目前還不打算開源,所以沒有原始碼。不過倒是可以分享一下我在壓縮 Readhub+ Apk 的一點小小的心得。

關於 Apk的壓縮與優化,這是一個老生常談的話題了。大家耳熟能詳的方法就有很多,比如開啟混淆,壓縮圖片,使用 SVG,去除無用庫,使用 AndResGuard 之類的。這些網上已經有太多教程了,我就不再贅述了。今天這篇文章,是想和大家分享下不那麼耳熟能詳的思路。

2. 思路


幾年前,我也寫過一篇關於 Apk壓縮的文章,當時老闆說要推廣應用,為了方便使用者下載,叫我把 Apk 弄小點,畢竟當時流量費還是挺貴的。最開始經過上面提到的那些操作後,安裝包只減少了 1M 左右,優化效果並不明顯,因為寫程式碼的時候已經比較注意規範了,所以常規的優化操作效果有限。

後來,經過仔細分析了 Apk 的組成後,我發現有一個層級比較深的頁面用到了地圖,因為地圖會引入 so 檔案,就會導致 Apk 體積增加很多。所以我很機(ji)智(zei)的用 js 地圖代替了原生地圖,一下子 Apk 就只剩下 3M多了,這是當時那篇文章,《Android快速實現地圖功能(不僅快!而且小!)》,感興趣的可以去看下。當然,這種做法現在已經很普遍了。

3. 分析


我覺得 Apk 優化,在程式碼本身已經寫的比較規範的情況下,常規的壓縮操作帶來的效果是非常有限的。如果想要做到極限壓縮,那就一定要用一些"非常手段",這不僅要從技術層面考慮,還要結合產品自身的特點。比如產品針對的使用者群體,產品面向的市場範圍等。

拿我最近釋出的 Readhub+ App來說:

  • v1.0.0 版本,Apk 大小 1.14M
  • v1.2.0 版本,Apk 大小 1.13M
  • v1.5.0 版本,Apk 大小 861K
  • v1.8.0 版本,Apk 大小 858.25K

Apk 極限壓縮(說點不一樣的)

功能雖然在一直增加,但安裝包卻在一直在減小。而且如果要較真的話,這仍然稱不上是極限狀態,因為專案中還是用到了很多三方庫,如果把這些庫都去掉的話,可能最終只有不到 500K 的樣子。不過 800K 相較於現在動不動就好幾十兆的 App 來說,簡直已經是可以忽略的大小了,所以也就不打算繼續在這上面耗時間了。

既然提到了,就順便說一下吧,Readhub+ 的 v1.8.0 版本也釋出了,加入了很多設定功能,由於這個不是本文主要內容,就不具體說了,我把更新日誌單獨寫到了一篇文章裡,需要的朋友可以到公眾號看《 Readhub+ 更新日誌》。

4.拷貝


再說回到 Readhub+ Apk 的優化上,其實說實話,我覺得也沒什麼太有技術含量的操作,可能只不過是你壓根沒往那個方面想而已。而且這個也要結合專案自身的特點,所以這篇文章更多的是希望提供一個思路而已。

為了在 v1.2.0 的基礎上,繼續減小 Apk 的體積,我用 Android Studio 自帶的工具分析了 Apk 檔案的組成。如下圖所示,其中 support 包就佔了很大的體積,但是包中大部分的元件我都沒有用到,雖然已經開啟了混淆,但是再怎麼混淆,也只是減小了檔案的大小,並沒有完全去除檔案。

Apk 極限壓縮(說點不一樣的)

於是我就想著能不能把 support 包中無用的檔案去掉。開始想通過編譯的手段實現,在網上搜了一圈後,發現 gradle 目前好像不具備排除 jar 包中指定類的能力,而且即便有,那也是一個巨大的工程了。所以只能通過笨辦法了,拷貝 support 包中的原始碼。在之前的專案中,已經預設習慣了引入 v4 包和 v7 包,所以開始優化 Apk 的時候也壓根沒有朝這個方面想。

正如我剛開始所說,思路還是很簡單的。但比較奇怪的是,當我搜尋網上關於 Apk 優化的文章時,幾乎沒有哪篇提到這種做法的,也不知道是不是因為這種做法太 low 了,簡直讓很多人難以啟齒?還是這壓根都不能算個方法?

但儘管拷貝聽起來很“無腦”,可我覺得對於沒有這樣做過的人來說,還是很容易走一些彎路的。因為我在拷貝的過程中,就遇到了一些問題。(肯定有人心想,連程式碼拷貝都能出問題?真“辣雞”,哭笑不得.jpg)。因為 support 包中程式碼非常多,所以拷貝哪個版本,拷貝哪些檔案,怎麼拷貝都算是問題。

4.1 拷貝哪個版本

Readhub+ 拷貝的是 androidx 中的程式碼,因為相較於 27.0.0 和 28.0.0,甚至更早版本的 support 包來說,谷歌對 androidx 的包結構做了更加細化的區分,而且將很多 Material Design View 類元件都拆分到了一個單獨的 material-components 開源倉庫中,這樣拷貝起來也就更加方便了。

Apk 極限壓縮(說點不一樣的)

4.2 怎麼拷貝

在操作過程中,我發現拷程式碼其實是比較簡單的,比較麻煩的是拷貝 support 包中各種 res 資源,不過這裡有兩種做法可以減少一些工作量。第一種是通過在 gradle 設定 sourceSets 屬性,將不同包中的 res 資源區分開。第二種就是新建一個Android Module 專門用來放 support 包中的各種 res 檔案。這樣就不會和自己專案中的資源混起來了,後期管理和維護起來也比較方便,Readhub+ 就是用的這種方式。

Apk 極限壓縮(說點不一樣的)

4.3 拷貝哪些檔案

這個就有點因人而異了,應該說是因專案而已了,如果你追求極致的小,那麼儘量不要拷貝 View 類的元件了,想要什麼效果可以自己手寫一個。因為 support 包的 View 元件,很多都考慮了相容問題,所以有很多程式碼都是為了提高對低版本的相容性而寫的。這可能對於你們的專案來說是完全多餘的。

可能有些人會有疑問,這麼直接拷貝程式碼,不利於後期的升級維護啊。這個我覺得大可放心,首先 support 包的升級頻率是非常低的,加上我們拷貝的是穩定版中的程式碼,除非出現了致命性的 bug,不然就算不升級一般也不會有什麼問題。

通過一通拷貝之後,Readhub+ 中 support 程式碼佔用的體積減少了 46.7%,一下就減少了好幾百K,這對於一個本身只有 1.2M 的應用來說,已經是相當大的瘦身了,簡直就是從賈玲瘦成了林志玲!

Apk 極限壓縮(說點不一樣的)

5.還沒結束


通過拷貝 support 包的程式碼,已經讓 Apk 的體積小了不少,但還是有繼續優化的空間的。這就又要提到文章開頭說的,需要我們要結合產品自身的特點,進行一些定向的優化了。

比如產品的定位,投放的市場,針對的使用者等。如果產品只投放到國內市場,那麼我們可以在 gradle 中配置只保留 zh 這部分語言資源,這也可以減小十幾 K的大小;還有,如果我們針對的是比較年輕的使用者,那麼在適配解析度的時候,可以只考慮 xxhdpi 以上的裝置,甚至連 logo 也可以只保留一套,在 Readhub+ 中就是這樣做的。

splits {
    density {
        enable true
        exclude "mdpi", "ldpi", "hdpi", "xhdpi", "xxxhdpi"
        compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
}
複製程式碼

當然,還有一些其他方式,這個就和專案本身有很大的關聯了,所以這裡也只能提供一個思路而已。還是文章開頭那句話,如果你真的想讓你的 Apk 變得非常小,那就一定要結合專案自身的特點去分析,看哪裡還能減小體積的。

雖然後面這些操作減少的體積並不多,只有幾十 K 的樣子,但這是在Readhub+ 僅有 800 多 K 的基礎上減小了這麼多。在這個體積下,哪怕每減小 1K也都是挺不容易的了。

最後,文章看完了,可能有些人比較懵逼,我在文中反覆提到的 Readhub+ App是個什麼應用?這是我最近釋出到酷安市場的一款三方Readhub 客戶端,至於 Readhub 又是幹嘛的?簡單點說就是個高效獲取新聞的網站,感興趣的可以自己百度下。至於應用下載,可以在我公眾號對話視窗回覆:Readhub 獲取。


相關資源:

推薦閱讀:

Apk 極限壓縮(說點不一樣的)

相關文章