如需轉(zhuǎn)載請評論或簡信,并注明出處适秩,未經(jīng)允許不得轉(zhuǎn)載
系列文章
目錄
前言
前面的章節(jié)我們已經(jīng)介紹了啟動分類的啟動流程,同時也介紹了啟動時間的測量方式寇窑。接下來將介紹兩種啟動優(yōu)化方式 — 視覺優(yōu)化和異步優(yōu)化
視覺優(yōu)化
app啟動后鉴象,WindowManager
會先加載app theme
中的windowBackground
梨撞,所以通常就會出現(xiàn)白屏的情況(取決于你的主題是Dark
還是Light
),我們對windowBackground
進行設(shè)置渐北,讓用戶從視覺上感覺app的啟動變快
解決方案
- 新增一個主題樣式阿逃,設(shè)置
windowBackground
屬性,在其中放一張背景圖片,或是廣告圖片之類的
<style name="AppTheme.Launcher" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/bg_launcher</item
</style>
bg_launcher.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap
android:gravity="center"
android:src="@drawable/ic_android" />
</item>
</layer-list>
- 給
MainActivity
設(shè)置主題
<activity android:name=".MainActivity"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- 在
MainActivity
的onCreate()
方法執(zhí)行前將主題替換回原來的樣式
@Override
protected void onCreate(Bundle savedInstanceState) {
//替換為原來的主題,在onCreate之前調(diào)用
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
這種方式雖然不能加快應(yīng)用的啟動時間恃锉,但是可以防止應(yīng)用啟動白屏搀菩,帶來更好的用戶體驗
異步優(yōu)化
如何進行異步優(yōu)化
核心思想:子線程分擔(dān)主線程任務(wù),通過并行減少時間
異步優(yōu)化的思想其實很明確破托,也很容易理解肪跋,但是真正在異步優(yōu)化的實踐過程中,其實是有很多“坑”的
這里寫了一個簡單的demo土砂,我們在Application
中進行了一些初始化操作州既,實際場景往往比這要復(fù)雜很多
Application.java
@Override
public void onCreate() {
super.onCreate();
//高德地圖
initAMap();
//weex
initWeex();
//Stecho
initStetho();
//圖片加載
initFresco();
//自己寫的代碼
initDeviceId();
//推送
initJPush();
}
顯然,上面這種初始化方式是串行的萝映,如果初始化的內(nèi)容過多吴叶,顯然啟動速度就會變慢。結(jié)合我們說的異步初始化的思想锌俱,我們使用了線程池來來優(yōu)化我們的初始化晤郑,使得初始化代碼能夠并行
這里的CORE_POOL_SIZE
我們參照了AsyncTask
的源碼,根據(jù)cpu數(shù)量來確定線程池的核心池的數(shù)量
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));
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
//高德地圖
initAMap();
}
});
service.submit(new Runnable() {
@Override
public void run() {
//weex
initWeex();
}
});
service.submit(new Runnable() {
@Override
public void run() {
//Stecho
initStetho();
}
});
....
}
如果對線程池比較熟的人會知道贸宏,現(xiàn)在Application
的onCreate()
方法一定執(zhí)行的特別快造寝。但是有的人可能會問,這里我們能不能把所有初始化方法都放到一個Runnable
中去呢吭练?
這樣做理論上其實是可以的诫龙,因為它是異步的,但是上面說了鲫咽,我們這里其實是根據(jù)cpu數(shù)量來確定線程池的核心池的數(shù)量签赃,假設(shè)創(chuàng)建出來3個線程,但是我們只用了一個分尸,那這樣顯然是一種資源的浪費锦聊,所以我們采取了對每個初始化方法都進行了submit
的方式
我們可以看到,通過異步的方式進行初始化箩绍,效果是很明顯的
異步優(yōu)化的問題
見識到了異步優(yōu)化的成果孔庭,但是先不要急著高興,簡單思考一下的話我們就會發(fā)現(xiàn)材蛛,這種方式其實是并不完全合理的圆到,很多場景下我們實際上并不能直接使用這種方案,這里舉幾個例子
- 不符合異步要求卑吭。實際項目初始化往往比較復(fù)雜芽淡,有時候會遇到某些初始化方法一定要在主線程執(zhí)行,那么用上面這種方式顯然也是不可行的
-
需要在某階段完成豆赏。例如我們在閃屏頁面就要用到
weex
的相關(guān)方法挣菲,這時候如果weex
在子線程還沒有初始化成功富稻,顯然就會有問題 -
任務(wù)之間有依賴關(guān)系。例如我們這里的
initJPush()
方法己单,這個方法需要用到DeviceId
唉窃,所以就需要在initDeviceId()
之后執(zhí)行
針對問題1,我們需要修改我們的代碼纹笼,把不符合異步要求的方法放到主線程執(zhí)行
針對問題2纹份,這里介紹一個小技巧,我們用到了CountDownLatch
廷痘,構(gòu)造方法中傳入1
蔓涧,這里的意思是countDownLatch
必須被滿足一次,也就是說直到 countDownLatch.countDown()
被執(zhí)行笋额,否則countDownLatch.await()
會一直等待
private CountDownLatch countDownLatch = new CountDownLatch(1);
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
.....
service.submit(new Runnable() {
@Override
public void run() {
//weex
initWeex();
countDownLatch.countDown();
}
});
.....
try {
countDownLatch.await();
}catch (Exception e){
e.printStackTrace();
}
}
針對問題3元暴,我們很容易想到下面這種方式,把initDeviceId()
放到initJPush()
之前
service.submit(new Runnable() {
@Override
public void run() {
initDeviceId();
initJPush();
}
});
這樣做的話兄猩,看起來好像解決了問題茉盏。但是實際場景往往很復(fù)雜,有很多類似的這種依賴關(guān)系枢冤,我們很可能遇到一個情況鸠姨,就是我們并不能直接把某個初始化方法移到同一個Runnable
下
我們上面這種寫法還有一個很大的問題,就是代碼不優(yōu)雅淹真,如果實際項目中有一百個初始化項目呢讶迁?難道就要寫一百次service.submit()
方法,我相信很多同學(xué)肯定是無法接受的核蘸,因為不優(yōu)雅的代碼同時也帶了很高的維護成本
總結(jié)
本文介紹了兩個比較常規(guī)的啟動優(yōu)化方案巍糯,一個是視覺優(yōu)化,一個是異步優(yōu)化客扎。但是經(jīng)過本文的分析祟峦,我們看到了,常規(guī)的異步優(yōu)化方法徙鱼,在真實項目中實施起來搀愧,其實是存在很多問題的。那我們?nèi)绾蝸斫鉀Q這些問題呢疆偿?請看啟動優(yōu)化(四),直接項目奉上搓幌,有興趣可以自己看看杆故,具體分析可能要等到哪天有空我想分析的時候了。溉愁。