「性能優(yōu)化1.0」啟動分類及啟動時間的測量
「性能優(yōu)化1.1」計算方法的執(zhí)行時間
「性能優(yōu)化1.2」異步優(yōu)化
一嘹吨、異步優(yōu)化
在上一小節(jié)中卵凑,我通過獲取應用的啟動時間和每一個方法執(zhí)行之間之后萌庆,我們發(fā)現(xiàn),如果在 Application 或者 MainActivity 生命周期中串行去執(zhí)行這些第三方庫的初始化,是會拖慢整個應用的啟動過程的,因此我們想通過子線程與主線程并行的方式來分擔主線程的工作猜惋,從而減少主線程的執(zhí)行時間。
1.1培愁、讓任務執(zhí)行在子線程中
1.1.1著摔、常規(guī)方案
我們常規(guī)的方式是怎樣的呢?
public void onCreate(){
new Thread() {
public run() {
//執(zhí)行任務1
//執(zhí)行任務2
//執(zhí)行任務3
}
}.start();
}
但是這樣是不優(yōu)雅的竭钝,首先直接 new Thread()
這種方式比較簡單粗暴梨撞,而且這里只是開啟一個線程,我們最初的想法是想每一個異步任務就使用一個線程去執(zhí)行香罐。那么我們的偽代碼就變成如下這種方式:
public void onCreate(){
new Thread() {
public run() {
//執(zhí)行任務1
}
}.start();
new Thread() {
public run() {
//執(zhí)行任務2
}
}.start();
new Thread() {
public run() {
//執(zhí)行任務3
}
}.start();
}
那要多個線程卧波,那我就創(chuàng)建多個線程唄,當然這種方式確實要比第一種好一些庇茫,因為它可以更加充分地利用 CPU 港粱,但是直接創(chuàng)建線程還是不優(yōu)雅,所以使用線程池來管理這些線程會好一些旦签。
1.1.2查坪、線程池管理
通過以下方式就可以獲取到我們對應的線程池,但是這個線程個數(shù)不能隨意填宁炫,我們要能充分利用到 CPU 資源偿曙,因此我們可以參考 AsyncTask
它是如何去設置核心線程數(shù)
的。
Executors service = Executors.newFixedThreadPool(核心線程個數(shù));
AsyncTask 設置核心線程數(shù)
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//CORE_POOL_SIZE 就是核心線程數(shù)
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
因此有了這個核心線程數(shù)
之后我們的代碼就變成如下方式:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//參考AsyncTask來設置線程的個數(shù)羔巢。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
關于
核心線程數(shù)
的設置是一個比較小的知識點望忆,不過這個對 CPU 資源的利用是很有幫助的。
通過改造后竿秆,我們現(xiàn)在的代碼如下:
@Override
public void onCreate() {
super.onCreate();
//參考AsyncTask來設置線程的個數(shù)启摄。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
initBugly();
}
});
service.submit(new Runnable() {
@Override
public void run() {
initImageLoader();
}
});
}
還記得我們來上一小節(jié)中使用 AOP 來計算每一個方法的耗時,那么現(xiàn)在我們來對比一下通過異步加載和沒有異步加載這兩種方式的時間差別幽钢。
- 沒有異步加載的代碼執(zhí)行結果
2019-03-17 20:29:12.946 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-03-17 20:29:12.979 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.initBugly() cost:12
2019-03-17 20:29:13.002 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.initImageLoader() cost:23
2019-03-17 20:29:13.002 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.onCreate() cost:35
- 異步加載的代碼執(zhí)行結果:
2019-03-17 22:07:38.022 13948-13948/com.example.perfermance E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-03-17 22:07:38.062 13948-13948/com.example.perfermance E/PerformanceAop: method MyApplication.onCreate() cost:3
2019-03-17 22:07:38.078 13948-13967/com.example.perfermance E/PerformanceAop: method MyApplication.initBugly() cost:15
2019-03-17 22:07:38.094 13948-13968/com.example.perfermance E/PerformanceAop: method MyApplication.initImageLoader() cost:28
通過兩次輸出的 log 數(shù)據(jù)對比歉备,可以看出主線程執(zhí)行的 onCreate
方法的執(zhí)行時間從原來的 35ms 減到到了 3ms 。
但是這里又有另外一個問題匪燕,那就是有一些方法是必須在 Application onCreate 執(zhí)行完成之前完成初始化的蕾羊,因為在 MainActivity 中就需要使用到,那我們上面的異步就會有問題了帽驯,那如何解決這個問題呢肚豺?
1.1.3、異步任務必須在某一個階段執(zhí)行完成
我們還是以 initBugly()
方法來舉例界拦,這個方法是在異步線程中執(zhí)行吸申,如何控制讓其在 Application onCreate 執(zhí)行完畢之前它先完成呢?
這時就需要使用到 CountDownLatch
了,我們先來看看示例圖:
- 定義一個
CountDownLatch
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
- 在方法執(zhí)行完畢時截碴,執(zhí)行
countDownLatch.countDown()
private void initBugly() {
try {
//模擬initBugly耗時
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, "初始化initBugly完畢");
//數(shù)量減一
countDownLatch.countDown();
}
- 等待
countDownLatch.await()
在 onCreate 方法結束點等待梳侨,如果在此處之前之前調用了countDownLatch.countDown()
,那么就直接跳過日丹,否則就在此等待走哺。
public void onCreate() {
super.onCreate();
//參考AsyncTask來設置線程的個數(shù)。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
initBugly();
}
});
service.submit(new Runnable() {
@Override
public void run() {
initImageLoader();
}
});
//在 onCreate 方法中等待哲虾,如果在此處之前之前調用了countDownLatch.countDown()丙躏,那么就直接跳過,否則就在此等待束凑。
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "Application onCreate 執(zhí)行完畢");
}
這樣晒旅,我們的 Application onCreate 方法就會等待異步任務 initBugly 執(zhí)行完畢之后才會結束 onCreate 這個方法的生命周期。