項目重構(gòu)App的啟動屹徘,之前的處理是使用SplashActivity作為過渡頁走趋,然后將Application中的第三方SDK放到IntentService中加載,啟動過程中的一些耗時操作也都去掉了噪伊,奈何MainActivity中的業(yè)務(wù)邏輯還是太過復(fù)雜簿煌,啟動仍然比較漫長氮唯。研究了一下
淘寶
項目的啟動處理,總結(jié)出一種非常優(yōu)化的App啟動方案姨伟。
傳統(tǒng)的啟動處理
市面上大部分的App啟動處理如出一轍惩琉,基本都是先進(jìn)入SplashActivity,然后Handler延時后跳轉(zhuǎn)到MainActivity夺荒。這樣做的好處是SplashActivity作為過渡頁更加輕量瞒渠,啟動到顯示界面的時間更短,給用戶較好的體驗技扼,而且SplashActivity中也可以提前做一些數(shù)據(jù)處理在孝,減輕MainActivity的壓力。但是加入SplashActivity過渡頁也有一些缺點淮摔,比如要多繪制一個頁面,要使用Intent傳遞數(shù)據(jù)始赎,不能減輕MainActivity頁面的繪制壓力和一些數(shù)據(jù)加載和橙。
除此之外,Application的處理也是一個很重要的點造垛,因為App啟動是先走Application的onCreate方法的魔招,如果onCreate中加載SDK的時間過于漫長,就會導(dǎo)致App啟動后顯示一段時間白屏頁才會進(jìn)入SplashActivity五辽,當(dāng)然也可以使用windowBackGround屬性避免白屏办斑,但是時間長了總是會影響用戶體驗的。所以Application中一般都是采用IntentService來加載SDK的杆逗。
為了解決App啟動慢和SplashActivity過渡啟動的缺點乡翅,個人去研究了一下淘寶的啟動,發(fā)現(xiàn)第一次啟動耗時大概3秒蠕蚜,然后退出去悔橄,再進(jìn)入時就不會顯示Splash癣疟,直接進(jìn)入主頁讀取緩存顯示頁面。我猜測淘寶的啟動肯定是根據(jù)應(yīng)用的進(jìn)程是否存活邪蛔,如果存活就直接進(jìn)入MainActivity店溢,否則顯示Splash,而且淘寶的Splash加載很快荣回,MainActivity的加載也很快戈咳,所以淘寶應(yīng)該是采用了Splash綁定在Mainactivity上的方式著蛙,即SplashFragment+StubView
的方式啟動App。
優(yōu)化后的Splash
傳統(tǒng)的App啟動方式不足以滿足現(xiàn)在用戶的體驗猎唁,用戶需要的是加載更快顷蟆,性能更優(yōu)的體驗帐偎,所以這里采用SplashFragment+StubView的方式去實現(xiàn)App的快速啟動。
跟傳統(tǒng)啟動方式最大的不同是,優(yōu)化后的啟動會直接進(jìn)入MainActivity漫贞,然后在MainActivity控制Splash的顯示隱藏,這樣在填充Splash的時候還以為直接加載MainActivity的數(shù)據(jù)摇肌,當(dāng)窗體填充完畢之后仪际,就可以繪制MainActivity的布局树碱,然后移除Splash,展示頁面框舔,這種方式很好的體現(xiàn)了異步的操作,使得填充和加載分離樱溉,啟動的速度更快纬凤。
既然知道了這種實現(xiàn)方式,那具體代碼是如何編寫的呢挖帘,接下來看一下如何實現(xiàn)拇舀。
1. 填充SplashFragment
在MainActivity的onCreate方法中骄崩,首先薄辅,需要做的就是填充Splash并顯示,顯示的內(nèi)容就可以在SplashFragment中編輯。
log.d(TAG, "==========填充SplashFragment==========");
// 1.初始化SplashFragment源请,填充Splash
final SplashFragment splashFragment = new SplashFragment();
getFragmentManager().beginTransaction().replace(R.id.container,splashFragment).commitAllowingStateLoss();
2. 窗體部署完畢彻况,填充主頁布局
當(dāng)整個頁面最底層DecorView填充完畢之后纽甘,就可以開始去填充MainActivity的布局了,并且使用Handler發(fā)送延時消息决瞳,2秒后移除Splash皮胡。當(dāng)然移除Splash也可以直接在數(shù)據(jù)加載完畢顯示頁面之前移除掉赏迟。
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
// 填充布局
viewStub.inflate();
// 初始化布局
initView();
// 2秒后移除Splash
mHandler.postDelayed(new DelayRunnable(MainActivity.this, splashFragment), 2000);
}
});
3. 加載MainActivity數(shù)據(jù)
其實加載數(shù)據(jù)會在第二步之前執(zhí)行,因為第二步是異步操作泻仙,并且加載數(shù)據(jù)是耗時操作量没,所以需要更早執(zhí)行允蜈,不要放在第二步里去執(zhí)行饶套,這里在測試的時候有可能數(shù)據(jù)先加載完畢,布局才繪制完怠李,調(diào)用控件就會有空指針問題蛤克,但是實際開發(fā)中肯定不會出現(xiàn)這種情況的构挤,MainActivity中的數(shù)據(jù)加載肯定很耗時筋现。
Application的優(yōu)化
Application是應(yīng)用的核心樞紐,應(yīng)用啟動時會先走Application的onCreate方法去初始化一些全局?jǐn)?shù)據(jù)一膨,比如SDK等等豹绪。但是當(dāng)全局?jǐn)?shù)據(jù)很多時,App的啟動就會變得緩慢瞒津,因為初始化是同步加載的括尸,所以要想加快啟動姻氨,就需要將延時操作放到后臺去處理。
Android里有一個Service前联,正好可以做延時處理的操作似嗤,這個Service就是IntentService。關(guān)于IntentService乘粒,不了解的看這里灯萍。
IntentService 示例與詳解
IntentService文檔
接下來旦棉,看一下如何實現(xiàn)药薯。
自定義InitializeService繼承IntentService童本,實現(xiàn)構(gòu)造和方法onHandleIntent
在Application的onCreate方法中啟動初始化服務(wù)
// 啟動服務(wù)去做耗時操作
InitializeService.start(this);
public static void start(Context context) {
Intent intent = new Intent(context, InitializeService.class);
intent.setAction(ACTION_APP_LAUNCHER);
context.startService(intent);
}
- 在onHandleIntent方法中判斷Intent穷娱,然后去初始化SDK
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_APP_LAUNCHER.equals(action)) {
performInit();
}
}
}
/**
* 啟動初始化耗時操作
*/
private void performInit() {
// 模擬延時加載
SystemClock.sleep(2000);
Log.d(TAG, "==========初始化第三方SDK結(jié)束==========");
}
注意:
- 在清單文件中配置Application和Service
- 并不是所有的SDK都能放到Service中去初始化的,在MainActivity中用到的還是需要提前初始化