導語
JOOX Music是騰訊海外布局的一個音樂產品,2014年發布以來已經成為5個國家和地區排名第一的音樂App。東南亞是JOOX Music的主要發行地區,由於JOOX Music所面對的市場存在很多的低端機型,並且這些市場的網絡環境相對來說是比較差的,為了提升下載轉化率,對JOOX Music進行APK瘦身是必不可免的。

JOOX Music版本大小變化
JOOX Music(后面簡稱JOOX)現在已經在進行V3.8版本的開發了,不過,在這之前JOOX經歷了從V2.1版本的18M暴漲到V3.1版本的51M,再到V3.7版本的36M暴瘦,在這漲幅33M到跌幅15M的期間(JOOX Android端是往GP上放的,不過為了方便,這里說的Size就指APK的大小而不是Download Size),它到底經歷過了什么呢?接下來讓我們細細品味~
JOOX裁包過程經歷V3.5, V3.6, V3.7三個版本,並不是一次性‘水到渠成’的哦。本文主要介紹JOOX產品在Android端的APK大小裁剪所用到的方法,主要從理論,實踐以及可持續化三個主要的方面進行介紹。
一,理論知識
首先,先上一下理論知識,畢竟任何事情都是先有理論然后再去實踐的嘛。JOOX的減包主要通過一下幾方面進行的:
- 圖片資源
- 代碼方面(JAR包,so庫等等)
- 資源混淆
這里會根據上面三個方面進行理論介紹。不過,在這之前需要先給大家介紹一個Android Studio2.2提供的神器:Analyze APK。

如圖,我們只需要將要分析的APK拖入到Android Studio中,通過該工具我們可以清楚的觀察到我們的APK中各個部分所占的比重是多少,而且通過目錄索引我們可以快速定位出導致APK變大的是哪些文件從而加快我們的APK瘦身速度。想要了解更多可以查看如下文章:
1. 圖片資源方面
圖片資源基本上都是APP中占比重最大的一塊,而且對於圖片資源的優化對比於其他的方式來說是最簡單的,一般也是最容易看到效果的。所以,我們先從圖片資源方面入手。
對於大多數APP來說,基本上圖片資源主要都是由PNG和JPG來組成的,PNG和JPG是最常見的圖片格式了,這里我就不做過多的介紹了。一般來說,對於圖片資源減包這方面,主流的做法都是采取一下方式進行的:
- 刪除無用資源
- 壓縮PNG圖片,並將能轉成JPG的圖片進行轉換
- 壓縮JPG圖片
以上是我們最常見的做法,也是最有效的方式,不過對於JOOX來說,這里面多加了一項做法,那就是把圖片向WebP進行轉換。

盜圖一張~
WebP是Google在2010年發布的,現在WebP包含有損壓縮,無損壓縮,以及透明通道的有損壓縮,上圖是WebP和PNG的比較。根據官方給出的數據,基本上對於PNG進行無損壓縮能有效減小30%的大小,對比於JPG格式轉換成WebP基本上也是30%左右,而如果將圖片進行有損WebP化的話,基本上就只有原圖的1/3大小了,所以WebP的壓縮比例顯而易見。最重要的是,最新的數據顯示,WebP在解碼方面基本上和PNG差不多,甚至有時候比PNG快!如果想對WebP有更多的了解可以看看這篇文章:
基本上,對於Android4.0的手機就有對WebP的支持了,不過根據相關的數據顯示,對於Android 4.1才開始對WebP有了比較穩定的部分支持(即大多數只支持不含alpha通道的WebP圖片),Android 4.2才基本完全支持WebP。
所以,這里總結一下JOOX為什么要使用WebP的理由:
-
WebP圖片相對於PNG和JPG占用的空間更小
-
WebP在解碼方面基本上和PNG差不多,甚至有時候比PNG快
-
JOOX目前最低支持的Android系統為4.1,而Android 4.1對WebP有了初步的支持
2. 代碼方面
為了方便大家閱讀這一節,我下面用A,B,C英文字母標注了一下。
a) 代碼方面,基本上也是和主流的方式差不多,啟用代碼混淆,刪除無用代碼,無用的JAR包,以及無用so庫等等,可以很有效的減少APK的大小。
不過,上面說的都是比較簡單的情況,比較蛋疼的情況在於,大多數時候你會發現你在使用第三方的JAR包時,你只想用它一丟丟功能,而且這部分功能的實現代碼只有一點點,而你卻要導入整個JAR包。更蛋疼的是,你還不得不用,這時候你肯定整個人都不好了。想一想,如果你需要的功能代碼就占幾十KB,而你卻導入了個幾百KB甚至以MB為單位的JAR包,你的leader不會砍你嗎?如果不會,我只想說:這樣的leader給我來一車!

開個玩笑_
b) JOOX在開發過程中碰到上述的情況,采用的方法是將我們需要的代碼部分提取出來,並重新生成jar再導入我們的工程中,這樣雖然操作麻煩了一些,不過也能減少很大一部分的APK大小。而且,有時候我們可以通過減少第三方的SDK升級或JAR更換來減小APK大小,畢竟一般來說,SDK只會越來越大(畢竟加了新特性),如果我們能確定當前版本的JAR包或者SDK繼續使用下去沒問題的話可以選擇不用替換,這樣也可以達到抑制APK Size增長的效果。
c) 還有就是,現在項目開發都是采用主工程依賴子工程的方式進行的,而子工程中很多時候需要使用到相同的組件,這時候我們需要將這些組件封裝好並放入到通用的工程中,子工程去依賴該工程實現代碼的復用,避免子工程重復實現從而減小APK大小。
d) 當然,這里也少不了so庫的動態加載以及Android的插件化技術啦,通過這兩個方式也可以達到減包的目的。
-
關於so庫,一般都是打包在APK內部然后在運行時再加載運行的,而這里說的動態加載是指我們將APP中並非啟動必用的so庫放在服務器上,當用戶需要使用某項功能時再從網絡獲取相關的so庫並加載運行,一般我們都是使用System.loadLibrary來加載so庫的,而通過SDcard加載so庫的話需要我們將該so庫拷貝至APP內部存儲空間,再調用System.load方法進行加載即可。對於JOOX來說,其當中使用到的DTS的相關so庫如果采用此方式來加載的話可以減少6.5M的APK大小,可見此方式的優化效果有多‘流弊’。不過此方案的難點不在於上面的描述,在於如何對so庫的安全校驗,更新替換策略等等,但是這不是本文的重點。更多相關的知識可以搜索以下文章:《Android動態加載補充 加載SD卡中的SO庫》
-
而Android的插件化原理基本上就是以ClassLoader為基礎進行的啦,Android中包含DexClassLoader和PathClassLoader,而我們則是通過DexClassLoader加載外部的APK, JAR或者DEX文件來加載我們下發的插件,當然,完成了這一步還不算完,后面還需要考慮該用代理Activity還是通過Hook系統的startActivity來啟動插件的Activity。目前已經有很多的方案存在了,大家可以去學習一下,說不定哪天就用上了呢?
e) 說了這么多,再提一個基本都不陌生的神器:ProGuard。ProGuard不僅能夠幫助我們移除無用的代碼(當然,不可能很完全),而且使用簡短並且無意義的字符來重命名我們的類名,字段以及方法,從而達到對代碼進行壓縮,優化,混淆的效果,從而使我們的APK更小,而且使得APK更難以被逆向工程。
可以去官方那了解更多:
https://www.guardsquare.com/en/proguard
f) 這里總結一下如何從代碼方面減小APK大小:
-
刪除無用的代碼,無用的JAR包,無用的so庫
-
對於使用到的第三方庫,盡量做代碼提取,將JAR包中用不到的代碼去除,盡量減少SDK或者JAR的更新。
-
項目中盡量實現代碼復用,提取公共組件工程,以供其他項目使用
-
動態加載so庫以及插件化技術
-
啟用代碼混淆
3. 資源混淆
關於資源混淆,其核心就在於對APK中resources.arsc文件的修改,大家都知道,Android項目中res目錄下每個資源都會有其對應的ID,而ID在R文件下關聯着資源名稱(如R.string.xxx),通過這些ID我們可以很方便的鎖定某一項資源,而資源混淆的原理就在於修改ID對應的資源路徑進行優化處理(如將res/drawable/xxx修改成res/drawable/a,或者修改為r/d/a),通過這個方式可以大大減小resources.arsc文件的內容,從而達到減包的目的。
資源混淆方面,JOOX采用的是微信提供的Android資源混淆打包工具。工具傳送門:
關於resources.arsc的生成可以看看這篇文章:
二,減包實戰
上一節介紹了相關的理論知識,講的比較無聊枯燥,這一節盡量來點有趣的內容,幫助大家在快樂中學習。
一樣的,按照圖片壓縮-代碼優化-資源混淆方面進行實戰講解。
1. 圖片方面
現在JOOX最低支持4.1Android系統的手機,系統本身對於WebP有了初步的支持。
目前大多數Android App基本都只包含PNG和JPG的資源圖片,WebP相對來說由於手機支持的不是很完備(基本上Android4.2及以上才完全支持WebP),所以使用率相對就低一些。不過這里還是要介紹如何使用WebP以及使用它的好處。
1.1 應用圖片資源格式(PNG, JPG, WebP)
1.1.1 PNG和JPG圖片
PNG和JPG圖片是大家見得最多的圖片格式了,這里就不多介紹了。相信大家經常有這樣的感受,每當UI給出視覺切圖的時候,心中總會有種萬馬奔騰而過的趕腳----什么?我才剛減的包又被你搞大了?

就拿JOOX項目的一個需求來說,UI給的圖片統一PNG格式,整個文件夾包含15張切圖,大小2.88M...這要直接放進去別說leader要砍我了,趕緊跑路才是真的。
回到正題,相信上面說到的是開發新需求中經常碰到的事情,所以關鍵點在於我們如何縮小UI給的圖片而又不讓他們的像素眼發現。
一般我們從UI那里拿到的圖片都是PNG格式的圖片,不過我們都需要對其進行簡單的壓縮然后再放到項目當中(理由大家都知道了吧),不過你會發現對UI給的PNG圖片進行PNG壓縮好像沒有什么太大的效果...這里大多數人的做法是篩選出可以不使用alpha通道的PNG圖片轉為JPG圖片,你會發現效果大大的好,如圖:

原圖

轉JPG后
這里我只簡單的進行了JPG轉換,圖片大小裁剪效果怎么樣就不解釋了,大家可以看的出來。這是我們經常做的方式,不過你以為這樣紙就不會被leader砍了?而且還有一些特別惡心的圖片,Size又大個,又要alpha通道,壓縮一下下都能被看出來的怎么辦?我也很絕望呀。

too young too simple
1.1.2 WebP登場
前面的理論篇已經講了WebP的知識以及列出了相關的文章,不過這里我還是貼一下,方便大家“穿越”過去。想對WebP有更多的了解可以看看這篇文章:
WebP格式是Google2010年推出的一種圖片格式,支持有損和無損,可以包含alpha,也可以不要等等,不過這些都不是關鍵,關鍵是它的裁包效果呀。先上一下效果圖。同樣是拿之前的bg圖做無損壓縮:

轉換WebP后
啊啊啊啊啊,震驚啊,感動中國十大---扯遠了。比較原圖的900多K,這幾十倍的差距...而且顯示效果連UI都看不出來有什么兩樣。這里是使用轉換后的JPG圖進行轉換的,不過用原圖進行轉換也是24KB左右。
1.2 確定需要WebP化的圖片方案
前面提到了,Android4.2開始基本才完全支持WebP(4.0有些手機可能不支持,4.1可能不支持有alpha通道的WebP),而JOOX本身最低支持的系統版本為4.1,怎么辦?這里采用了折中的方式,將可以去掉alpha通道的圖片進行WebP化,其他圖片盡量都進行一次壓縮。不過如果以后能將大部分甚至是全部圖片WebP化,估計應用大小會大大減小。
說到這里,可能有人感覺很麻煩,還不如直接全部壓縮一下呢。這里就需要用數據來說說為什么要用WebP啦。
這里我分別用未處理的包,PNG壓縮后的包,WebP部分圖片的包以及WebP+PNG壓縮后的包進行了對比。先給出一些數據:
-
對res目錄下的圖片進行PNG壓縮之后文件夾整體大小小了2.2M
-
對res目錄下的圖片進行部分圖片WebP化之后小了1.8M
-
對res目錄下的圖片進行上述方法兩者結合小了4M左右。
不多說了,直接上圖:

原包VS PNG壓縮后的包

原包VS WebP化后的包

原包VS PNG壓縮+WebP化后的包
發現了嗎,我們對PNG圖片進行壓縮后,雖然res文件夾大小整體小了2.2M,但是打包出來后APK只小了350k,而對部分圖片WebP化后res文件夾小了1.8M,打出來的包小了有1.5M。而采用PNG壓縮+WebP化打出來的包Size減少量差不多就是它們兩者之和。現在,你還敢說不打算考慮用用WebP咩?
這里提一下在裁包過程碰到的神秘事件,先上圖:

一張WebP引起的神秘事件
這里截圖了RDM上dailybuild生成的包大小的記錄,最下面的紅框圈着的,是我將一張2kb的圖片轉換成WebP后(轉換后2KB以內)打出來的包先對於上一個包小了103kb...是不是覺得很奇怪?更奇怪的還在后面,后來發現這張圖片沒有用到,然后將它刪除掉,然后...對應第一個紅框,包大了81kb...原因我也不知道,不過感覺應該是APK在打包過程對於WebP壓縮的更好?
1.3 項目res資源裁剪流程
在確定需要WebP化的方案之后,我們該開始裁剪了咩?當然不是啦。

在這之前我們一定要學會使用Android Studio提供的Analyze APK哦,通過該工具快速定位項目中的大圖以及找出需要WebP化的圖片,提高裁包的速度。
現在可以開始我們的裁包啦,由於項目中圖片資源過多,我們把目標鎖定在文件大小大於30KB的圖片,對於小於30KB的則不管啦。
我們將APK拖入Android Studio中,即可看見APK各部分所占用的大小,然后通過查看res下的資源查找占用大的圖片。如下圖:

看,我們一下子就能找出大圖在哪了,然后可以把能WebP的大圖揪出來啦。不過找出的大圖在WebP化的過程需要注意一點哦,像JOOX Android端最低支持4.1的系統,就要小心轉換之后的圖片包含alpha通道,所以需要先將圖片轉JPG去掉alpha通道,然后在轉換成WebP,這些細節都要多加注意噢。
簡單的總結一下流程:
-
通過APK Analyzer找出能WebP化的大圖
-
將該圖片轉JPG,去掉alpha通道 //根據實際情況考慮哦
-
將JPG轉WebP,並壓縮其余PNG圖片
-
最總要的一點,項目中沒用到的圖片趕緊刪掉啦啦啦
是不是很簡單?o(∩_∩)o
1.4 總結
這里主要介紹了JOOX在圖片資源方面如何進行包Size裁剪,並將JOOX的這方面走的路線介紹給大家(什么?你不知道?當然是圖片WebP化啦)。通過本文可以看見,轉換成WebP之后的圖片不僅占用的空間小,最主要的是生成的APK也減小了很多。通過本次減包,在圖片方面JOOX大約減5M左右的APK大小,效果不是一般的好哦。
當然,WebP也有相關的缺陷,比如壓縮速度慢,支持得不太好等等,不過這些都不是問題噢,壓縮的話我們完全不用考慮呀,4.0及以下的可以采用相應的庫來支持呀,反正是個問題基本都是能解決的啦。

我承認我說錯了(┬_┬)
2. 代碼方面
其實在這一方面並沒有太多要說的,不過呢,為了保證文章的完整性,我還是要“扯”一會 ~ _
代碼方面的減包,基本上就是按照理論一節的方式進行的,去除無用JAR包,無用so庫算是比較簡單的事情了,麻煩的地方在於去除無用代碼和提取第三方庫使用到的代碼(當然,第三方庫里面的資源就按照圖片篇的處理啦)。

裁剪so庫,JAR包之前

裁剪so庫,JAR包之后
可以清楚的看見,通過裁剪so庫,JAR包,我們實現了5.4M的瘦身,效果很明顯啊有木有!當然,這也不是說裁就裁的,JOOX在這次裁包中進行了一些架構的調整以及大量的代碼調整才得到的效果。
代碼這里的話大家應該都有同樣的感受,就拿JOOX來說,JOOX這里合入了P2P直播的代碼,而P2P直播那邊使用的是Glide作為圖片加載框架,JOOX本身就實現了自己的圖片加載框架,項目中又存在一些第三方使用了Universal-ImageLoader框架,這就蛋疼了,本來只需要一個框架就能搞定的事情,因為合入了其他工程代碼導致了存在多個相同功能的框架代碼,導致項目不經意間就變大了好幾百KB。所以,JOOX在這方面進行了框架的統一,即統一使用Glide圖片加載框架,這樣可以減少多出的框架的代碼占用的空間。
由於JOOX目前還沒有實現動態加載so庫以及插件化的方式,所以文章就在理論里面給大家介紹了一下能夠通過這兩個方式達到的效果。當然,JOOX下一步要做的也是要朝這兩個方面靠齊啦。
3. 資源混淆
同樣的,為了方便大家,工具傳送門:
資源混淆采用的是微信提供的混淆方案,不過該方案可能和熱補丁方面有些沖突,不過沒關系,就如上面所說,問題基本都是能解決的,畢竟微信本身也有熱補丁。
其實使用該工具比較麻煩的地方在於列出我們的白名單列表(畢竟人家都給我們實現工具了),將不需要混淆的資源加入白名單,防止混淆之后應用運行異常。而如何將所有白名單找出來呢?當然是使用CTRL+SHITF+F(即全局搜索)啦,這還用我說咩?直接查找getIdentifier基本就能將需要保護的對象給找出來啦。
通過該方式JOOX基本減包了1.5M左右,可以看見,資源混淆減包的方式還是‘灰’常強大的,當然,要注意它列出的注意事項。
三,可持續化
前面說了辣么多減包的操作,這里就不說這些東西啦,就講講我們該如何進行可持續化減包吧。
從前面的減包過程可以知道,APK大小占比重最大的算是圖片了,所以這里也是主要針對圖片進行可持續化處理以達到持續減包的效果。好,現在請大家拿出自己的錢包多翻幾下,發現問題了咩?現在月底啦,我們的錢包只會越來越扁,而需求只會越來越多啊!

程序員&產品
開個小玩笑,跑題了~如上所說,需求是不會有做完的一天的,所以,UI也不會停止給出新的切圖,如果我們不對UI給的圖片進行處理的話,過不了多久就會發現,我們之前減包基本就是白干活啊。所以,我們需要對圖片進行可持續化處理,以保證我們的應用不會在短時間內快速增大。
當然,你覺得和UI說一聲以后給圖記得壓縮就好了...但是,你會發現,換了幾批UI之后,又回到了從前的樣子,又要開始新一輪的包裁剪了。而且UI還不一定記得壓縮圖片,你每次拿到切圖都要自己檢測一下,多累呀。
所以控制APK大小的任務最后還是落到我們的頭上,怎么辦捏?當然是操起鍵盤就是干呀!JOOX在這方面有兩點參考實現:
-
第一點: JOOX在gradle中加入了相應的圖片壓縮task,每當版本發布之前會執行該task,將新加入的圖片進行一次無損壓縮(為什么要無損壓縮?因為有損壓縮可能會導致圖片出問題,壓縮完之后需要肉眼查看,比較麻煩,而且前面也提到了,對於PNG進行壓縮之后對於APK的裁剪效果不是很明顯),當然,能夠轉換成WebP的圖片需要開發人員進行自己轉換,因為Android Studio 2.3提供了WebP轉換的工具,所以很方便。
-
第二點: JOOX在RDM上集成了大文件監控報警機制,如果在提交SVN之后編譯出來的包大小相對於上一個包的大小超出了相應的閾值,將會進行報警以及生成相應的BUG單給對應的開發人員進行提醒,開發人員在收到BUG單之后進行相應的處理。這樣可以防止可以避免使用的大文件積累到后面而使得我們不得不再次大動干戈的進行包Size裁剪。

四,展望未來
接下來,JOOX要走的方向即文章提到的,so庫動態加載和插件化技術,通過這兩個方案預估能夠再為JOOX帶來幾M左右的包大小裁剪,實現真正的完美瘦身。當然,瘦身雖好,但是也是要付出相應的代價的呀,不是說瘦就能瘦的。通過動態加載so庫進行減包的話需要考慮業務划分呀,可能會影響用戶體驗等等,缺點也是相應的存在的,所以說,凡事都是需要做兩手考慮,世上可沒有后悔葯吃喲~
更多精彩內容歡迎關注騰訊 Bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合並功能幫助開發同學把每天上報的數千條 Crash 根據根因合並分類,每日日報會列出影響用戶數最多的崩潰,精准定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發布后快速的了解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!
