轉載請註明出處: http://www.cnblogs.com/soaringEveryday/p/5254520.htmlhtml
隨着Android移動開發的需求愈來愈複雜,咱們不可避免的遇到發佈出去的apk體積愈來愈大的問題,目前超過10MB、20MB的apk已是很常見的事情了,可是依然可以看到一些apk的體積控制的很小。apk體積增大源於:android
本文將從我本身的經歷項目中探討如何有效減少apk的體積。減少Apk體積是一件頗有用處的android優化手段,下降了用戶須要下載的比特數,同時也下降了分發安裝時失敗的機率。git
將apk解壓後發現,體積佔大頭的分辨是lib文件夾、res文件夾和dex文件。因此咱們的下降apk體積的策略也應當從如何縮減so文件、資源圖片、控制代碼質量上來入手。github
使用Progruard安全
Proguard是Android很早就使用的代碼混淆工具,除了用於混淆代碼提升安全性之外,他在代碼編譯的時候也會經過遍歷代碼的方式來發現沒有被調用的代碼,從而將其在打包成apk時剔除,最終必定程度上下降了apk的大小。架構
可是Proguard使用時候是要注意的,由於代碼中利用反射機制的地方會被Proguard工具破壞,因此要慎重的編寫混淆例外文件,同時對於混淆後打包出來的apk要從新充分迴歸測試下。框架
使用Android Lint工具
Proguard提供了代碼的縮減方式,而Lint對於res下面的資源進行了充分的優化,他會提供一份報告給你,從而通知你哪些資源沒有被用到,顯然剔除這些資源是能夠減小apk體積的。這些資源包括res文件夾下全部的內容,好比圖片、字串、尺寸等等。如今Android Lint已經集成到了Android Studio中,用法很簡單。測試
進入Android Studio的菜單中選擇Analyze->Inspecting Code便可gradle
分析完畢後在Inspection選項卡中會有一份詳細的報告,找到Android Lint項目
拉到下面Unused resource這一欄打開,便是未被使用的資源列表,用戶能夠參照來手動刪除資源
清理Assert文件夾
Assert文件夾常常會放置一些不被編譯的資源,時間久了,裏面可能一些文件或者資源已經不用了,然而這個文件夾也是會被打包到apk裏面的。因此按期清理這個裏面的內容也是減少apk體積的重要一步。
用代碼代替圖片
開發的時候有些地方能用代碼作出來的就儘可能不用圖片來渲染,這樣子能夠減小圖片資源的數量從而減小體積,這裏舉幾個例子。
用shape代替背景圖
不少背景圖好比按鈕的背景、純色背景都是能夠用shape來製做的,這樣子僅用xml代碼就代替了png資源。好比這麼要給按鈕背景圖(純色背景、帶邊框、圓角)能夠用shape而不是Png圖片來製做
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> <stroke android:width="0.5dp" android:color="@color/white"/> <gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="0.0" /> <corners android:topLeftRadius="4dp" android:topRightRadius="0dp" android:bottomLeftRadius="4dp" android:bottomRightRadius="0dp" /> </shape> </item> </selector>
用RotateDrawable代替僅僅是方向不一樣的「內容相同」的圖片
這裏兩個圖片是兩個按鈕箭頭,可是僅僅方向不一樣而已,其實能夠只用其中一個圖片便可,而另外一個用RotateDrawable來讓其「調轉」180度
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_arrow_left" android:fromDegrees="180" android:pivotX="50%" android:pivotY="50%" android:toDegrees="180" />
用layer-list來製做多層圖片從而達到複用
有些需求中須要一種圖片,可是明顯這個圖片是其餘幾個圖片簡單疊加而已,那麼可使用layer-list來達到目的
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <!-- 最底層的圖片,以x,y軸座標爲中心進行旋轉--> <rotate android:pivotX="0" android:pivotY="0" android:fromDegrees="-10" android:toDegrees="-10"> <bitmap android:src="@drawable/chatting_bg_default_thumb"/> </rotate> </item> <!-- 第二層的圖片,以x,y軸座標爲中心進行旋轉--> <item> <rotate android:pivotX="0" android:pivotY="0" android:fromDegrees="15" android:toDegrees="15"> <bitmap android:src="@drawable/chatting_bg_purecolor_thumb"/> </rotate> </item> <!-- 最上層的圖片,以x,y軸座標爲中心進行旋轉--> <item> <rotate android:pivotX="0" android:pivotY="0" android:fromDegrees="35" android:toDegrees="55"> <bitmap android:src="@drawable/mark"/> </rotate> </item> </layer-list>
使用屬性動畫而不是多圖片連續播放的幀動畫
這裏就不過多解釋了,好比你作一個圖片的旋轉,經過屬性動畫便可用代碼搞定。若是你要用多張圖片「連續播放」的話,那要用不少張預備圖片才行,無疑加大了apk的體積
放棄一些圖片資源
咱們知道Android是支持多分辨率的,提供了多套圖片適配的機制,咱們根據不一樣尺寸的屏幕來放入多套的圖片,好比ldpi、mdpi、hdpi、xhdpi等。可是實際上有些圖片你多是不須要放置的。
此外,咱們所引入的第三方包中可能也引用了資源圖片,可是其中的某些咱們多是不想要的,好比ldpi和xxxhdpi,那麼是否能夠設置什麼東西來讓打包的時候剔除他們呢?是能夠的,能夠配置下build.gradle
defaultConfig {
// ...
resConfigs "en", "de", "fr", "it"
resConfigs "hdpi", "xhdpi", "xxhdpi"
}
defaultConfig提供了resConfig這個flavor來指定打包出只打包某些資源,好比字串、圖片等等
壓縮圖片
圖片從美工那邊拿到的時候是比較漂亮的,可是代價是size一般也高居不下,咱們能夠用一些工具來「近乎無損」的縮小圖片資源,同時不下降圖片效果。這裏推薦pngquant這個工具,能夠參考個人上篇博客
http://www.cnblogs.com/soaringEveryday/p/5148881.html
so的優化
咱們在接入百度地圖的時候,發現須要引入不少不少so
這些so文件佔了不少大致積,若是你不加控制,全部的so都會打包到你的apk了,最後發現這些so文件盡然佔了咱們apk的近乎三分之一的體積。然而考慮下咱們的用戶,基本都是跑在手機上的(沒有人跑在模擬器上),因此明顯x86和x86_64的so是不須要支持,那麼咱們能夠經過配置gradle來制定只打包某些so,依然是在defualtConfig中:
defaultConfig {
... ...
ndk {
//設置支持的SO庫架構
abiFilters 'arm64-v8a', 'armeabi' //, 'x86', , 'x86_64', 'arm64-v8a'
}
}
最後打包出來的apk真的是減小了好幾個MB,這是太好了
固然若是有一些so是大家本身開發的,那麼能夠參考這個文章來參考若是用ndk開發的時候減小so自己的體積,這裏就不過多介紹了
https://blog.algolia.com/android-ndk-how-to-reduce-libs-size/
對第三方庫進行從新定製(從新打jar包)
開發中引入大量的第三方開發庫也是一個增長apk體積的重要緣由,由於你把人家的代碼和資源全給包含進來了。可是想一想人家的代碼,並不必定全要的,是否能夠只引入人家的一部分代碼,而不是在build.gradle中僅僅添加一行「compile」來所有依賴呢?答案是能夠得!這裏舉一個例子
咱們開發中有一個需求是將數據經過圖標的方式顯示出來,這裏咱們站在巨人的肩膀上,使用了MPAndroidChart這個優秀的開源項目(https://github.com/PhilJay/MPAndroidChart),可是發現他們的東西太多了,咱們僅僅須要使用其中一種chart。若是在build.gradle裏面加一句:
dependencies {
compile 'com.github.PhilJay:MPAndroidChart:v2.2.3'
}
這要把他們的庫全給引用過來了。想到他們是開源的,代碼有,因此咱們僅僅把他們的咱們所用到的代碼給剝離出來,單獨打包了一個jar包引入到咱們的項目裏面,就OK了,減小了大量的無用依賴代碼!
動態加載技術(插件化)
如今大型互聯網移動App不少都採用了動態加載的技術,由於他們的業務需求太大,經過動態加載技術能夠將一部分業務模塊獨立出來,以插件的方式分割出去,這樣子主apk的體積就大大減少。當用戶安裝主apk後,靜默的在後臺下載插件apk,當用戶點擊使用到相關的子模塊項目時候,動態的加載插件apk。
動態加載技術無疑從根本上減小了apk的體積,可是引入這個技術是有代價的,增長了項目的維護難度和開發難度。因此該技術適用於大型的移動應用,當你的業務大到不分開模塊難以高效率開發維護的時候,再考慮動態加載技術吧,不然若是小規模應用,仍是老老實實考慮傳統的android官方推薦的開發方式。
這裏推薦幾個比較好的動態加載開源框架項目供你們研究