Android APP終極瘦身指南

APK瘦身實踐

http://www.jayfeng.com/2015/12/29/APK%E7%98%A6%E8%BA%AB%E5%AE%9E%E8%B7%B5/

APP終極瘦身指南

http://www.jayfeng.com/2016/03/01/Android-APP%E7%BB%88%E6%9E%81%E7%98%A6%E8%BA%AB%E6%8C%87%E5%8D%97/

                                                  Android APK瘦身實踐


因爲推廣的需要,公司需要把APK的大小再“減小”一下,4M以內!
當達到4M以內之後,公司建議說,能否再壓壓?2M如何?

瘦身前

因爲平時就考慮到大小的限制,所以很多工作已經做過了,如下列舉現在的狀態:

  1. 7.3M(Debug版本)和6.5M(Release版本)
  2. 開啓minifyEnabled
  3. 開啓shrinkResources
  4. 已經去除不相關的大型庫
  5. 圖片和代碼已經經歷過粗略的一輪清理

開始魔鬼瘦身

1. tinypng有損壓縮

android打包本身會對png進行無損壓縮,不信大家可以看看apk中的圖片的大小實際上比你代碼工程裏的圖片要小(針對沒進行過無損壓縮的那些png圖)。
所以,純粹的進行無損壓縮並不會對apk的減小有任何效果,這是我特別想在這裏強調的一個經驗。
現在大家主流的比較喜歡用的tinypng其實是有損壓縮:

https://tinypng.com/
[原文] TinyPNG uses smart lossy compression techniques to reduce the file size of your PNG files…
[翻譯] TinyPNG使用智能有損壓縮技術,來減少PNG文件的大小…

通過tinypng確實能在儘量少的損失下再減小apk,如果圖片資源多或者大的話,效果還是很明顯的。
具體減少多少,因爲這個處理過程我們是間隔做的,無法準確給出結果,就按200k~500k算吧。

2. png換成jpg

經驗發現,一些背景,啓動頁,宣傳頁的PNG圖片比較大,這些圖片圖形比較複雜,如果轉用有損JPG可能只有不到一半(當然是有損,不過通過設置壓縮參數可以這種損失比較小到忽略)。
因爲都是大圖,所以這種方式能有效減小apk的大小。
這種情況下的apk的減小是不可估量的。

3. jpg換成webp

如果png大圖轉成jpg還是很大,或者想壓的更小,而儘量不降低畫質,那麼可以考慮一下webp。

android 4.0+才原生支持webp, 但是我們的app是兼容2.3+,所以4.0以下的設備將無法看到圖片。

  1. 考慮到我們4.0以下的所有設備比例之和大約在0.44%,非常少
  2. 4.0以下的設備不會崩潰

我們選擇不對4.0以下做webp兼容處理,不顯示就不顯示。否則,要引入webp相關so文件增大apk大小。

通過把下面四張大圖換成webp,webp的quality參數按50配置(據說官方評測75是最佳值),清晰度勉強可以接受,這個值大傢俱體按產品要求來定。
jpg大圖轉webp
其中安裝jpg轉webp工具:

1
brew install webp

轉換命令如下

1
2
3
cwebp -q <quality> input.jpg -o output.webp
// Example:
cwebp -q 50 a.jpg -o a.webp

更多下載:https://developers.google.com/speed/webp/docs/precompiled

最終,apk減小了188k。

補充:
在4.0 ~ 4.2.1的設備上無法顯示帶有透明度的webp,比如,把png轉成webp則無法顯示,但是如果把png先轉成jpg再轉成webp則能正常顯示了,但會丟失透明度。參考鏈接:http://developer.android.com/guide/appendix/media-formats.html

4. 大圖縮小

如果經過上面的步驟,依然存在大圖的話,說明確實圖有點大了,可能真的有點大了!
所以,要考慮的問題是,是否有必要保證如此的大小?能否縮小?
如果這方面能減小的話,apk瘦身的效果必然又會上一個檔次。
這種情況下的apk的減小是不可估量的。

5. 覆蓋aar裏的一些默認的大圖

一些aar庫裏面包含根本就沒有用的圖。最典型的是support-v4兼容庫中包含一些“可能”用到的圖片,實際上在你的app中不會用到。
support庫資源替換
我沒有把所有圖都替換掉,只是把幾張大一點點的圖(選中的那些圖)用1x1的圖片替換,如果9patch圖的話,要做成3x3的9patch圖替換。
support庫可能還算好的,就怕有些庫引用了一些大圖而不自知,可以在/build/intermediates/exploded-aar/下的各個aar庫的res目錄查找檢驗。
apk減小了18k。

6. 刪除armable-v7包的so

感謝@楊輝__ ,@kymjs張濤的提醒,armable-v7和armable文件夾可以只保留armable。
當然,armable-v7a的庫會對圖形渲染方面有很大的改進,因爲我們主要是一些業務上動態庫,所以刪掉無大礙。
armable-v7a的so文件列表
apk減小了191k。

7. 微信資源壓縮打包

這個方案網上一直在說,之前一直沒有需求或者動力實踐,在這裏感謝一下@裸奔的凱子哥的推薦和交流,他那邊的apk可以壓小1M,效果還是比較驚人的。
這個步驟我是在後面很多步壓縮之後測試的,每個階段的壓縮結果都會有些許出入,所以數據僅供參考。
微信壓縮方案結果比較
通過正常壓縮,apk包減小了464k。
如果開啓7zip,apk包減小了594k。
apk減小了594k。

PS: 關於這個壓縮,我集成到了gradle腳本中了,新建了一個Task,大概代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
task compressReleaseApp {
    // 在現有release的版本上生成到compressed目錄下
    def appid = "appid"
    def channel = "abcdefghijkl"
    def guardJarFile = file('../AndResGuard/andresguard-1.1.jar')
    def guardConfigFile = file('../AndResGuard/config.xml')
    def originApkFile = file("../app.${appid}/build/outputs/apk/release/${appid}-release-${rootProject.ext.versionName}-${rootProject.ext.versionCode}-${channel}.apk")
    def outputDir = file("../app.${appid}/build/outputs/apk/compressed/")
    def keystoreFile = file(RELEASE_STORE_FILE)
    // 開始執行壓縮命令
    def proc = "java -jar ${guardJarFile} ${originApkFile} -config ${guardConfigFile} -out ${outputDir} -signature ${keystoreFile} ${RELEASE_STORE_PASSWORD} ${RELEASE_KEY_PASSWORD} ${RELEASE_KEY_ALIAS}".execute();
    proc.waitFor();
    println "return code: ${ proc.exitValue()}" + ", stderr: ${proc.err.text}" + " stdout: ${proc.in.text}"
}

config開啓了7zip, 部分配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<resproguard>
    <!--defaut property to set  -->
    <issue id="property" >
        <seventzip value= "true" />
        <!--  ...  -->
    </issue>

    <issue id="whitelist" isactive="true">
        <path value ="com.xxx.yyy.R.drawable.emoji_*" />
        <path value ="com.xxx.yyy.... />
    </issue>

    <issue id ="compress" isactive="true">
        <!--  ...  -->
    </issue>
</resproguard>

詳情參考:Android資源混淆工具使用說明
原理介紹:安裝包立減1M–微信Android資源混淆打包工具

8. proguard深度混淆代碼

之前爲了簡單起見,很多包都直接忽略了,現在啓動嚴格模式,把能混淆的都混淆了:
release版本混淆效果
採用微信壓縮方案最終效果比較:
最終壓縮release版本混淆效果
apk減小了215k。

PS:混淆後,一定要經過嚴格測試,有時候甚至很難發現錯誤,比如我開啓嚴格混淆,用了一段時間之後慢慢發現了兩個bug,排除了兩個包程序才正常。

9. 深度清理代碼和資源

有意思的是,無論何時何地去清理代碼和資源,總能有新的發現:

  • 新發現或者新引入的無用圖片
  • 這幾張圖怎麼一樣
  • 這個類好像沒有用
  • 沒用的類相關的圖片也沒用
  • 有些圖片可以用着色方案替換
  • 有些圖片可以用shape來代替
  • hdpi裏的ic_luancher.png好像也可以刪掉

apk減小了66k。

10. proguard去符號表

之前爲了保留調試信息,我們是在Proguard保留了符號表的:

1
-keepattributes SourceFile,LineNumberTable

官方渠道我覺得還是儘量保留這個,現在針對推廣渠道,只能採用特殊手段,註釋這一行。
apk減小了230k。
ps:以後友盟上看推廣渠道的bug要辛苦一點,手動上傳mapping.txt了。

11. provided關鍵字

可以對僅在運行時需要的庫設置provided關鍵字,實際並不被打包:

1
provided 'com.android.support:support-annotations:22.0.0'

我沒有發現這樣的場景,如果說有的話,就是support-annotations,但是經過後來的測試驗證,support-annotations本來就會在release版本中被minifyEnabled掉,所以對support-annotations設置provided是沒有意義的。
如果有實際場景,歡迎留言說明,不甚感激。
apk沒有減小。

12. 表情包在線化

雖然應用的表情不多,只有50來個,但是如果能把這部分表情放到網上,不僅能有效減小apk大小,還可以方便後期擴展支持:
表情列表
打包成emoji_v1.zip, 大小是202k。
現在把emoji_v1.zip放到網上,按需下載後使用,最終對比結果如下:
表情包在線化成效
apk減小了193k。

13. 全版本兼容的着色方案

考慮着色方案主要目的是更方便支持多主題,減輕UI工作量,減少工程裏一大堆selector文件等,然後纔是,順便的減小一下apk大小。
通過着色方案,我們去除了10多張純色的按下狀態圖片和對應的xml等等。
apk減小了15k。

PS: 具體實現可以參考 http://www.race604.com/tint-drawable/ ,而我也把它集成到了我的LessCode庫中了:DrawableLess.java

14. 去除重複庫

發現兩個地方:

  • 現在發現七牛的SDK引用了android-async-http-1.4.6.jar,雖然不大,只有95.4k,但是感覺完全可以寫一個輕量級的jar,控制在10~20k就足夠了,具體可以在現有的網絡庫上實現。
  • 自己工程使用的是UIL,但是引入的第三方庫引用了picasso,兩個重複的圖片下載庫也是完全沒用必要的。

現在還沒有處理這塊,新任務介入,延期優化,敬請期待。

15. 去除無用庫

這是一個很基本的點,但是確很容易被人忽視,當你仔細回顧的時候,有一些雞肋的功能或者庫,是幾無用處的。不如干脆去掉。
比如,在很早的時候,我就把我們app裏的sharesdk刪除了,因爲對於我們的產品定位和推廣來看,這毫無意義。
這種情況下的apk的減小是不可估量的。

16. 去除百度統計

這個視具體情況決定。
因爲我們的APP裏面包含友盟和百度兩套統計系統,早期老闆要求,事實上後面已經很少看這方面的數據,百度統計的數據幾乎沒用人去看,可以暫時先去除。
原本的百度統計的jar有130多k,去除之後的apk的減小會遠遠沒有這麼多。
apk減小了20k。

17. 使用更小的庫

使用更小的庫不應該成爲你選擇方案的決定性因素,但是可以作爲參考因素(freso確實太大了,這個大小也可以成爲決定性因素)。
圖片下載,網絡請求,json解析等等的庫和它的競品都有多大,你心裏有數嗎?
以工具庫爲例,網上有很多工具庫,但是往往它們的大小很難控制。

  • xutils-3.2.6.aar – 843.8k
  • lite-common-1.1.3.jar – 148.1k
  • lesscode-core-0.8.2.aar – 64k

上面最後一個庫LessCode是我自己收集的工具類集合,非常小:LessCode,混淆後只有不到50k大小。
不僅提高了開發效率,減少了冗餘代碼,而且能避免引用一些其他大型的庫,有效避免包的增大。
比如,我們碰到過這樣的一個bug,快速點擊按鈕多次觸發跳轉,現在RxJava結合RxBind有這樣的一個場景解決方案,如果引入這些庫的話必然會增大apk大小,實際上就幾行代碼,我把這樣的解決方案集成到了LessCode,下次別的項目碰到這樣的問題不用再猶豫是否要引入一個這麼大的庫了。
這些小的工具庫,建議根據自己的經驗人手總結一個,不求全,但求精!
這種情況下的apk的減小是不可估量的。

18. 插件化

尷尬的是,我們所呈現的功能大部分都是重要的不可分割的功能,很難從業務上分離出來。
今年預計要實踐一個輕量級的插件化方案,用別人的也好,自己寫也好,希望能解決或者優化一些安裝包加載多模塊,或者主題切換,或者熱修復的問題。
這裏作爲候選方案備用。
這種情況下的apk的減小是不可估量的。

19. 功能業務取捨

一開始考慮瘦身,領導是允許適當的砍掉一些功能,因爲4M的目標我們已經實現了,所以現在還沒有到砍功能的地步。
這裏作爲候選方案備用。
這種情況下的apk的減小是不可估量的。

補充

文章發出後,收到了一些朋友的建議,補充幾點。

1. 去除無用的語言資源

感謝@牧志軒的建議,通過配置resConfigs可以選擇只打包哪幾種語言,進而去掉各種aar包中全世界的語言,尤其是support包中的。
沒選擇語言的語言包
選擇保留什麼語言要根據產品的用戶和市場來定,如果只選擇默認英語和中文語言,配置如下

1
2
3
4
5
android {
    defaultConfig {
        resConfigs "zh"
    }
}

看看效果:
只選擇默認英文和中文的語言包效果
如果不採用微信壓縮方案結果對比,apk減小了197k。
如果採用微信壓縮(開啓7zip)對比結果,apk只減小了16k,因爲微信對resources.arsc進行了強力壓縮,厲害!
apk減小了16k。

2. 刪除x86包的so

再次感謝@楊輝__的建議,x86的包刪除了之後,測試反應好像有些機器容易崩潰,未能經過嚴格測試,所以主版本又復原了,只在個別渠道執行這條措施。
一般情況下不會有問題,測試了一下效果,apk減小了78k。
這裏作爲候選方案備用。

3. 其他人的經驗分享(持續更新)

小結

最終,我們成功的把apk壓到了2.9M,如果把上面遺漏的步驟繼續再做,應該還能再減小一點。
客戶反應壓的好小,領導簡直不敢相信~
瘦身不難,難的是魔鬼瘦身!




                                               Android APP終極瘦身指南                                       


前言

之前寫了一篇《APK瘦身實踐》側重於實踐和效果對比,後來受徐川兄點撥,建議改寫成一篇更全面的瘦身終極殺招大全,深以爲然,思考良久,新開一篇。

指南條例

第1條:使用一套資源

這是最基本的一條規則,但非常重要。
對於絕大對數APP來說,只需要取一套設計圖就足夠了。鑑於現在分辨率的趨勢,建議取720p的資源,放到xhdpi目錄。
相對於多套資源,只使用720P的一套資源,在視覺上差別不大,很多大公司的產品也是如此,但卻能顯著的減少資源佔用大小,順便也能減輕設計師的出圖工作量了。
注意,這裏不是說把不是xhdpi的目錄都刪除,而是強調保留一套設計資源就夠了。

第2條:開啓minifyEnabled混淆代碼

在gradle使用minifyEnabled進行Proguard混淆的配置,可大大減小APP大小:

1
2
3
4
5
6
7
android {
    buildTypes {
        release {
            minifyEnabled true
        }
    }
}

在proguard中,是否保留符號表對APP的大小是有顯著的影響的,可酌情不保留,但是建議儘量保留用於調試。
詳細proguard的相關的配置和原理可自行查閱。

第3條:開啓shrinkResources去除無用資源

在gradle使用shrinkResources去除無用資源,效果非常好。

1
2
3
4
5
6
7
android {
    buildTypes {
        release {
            shrinkResources true
        }
    }
}

第4條:刪除無用的語言資源

大部分應用其實並不需要支持幾十種語言的國際化支持。還好強大的gradle支持語言的配置,比如國內應用只支持中文:

1
2
3
4
5
android {
    defaultConfig {
        resConfigs "zh"
    }
}

第5條:使用tinypng有損壓縮

android打包本身會對png進行無損壓縮,所以使用像tinypng這樣的有損壓縮是有必要的。
重點是Tinypng使用智能有損壓縮技術,以儘量少的失真換來圖片大小的銳減,效果非常好,強烈推薦。
Tinypng的官方網站:http://tinypng.com/

第6條:使用jpg格式

如果對於非透明的大圖,jpg將會比png的大小有顯著的優勢,雖然不是絕對的,但是通常會減小到一半都不止。
在啓動頁,活動頁等之類的大圖展示區採用jpg將是非常明智的選擇。

第7條:使用webp格式

webp支持透明度,壓縮比比jpg更高但顯示效果卻不輸於jpg,官方評測quality參數等於75均衡最佳。
相對於jpg、png,webp作爲一種新的圖片格式,限於android的支持情況暫時還沒用在手機端廣泛應用起來。從Android 4.0+開始原生支持,但是不支持包含透明度,直到Android 4.2.1+才支持顯示含透明度的webp,使用的時候要特別注意。
官方介紹:https://developers.google.com/speed/webp/docs/precompiled

第8條:縮小大圖

如果經過上述步驟之後,你的工程裏面還有一些大圖,考慮是否有必要維持這樣的大尺寸,是否能適當的縮小。
事實上,由於設計師出圖的原因,我們拿到的很多圖片完全可以適當的縮小而對視覺影響是極小的。

第9條:覆蓋第三庫裏的大圖

有些第三庫裏引用了一些大圖但是實際上並不會被我們用到,就可以考慮用1x1的透明圖片覆蓋。
你可能會有點不舒服,因爲你的drawable下竟然包含了一些莫名其妙的名稱的1x1圖片…

第10條:刪除armable-v7包下的so

基本上armable的so也是兼容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進,如果沒有這方面的要求,可以精簡。
這裏不排除有極少數設備會Crash,可能和不同的so有一定的關係,請大家務必測試周全後再發布。

第11條:刪除x86包下的so

與第十條不同的是,x86包下的so在x86型號的手機是需要的,如果產品沒用這方面的要求也可以精簡。
建議實際工作的配置是隻保留armable、armable-x86下的so文件,算是一個折中的方案。

第12條:使用微信資源壓縮打包工具

微信資源壓縮打包工具通過短資源名稱,採用7zip對APP進行極致壓縮實現減小APP的目標,效果非常的好,強烈推薦。
詳情參考:Android資源混淆工具使用說明
原理介紹:安裝包立減1M–微信Android資源混淆打包工具
建議開啓7zip,注意白名單的配置,否則會導致有些資源找不到,粗略配置如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<resproguard>
    <!--defaut property to set  -->
    <issue id="property" >
        <seventzip value= "true" />
        <!--  ...  -->
    </issue>

    <issue id="whitelist" isactive="true">
        <path value ="com.xxx.yyy.R.drawable.emoji_*" />
        <path value ="com.xxx.yyy.... />
    </issue>

    <issue id ="compress" isactive="true">
        <!--  ...  -->
    </issue>
</resproguard>

第13條:使用provided編譯

對於一些庫是按照需要動態的加載,可能在某些版本並不需要,但是代碼又不方便去除否則會編譯不過。
使用provided可以保證代碼編譯通過,但是實際打包中並不引用此第三方庫,實現了控制APP大小的目標。
但是也同時就需要開發者自己判斷不引用這個第三方庫時就不要執行到相關的代碼,避免APP崩潰。

第14條:使用shape背景

特別是在扁平化盛行的當下,很多純色的漸變的圓角的圖片都可以用shape實現,代碼靈活可控,省去了大量的背景圖片。

第15條:使用着色方案

相信你的工程裏也有很多selector文件,也有很多相似的圖片只是顏色不同,通過着色方案我們能大大減輕這樣的工作量,減少這樣的文件。
藉助於android support庫可實現一個全版本兼容的着色方案,參考代碼:DrawableLess.java

第16條:在線化素材庫

如果你的APP支持素材庫(比如聊天表情庫)的話,考慮在線加載模式,因爲往往素材庫都有不小的體積。
這一步需要開發者實現在線加載,一方面增加代碼的複雜度,一方面提高了APP的流量消耗,建議酌情選擇。

第17條:避免重複庫

避免重複庫看上去是理所當然的,但是祕密總是藏的很深,一定要當心你引用的第三方庫又引用了哪個第三方庫,這就很容易出現功能重複的庫了,比如使用了兩個圖片加載庫:Glide和Picasso。
通過查看exploded-aar目錄和External Libraries或者反編譯生成的APK,儘量避免重複庫的大小,減小APP大小。

第18條:使用更小的庫

同樣功能的庫在大小上是不同的,甚至會懸殊很大。
如果並無對某個庫特別需求而又對APP大小有嚴格要求的話,比較這些相同功能第三方庫的大小,選擇更小的庫會減小APP大小。

第19條:支持插件化

過去的一年,插件化技術雨後春筍一樣的都冒了出來,這些技術支持動態的加載代碼和動態的加載資源,把APP的一部分分離出來了,對於業務龐大的項目來說非常有用,極大的分解了APP大小。
因爲插件化技術需要一定的技術保障和服務端系統支持,有一定的風險,如無必要(比如一些小型項目,也沒什麼擴展業務)就不需要了,建議酌情選擇。

第20條:精簡功能業務

這條完全取決於業務需求。
從統計數據分析砍掉一些沒用的功能是完全有可能的,甚至乾脆去掉一些花哨的功能出個輕聊版、極速版也不是不可以的。

第21條:重複執行第1到20條

多次執行上述步驟,你總能發現一些蛛絲馬跡,這是一個好消息,不是嗎?

在線評估

針對很多朋友的反饋,有必要對條例的適用範圍、易用性和風險指數做個粗略的評估,彙總如下,方便大家執行。

指南條例 適用範圍 易用性 風險指數 備註
使用一套資源 非極高UI要求的APP  
開啓minifyEnabled 全部  
開啓shrinkResources 全部  
刪除無用的語言資源 非全球國際化應用  
使用tinypng有損壓縮 非極高UI要求的APP  
使用jpg格式 僅限非透明大圖  
使用webp格式 僅限4.0+,4.2+設備  
縮小大圖 限允許縮小的大圖  
覆蓋第三庫裏的無用大圖 全部  
刪除armable-v7包下的so 限允許對極少數設備不兼容  
刪除x86包下的so 限允許對x86設備不兼容  
使用微信資源壓縮打包工具 全部 切記要配置白名單
使使用provided編譯 全部 容錯處理
使用shape背景 全部  
使用着色方案 全部  
表情在線化 限含表情包的APP  
避免重複庫 全部  
使用更小的庫 全部  
支持插件化 限擴展性要求高的APP  
精簡功能業務 限允許精簡的APP

小結

相信經過上述步驟,一定可以把你的Android APP極大的瘦身下去。
考慮到一定的風險性,建議挑選適合自己的方法就行;同時,我也會跟蹤最新的瘦身技巧,及時補充更新。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章