「性能優(yōu)化1.2」異步優(yōu)化

「性能優(yōu)化1.0」啟動分類及啟動時間的測量
「性能優(yōu)化1.1」計算方法的執(zhí)行時間
「性能優(yōu)化1.2」異步優(yōu)化

一、異步優(yōu)化

在上一小節(jié)中,我通過獲取應(yīng)用的啟動時間和每一個方法執(zhí)行之間之后,我們發(fā)現(xiàn),如果在 Application 或者 MainActivity 生命周期中串行去執(zhí)行這些第三方庫的初始化,是會拖慢整個應(yīng)用的啟動過程的,因此我們想通過子線程與主線程并行的方式來分擔主線程的工作,從而減少主線程的執(zhí)行時間。

并行執(zhí)行

1.1、讓任務(wù)執(zhí)行在子線程中

1.1.1、常規(guī)方案

我們常規(guī)的方式是怎樣的呢?

public void onCreate(){
    new Thread() {
        public run() {
            //執(zhí)行任務(wù)1
            //執(zhí)行任務(wù)2
            //執(zhí)行任務(wù)3
        }
    }.start();
}

但是這樣是不優(yōu)雅的,首先直接 new Thread() 這種方式比較簡單粗暴,而且這里只是開啟一個線程,我們最初的想法是想每一個異步任務(wù)就使用一個線程去執(zhí)行。那么我們的偽代碼就變成如下這種方式:

public void onCreate(){
    new Thread() {
        public run() {
            //執(zhí)行任務(wù)1
        }
    }.start();
    
    new Thread() {
        public run() {
            //執(zhí)行任務(wù)2
        }
    }.start();
    
    new Thread() {
        public run() {
            //執(zhí)行任務(wù)3
        }
    }.start();
}

那要多個線程,那我就創(chuàng)建多個線程唄,當然這種方式確實要比第一種好一些,因為它可以更加充分地利用 CPU ,但是直接創(chuàng)建線程還是不優(yōu)雅,所以使用線程池來管理這些線程會好一些。

1.1.2、線程池管理

通過以下方式就可以獲取到我們對應(yīng)的線程池,但是這個線程個數(shù)不能隨意填,我們要能充分利用到 CPU 資源,因此我們可以參考 AsyncTask 它是如何去設(shè)置核心線程數(shù)的。

Executors service = Executors.newFixedThreadPool(核心線程個數(shù));

AsyncTask 設(shè)置核心線程數(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è)置線程的個數(shù)。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);

關(guān)于核心線程數(shù)的設(shè)置是一個比較小的知識點,不過這個對 CPU 資源的利用是很有幫助的。

通過改造后,我們現(xiàn)在的代碼如下:

@Override
public void onCreate() {
    super.onCreate();
    //參考AsyncTask來設(shè)置線程的個數(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í)行結(jié)果
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í)行結(jié)果:
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、異步任務(wù)必須在某一個階段執(zhí)行完成

我們還是以 initBugly() 方法來舉例,這個方法是在異步線程中執(zhí)行,如何控制讓其在 Application onCreate 執(zhí)行完畢之前它先完成呢?

這時就需要使用到 CountDownLatch 了,我們先來看看示例圖:

異步任務(wù)必須在某一個階段執(zhí)行完成
  • 定義一個 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 方法結(jié)束點等待,如果在此處之前之前調(diào)用了countDownLatch.countDown(),那么就直接跳過,否則就在此等待。

public void onCreate() {
    super.onCreate();
    //參考AsyncTask來設(shè)置線程的個數(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 方法中等待,如果在此處之前之前調(diào)用了countDownLatch.countDown(),那么就直接跳過,否則就在此等待。
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.e(TAG, "Application onCreate 執(zhí)行完畢");
}

這樣,我們的 Application onCreate 方法就會等待異步任務(wù) initBugly 執(zhí)行完畢之后才會結(jié)束 onCreate 這個方法的生命周期。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,668評論 1 32
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,711評論 1 4
  • 第十章:Android的消息機制 Handler是Android消息機制的上層接口,開發(fā)人員只需要與它交互即可,底...
    loneyzhou閱讀 808評論 0 1
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,281評論 0 8
  • 正與生命里的一切相同 我們愛得太是匆匆 好像只是昨天 彼此叫著專屬于對方的名字 我總是說,怎么又下雨了? 而你總是...
    若惜惜閱讀 268評論 0 0

友情鏈接更多精彩內(nèi)容