編輯:關於Android編程
讓你的apk文件盡可能小,應該使移除未使用的代碼和資源文件。那麼本章節介紹了如何做到讓APK更小,性能更好,下載轉化率會更高,以及如何指定在構建APK過程中保留或移除的代碼和資源,在我們還沒有分析APK大小之前,項目中存在一些資源放置處理不當,沒有統一的規范,依賴管理不合理,資源重疊,dex方法數過多等問題,導致APK文件比較大,公司要求APK體積大小要優化到3M左右。經過我們的努力終於達到要求,然而我們發現還能再小。
正所謂工欲善其事,必先利其器,我們得現有利器,下面就是我們常用的分析APK大小工具的利器。
Android Studio 2.2 新功能直接能分析APK的大小,雙擊打開就能看到那些占用APK比例大,方法數等。
分析任何的APK 查看APK下載包的大小,解壓後的實際大小 反編譯資源文件,還原layout中的資源id,代碼 分析dex,顯示每部分的方法數,直接查看那些library體積比較大使用方法:Build -> Analyz APK
有了Analyz APK這個利器,以下工具也可以基本不用了<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="nimbledroid">NimbleDroid
NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的自動化分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析可以分析出APK安裝包中大文件排行榜,各種知名SDK的大小以及占代碼整體的比例,各種類型文件的大小以及占排行,各種知名SDK的方法數以及占所有dex中方法數的比例,針對緩慢的方法,緩慢的第三方SDK和內存洩漏。
測量生成的速度、網絡、內存和磁盤使用率。總之有非常多分析App性能的功能,如果要做性能優化,也可以嘗試使用NimbleDroid。
查看詳細的方法耗時
具體使用方法請看官網:
https://nimbledroid.com/
ClassShark 是一款查看Android執行文件(apk)的浏覽工具,目前有兩個android App(Apk)和桌面(jar)的版本。
使用這款工具,可以很方便的打開APK/Class/Jar/res
等 文件和分析裡面的內容。
具體源碼與使用方法詳細在github中:
https://github.com/google/android-classyshark
通過以上任一工具分析我們知道我們項目中主要是以下文件占用APK大小:
classes.dex
classes.dex是java源碼編譯後生成的java字節碼文件,
res
主要是存放我們的圖片資源
resources.arsc
編譯後的二進制資源文件,非常多無效資源文件(語言)
assets
主要存放了我們的緩存數據文件,已做最優化壓縮,我們考慮能否雲端存放。
lib
主要是存放我們的so庫,目前我們已經優化了
既然知道了那些數據導致我們APK體積大,那麼我們就著手瘦身了。
對資源進行極限壓縮,主要是如res裡面用到的圖片資源文件和assets的html,db等一些緩存預留在APK的數據文件
assets資源壓縮,使用7zip或者lzma壓縮方式最高 res 圖片資源的壓縮,使用tinypng優化Android的資源圖片,通常我們可以在保證圖片不失真的情況下,多壓縮幾次。目前tinypng已經支持png和jpg圖片、.9圖的壓縮 將非alpha的圖轉換成jpg形式通過以上方法我們圖片降低了79%的大小。
這裡提供方便轉換的WEBP資源的工具:
https://isparta.github.io/https://imageoptim.com/mac
使用VectorDrawable和SVG圖片來替換原有圖片微信中的資源混淆工具主要為了混淆資源ID長度(例如將res/drawable/icon.png,png變成混淆為r/s/a.png),同時利用7z深度壓縮、對png的存儲方式做了改變占用內存更小,大大減少了安裝包體積
具體源碼與使用方法詳細在github中:
https://github.com/shwenzhang/AndResGuard
通過上面的圖片資源壓縮能對APK減小不少,但這往往還不夠,項目裡還有很多未使用的資源文件,重復的資源等,這裡主要參考Google官方文檔https://developer.android.com/studio/build/shrink-code.html 部分,利用Android Plugin開啟gradle 的Code shrinking和ProGuard結合使用。
ProGuard能夠檢測和刪除未使用的類,字段,方法,和從你的打包應用程序的屬性,包括那些包含代碼庫,ProGuard是一個混淆優化字節碼的工具,能夠刪除一些未使用的代碼,混淆使用的類,字段,方法和短名稱,經過混淆處理也能夠使APK源代碼得到保護
Code shrinking是一個Android Plugin for Gradle,從您的打包的應用程序中刪除未使用的資源,包括代碼庫中的未使用的資源。它工作在與代碼縮小,這樣,一旦未使用的代碼已被刪除,任何資源不再引用可以安全地刪除。
該功能需要依賴於:
SDK Tools 25.0.10 或更高 Android Plugin for Gradle 2.0.0 或更高code shrinking需要結合ProGuard使用,添加minifyEnabled true在你的build.gradle文件中。
需要注意code shrinking會減慢Gradle 編譯,應避免使用它在您的調試版本中使用它。Android Studio禁用ProGuard使用 Instant Run.。
例如,以下從build.gradle文件片段,使code shrinking為發布版本
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(‘proguard-android.txt'), 'proguard-rules.pro' } } ... }
其中getDefaultProguardFile(‘proguard-android.txt')默認ProGuard設置來自於Android SDK tools/proguard/中的文件夾
更多的代碼減少可以嘗試使用相同位置的proguard-android-optimize.txt文件(這裡我們又減少了0.5M)proguard-rules.pro是你自定義的proguard規則。
每一次build,ProGuard會輸出以下文件在/build/outputs/mapping/release/:
dump.txt在某些情況下,默認的混淆器配置文件proguard-android.txt文件是會移除所有只有未使用的代碼,但也有可能會誤刪除了你需要的代碼,所以要注意以下幾種情況:
在AndroidManifest.xml配置的文件類 使用了JNI 的接口方法 運行時反射調用方法(不過現在ProGuard已經可以處理這種了)添加-keep來忽略一下防止被混淆的代碼到proguard-rules.pro文件中,比如:
-keep public class MyClass
另外也可以使用@Keep 注解在你的需要忽略的代碼中,需要Annotations Support Library的支持
有關自定義proguard-rules.pro文件的更多信息,可以參考ProGuard Manual.這裡Troubleshooting列出了一些常見的問題。
Resource shrinking 只與Code shrinking 一起工作。在代碼中刪除所有未使用的代碼後,Resource shrinking才可以識別哪些資源的應用程序仍然使用,你必須先刪除未使用的代碼,Resource才會成為無用的,從而被清除掉。
添加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目錄保存res/raw/keep.xml文件,build的時候該文件不會被打包到APK裡面。
通常情況下,資源產品可以准確地確定資源使用。如果你使用Resources.getIdentifier()動態獲取指定資源的Id,在默認情況下,這樣資源具有匹配名稱的格式為潛在的使用,無法去除。
例如,下面的代碼將導致所有img_前綴的資源都無法去除。
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
resource shrinker 也通過搜索代碼中是否包含資源名來判斷是否在build的時候刪除。
在 resource 文件中指定 shrinkMode,你可以指定 Gradle 在處理該資源文件時候的方式,默認的值為 safe,你也可以將它指定為 strict(只會保留有明確引用的資源,以及處理被 tools:keep 和 tools:discard 標注的資源)
在後面查看資源回收情況,我們會講到,會遇到有些xml 無法被清除的問題,使用shrinkMode可以解決這個問題。
Gradle resource shrinker 只刪除你在代碼中未使用資源,這意味著它不會刪除不同的設備配置的可替代資源。如果有必要,你可以使用Android Gradle plugin 的resconfigs屬性刪除替代資源文件。
例如:我們項目中適配10種國家語言,而項目依賴了v7、v4等其他support包裡面包含20種國家語言,那麼我們可以通過resconfigs 刪除剩余的可替代資源文件,這對於我們APK大小可減少了不少,
以下代碼說明了如何限制你的語言資源,只是英語和法語:
android { defaultConfig { ... resConfigs "en", "fr" } }
像上面那樣通過resconfig屬性指定的語言。未指定的語言的任何資源都被刪除。
同樣的圖片資源我們也可以這麼做,例如:我們提供一套xxhdpi的圖片資源,其他的都過濾清除掉,這樣大量清除了support,其他第三方library的資源文件,關於這個待會我們在後面會說。
默認情況下,Gradle也將同名的資源,比如相同的名字,可能是在不同的資源文件夾下,這樣子不能通過shrinkResources屬性來去除。
只有當兩個或多個文件共享相同的資源名稱、類型和限定符時才發生資源合並,關於資源的合並優先級如下:
Gradle 主要從以下位置合並資源:
src/main/res/ 主要資源 Gradle 使用variant(build type 和 build flavors) Library的依賴使用Gradle合並重復資源優先順序為:
Dependencies → Main → Build flavor → Build type
例如:如果一個重復的資源在你的mian res中和一個Build flavor指定 ,Gradle 會優先選擇Build flavor
當你Gradle resource shrinker,Gradle Console 輸出日志,移除APK資源的信息。例如:
:android:shrinkDebugResources Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33% :android:validateDebugSigning
APK構建完成後會Gradle會生成一個resource.txt 在 /build/outputs/mapping/release/ 中,這個文件包括詳細信息,如資源參考其他資源和使用或刪除資源的詳細信息等。
例如:找出為什麼@drawable/ic_plus_anim_016,仍然包含在你的APK中,在resource.txt 搜索該文件名,你可能會發現它是被另一個資源引用,如下:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true 16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
現在需要知道為什麼@drawable/add_schedule_fab_icon_anim 仍然在使用,搜索我們可以知道應該有代碼引用著add_schedule_fab_icon_anim。
如果你不使用嚴格的檢查(就是上面講的shrinkMode),同樣的我們如果我們在drawable中使用了字符串’#FFFFFF‘ 這樣的使用resource shrinker也不能將他移除在APK中,通常我們可以在Gradle Console中看到以下信息:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506 used because it format-string matches string pool constant ic_plus_anim_%1$d.
那麼這種情況我們如何解決呢,可以通過以下方法來清理:
使用tools:discard,在 tools:shrinkMode=”strict” 的時候生效,指定某資源文件需要刪除。在你確定該資源文件無效的時候使用。利用Lint找出未使用的資源並清理掉
在Android Studio中打開“Analyze” 然後選擇”Inspect Code…”,范圍選擇整個項目,然後點擊”OK”
到這裡APK的大小又小了不少。
雖然我們上面很好的使用了resource shrinker可以回收一些未使用的資源(v7、v4、google Service 等Libarry資源),但有些資源仍然未被清除。
例如:那些未使用的多套替代資源,或者是library內部隱患著引用著的資源而我們卻沒有使用到。或者是我們要根據用戶的手機去提供不同版本的APK,如分辨率(xxhdpi,mhdpi等),so庫等。那麼我們可以使用APK Splits大大的減少一些無用的資源,這裡我們主要參考了http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 文檔。
APK Splits比起使用 flavors,能讓應用程序更有效地構建一些形式的多個apk。
多 apk 只支持以下類型:
屏幕密度 ABI使用新的APK Splits,構建同一個應用程序的hdpi版本和mdpi版本,能夠共享很多的任務 (如 javac,dx,proguard)。此外,它會被認為是一個單一的variant,並且同一個測試程序將會被用來測試每??個多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
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。
例如:我們項目主要提供xxhdpi的圖片資源,而項目中引用到了很多第三方庫(v7、v4、google Service 等Libarry資源)我們無法使用到,那麼我們可以通過這種方法來去除那些資源。這樣我們的APK又減小了非常多。
Multiple APK Support是一個在Google Play,可以發布不同的應用程序,分別針對不同的設備配置特征。每個APK是一個完整的、獨立的應用程序版本,但他們分享在Google Play相同的應用程序清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣我們的APK 就可以是分不同版本構建需要資源文件,從而減小APK的大小。
通過發布有多個APK,我們可以:
支持不同OpenGL的APK 支持不同的屏幕尺寸和密度的APK 支持不同的設備功能的APK 支持不同的平台版本的APK支持不同的CPU架構,每個apk(如ARM、x86,MIPS等)的APK
更多相關信息請參考https://developer.android.com/google/play/publishing/multiple-apks.html
目前我們基於這個方案做了不同屏幕的APK。
我們可以在項目中使用資源動態加載形式,例如:表情,語言,離線庫等資源動態加載,減小APK的大小。
未來對於一些獨立業務模塊,可以做成插件化動態加載,用戶需要使用時,只需下載少部分插件。
ReDex是Facebook開源一個減小安卓app大小以提高性能的工具,內嵌以及清除僵屍代碼這樣的優化來減小字節碼,主要是對Dex進行了優化,能讓APK 運行更快,不過需要多測試是否會崩潰。
教程很簡單具體更詳細的內容請參考:
https://code.facebook.com/posts/998080480282805/open-sourcing-redex-making-android-apps-smaller-and-faster/
github地址:
https://github.com/facebook/redex.git
簡介這是一個基於AlertDialog和Dialog這兩個類封裝的多種彈出框樣式,其中提供各種簡單樣式的彈出框使用說明。同時也可自定義彈出框。項目地址:http://ww
前置內容:Callable、Future、FutureTask Executor子類的execute方法接收一個Runnable作為參數,會在新線程中執行Ru
效果圖一、繪制圓環圓環故名思意,第一個首先繪制是圓環1:圓環繪制函數圓環APIpublic void drawArc (RectF oval, float startAn
先把來源貼上http://zrgiu.com/blog/2011/01/making-your-android-app-look-better/http://www.di