Android端應用秒開優化體驗

zhengxiaoyong發表於2016-07-28

前言

最近部門內丟擲了一個問題,應用啟動很慢、卡圖示?主要表現在中低端機型中。究其這個問題,由於對效能優化比較感興趣,借了個低端機和一箇中端機來一看究竟,對同一應用分別測了下它在中低端機的啟動時間,下面為啟動耗時情況:

啟動了三次,基本都在4s左右。

原因

究其原因,主要因素是任務在介面繪製前過於集中化。

應用啟動過程從使用者點選launcher圖示到看到第一幀這個過程中,主要會經過以下這些過程:

main()->Application:attachBaseContext()->onCreate()->Activity:onCreate()->onStart()->onPostCreate()->onResume()->onPostResume()

而一般我們的初始化任務主要都會集中化在Application:onCreate()方法中,這就使得初始化任務在第一幀繪製之前得完成,這就造成了卡圖示、應用啟動慢。那麼把任務打散呢?分散在LaunchActivity中去分段初始化?還是不行的,因為介面開始繪製是在onResume()方法開始後才開始繪製,所以,得從Activity的建立過程找辦法。

main->Activity建立的這個過程會經過一系列framework層的操作,這些操作都是系統自動執行的,不易進行優化,不過可以在Activity建立這個過程前後來找一些蛛絲馬跡,因為Activity的建立都會輾轉到ActivityThread:performLaunchActivity()這個方法中,在這個方法中可以知道這麼幾件事:

1、先通過Instrumentation:newActivity()來建立一個Activity例項
2、再判斷Application例項是否已建立,已建立則直接返回,否則呼叫
Instrumentation:newApplication()來建立Application例項,在這個過程中會依次執行attachBaseContext()和onCreate()方法
3、之後Activity:attach()方法會建立一個PhoneWindow物件,它就是介面,它有一個DecorView,呼叫setContentView()時會給配置DecorView,其中就會設定一個背景:

我們的View也是add進DecorView中顯示,它作為RootView肯定是最先顯示,所以可以給它設定個預設背景

4、最後依次呼叫Activity的onCreate、onStart等方法

措施

1、任務分級
2、任務並行
3、介面預顯示

對於任務集中初始化化、耗時初始化原因導致應用在中低端機啟動過慢,而Activity介面繪製的時機導致簡單的將任務分給Activity初始化也不起作用,我們必須找一個切入點

介面的建立和介面的繪製,這兩個過程第一個是Application的attachBaseConte和onCreate這兩個方法影響的,第二個則是Application建立一直到介面繪製

所以,可以對任務進行分級的臨界點可以這樣分:

1、CoreSDK——Application的建立
2、HighPrioritySDK——Activity的建立
3、LowPrioritySDK——Activity介面完成繪製
4、AsyncSDK——Activity的建立

如圖:

launch

對任務這樣分級後,測了一下,應用的啟動即使在低端機上,也能秒開:

分級帶來的問題

正常啟動過程那肯定是沒問題的,不過有這麼幾種場景:

1、App切回後臺,記憶體不足導致Application被回收,從最近任務列表中恢復介面時Application需重新建立
2、應用沒掛起時,Push推送需從Notification跳入應用內某介面
3、應用沒掛起時,瀏覽器外鏈需跳入應用內某介面

這些Case可能導致的問題是被跳入的介面使用到了未初始化的SDK,可能導致Crash或者資料異常,所以目標頁面啟動前必須確保SDK已經初始化,這個過程的原因是沒有喚起啟動頁來初始化SDK,可以通過hook newActivity解決。

public Activity newActivity(ClassLoader cl, String className, Intent intent) 
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    if (InitializeOptimizer.isApplicationCreated()
            && (InitializeUtil.isOuterChainIntent(intent) || 
InitializeUtil.isNotificationIntent(intent)) && (!InitializeOptimizer.isHighSDKInitialized()
            || !InitializeOptimizer.isLowSDKInitialized()
            || !InitializeOptimizer.isAsyncSDKInitialized())) {
        InitializeOptimizer.setApplicationCreated(false);
        intent.addCategory(InitializeUtil.INITIALIZE_CATEGORY);
        return (Activity) cl.loadClass(InitializeOptimizer.getLaunchClassName()).newInstance();
    }
    InitializeOptimizer.setApplicationCreated(false);
    return super.newActivity(cl, className, intent);
}

相關文章