最近針對手上的項目做了一些Android App啟動速度的優(yōu)化,查閱了一些資料
影響啟動速度的原因
高耗時任務
數(shù)據(jù)庫初始化、某些第三方框架初始化、大文件讀取、MultiDex加載等,導致CPU阻塞
復雜的View層級
使用的嵌套Layout過多,層級加深,導致View在渲染過程中,遞歸加深,占用CPU資源,影響Measure、Layout等方法的速度
類過于復雜
Java對象的創(chuàng)建也是需要一定時間的,如果一個類中結構特別復雜,new一個對象將消耗較高的資源,特別是一些單例的初始化,需要特別注意其中的結構
優(yōu)化的案例如下:
Glide及其他框架
Glide是一個很好用的圖片加載框架,除了常用的圖片加載、緩存功能以外,Glide支持對網(wǎng)絡層進行定制,比如換成OkHttp來支持HTTP 2.0。不過,如果在追求啟動速度的情況下,在Splash頁或主界面加載某一張圖片時,往往是第一次使用Glide,由于Glide沒有初始化,會導致這次圖片加載的時間比較長(不管本地還是網(wǎng)絡),特別是在其他操作也在同時搶占CPU資源的時候,慢的特別明顯!而后面再使用Glide加載圖片時,還是比較快的
解決方案:Application的onCreate方法中,在工作線程調(diào)用一次Glide.get(this)
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
Glide.get(BaseApplication.this);
}
}).start();View和主題
View層級
主要在于首屏/Splash頁的Layout布局層次過深,導致View在渲染時,遞歸加深,消耗過多的CPU和內(nèi)存資源,阻塞主線程,所以最根本的思路就是解決層級問題,檢查一個App的View層級,可以使用Android Studio自帶的Layout Inspector工具,如圖:

在選擇了需要檢查的進程及Window(Dialog可能會創(chuàng)建新的Window,但顯示的Activity是同一個)以后,就可以看到Android Studio自動進行的Capture的內(nèi)容了

根據(jù)左邊View層級顯示的內(nèi)容,分析不必要的嵌套布局,通過改造,即可對View層級進行優(yōu)化
當然優(yōu)化布局文件外;我們還可以通過代碼構建控件,由于加載解析xml文件也是耗時的
App主題
如果想提高APP的啟動速度,尤其是使用Splash的App,務必將第一個Activity的主題設為FullScreen的,這樣能有效提高啟動速度;啟動一個Activity的時候,系統(tǒng)會創(chuàng)建包含一個DecorView的Window,而StatusBar也好,ActionBar也好,都是這個View中的子元素,多了一個View,當然多了一層布局,肯定是耗時的
進一步優(yōu)化
某些APP,如:微博,能夠做到點了圖標就立即做出響應,顯示出它的Splash頁
apktool一下微博的apk,可以發(fā)現(xiàn)微博對首頁的主題背景,使用了一個drawable來實現(xiàn)
<!-- styles.xml -->
<style name="NormalSplash" parent="@android:style/Theme">
<item name="android:windowBackground">@drawable/welcome_layler_drawable</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:scrollbarThumbVertical">@drawable/global_scroll_thumb</item>
<item name="android:windowAnimationStyle">@style/MyAnimationActivity</item>
</style>
<!-- welcome_layler_drawable.xml -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@id/welcome_background" android:drawable="@drawable/welcome_android" />
<item android:bottom="@dimen/login_icon_padding_bottom">
<bitmap android:gravity="bottom|center" android:src="@drawable/welcome_android_logo" />
</item>
<item android:top="@dimen/splash_slogan_margin_top">
<bitmap android:gravity="center|top" android:src="@drawable/welcome_android_slogan" />
</item>
<item android:top="20.0dip" android:right="20.0dip">
<bitmap android:gravity="center|right|top" android:src="@drawable/channel_logo" />
</item>
</layer-list>
由此可見,使用layer-list的形式,可以使一系列的Bitmap按照類似View布局的形式來排布,通過將生成的drawable設置為background的形式,最終并不會生成任何View,極大程度減小View繪制占用的時間,提升啟動速度!
對于多線程的思考
在App啟動時,為了加快啟動速度,通常會使用多線程手段來并行執(zhí)行任務,充分發(fā)揮多核CPU的優(yōu)勢,提高運算效率。此方法固然能夠?qū)铀俣鹊膬?yōu)化,起到一定作用,但實際開發(fā)中,有以下幾點值得深思:
并發(fā)的線程數(shù),多少合適?(效率高但不至于阻塞)
頻繁切換線程,是否帶來負面影響?(頻繁地從主線程扔進輔助線程操作再將結果拋回來會不會比直接執(zhí)行更慢)
何時并行?何時串行?(有的任務能只能串,有的任務可以并行)
這個時候,拿Android經(jīng)典的AsyncTask類來說事,再合適不過了!
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
上面的代碼是AsyncTask確定線程池數(shù)量的部分,其中,核心執(zhí)行池保證最少2個線程,最多不超過CPU可用核數(shù)-1,最大線程池數(shù)量為CPU核數(shù)的2倍+1
這樣配置線程池的目的很簡單:防止并發(fā)過大,導致CPU阻塞,影響效率