android開發 之 Bolts-Android

TigerJin發表於2021-09-09

目錄

多執行緒程式設計對比: iOS與Android

android的多執行緒程式設計有

  • Thread

  • Handler

  • AsyncTask

  • IntentService

android多執行緒詳細可以參考

iOS的多執行緒程式設計有

  • NSthread

  • GCD

  • NSOperationQueue

iOS多執行緒詳細可以參考 / /

對比兩個平臺的多執行緒程式設計, android還是稍遜一些

  • Handler和IntentService的多執行緒程式設計, 開銷有些大, 而GCD配合Block, 簡潔好用並且功能也很強大

  • AsyncTask處理訊息佇列和多執行緒同步, 是比較費勁的, 而NSOperationQueue, 仍然是簡單好用五顆星

那有沒有一種方案可以讓android變得和iOS一樣簡單好用呢?

答案就是今天的主角

Bolts-Android

Bolts-Android由"榮譽"出品, 為什麼說是"榮譽"呢?

因為作為Baas的鼻祖, Parse已經成功地死在了沙灘上, 但是被Facebook關門之後, 它仍然發揮了餘熱, Parse的很多專案都在上開源了

廢話不多說, 直接看例子吧

Tasks

想要在當前執行緒執行Task, 就是這麼簡單

Task.call(new Callable() {    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in current thread");        return true;
    }
});

如果想在後臺執行緒執行Task

Task.callInBackground(new Callable() {    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in background thread");        return true;
    }
});

如果想在UI主執行緒執行Task

Task.call(new Callable() {    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in main thread");        return true;
    }
}, Task.UI_THREAD_EXECUTOR);

如果想要延時執行Task

Task.delay(2000).continueWith(new Continuation() {    @Override
    public Void then(Task task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in main thread (after delay 2 seconds)");        return null;
    }
});

是不是簡單到讓人無法相信? 其實這裡的Task和iOS中的GCD程式設計是類似的(詳細可以參考)

Chaining Tasks

熱完身, 我們來看下Bolts-Android的函數語言程式設計(關於函數語言程式設計詳細可以參考)

Task.call(new Callable() {    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " calling");        return true;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation() {    @Override
    public String then(Task task) throws Exception {
        Thread.sleep(2000);
        XLog.d(Thread.currentThread().getName() + " onSuccess " + task.getResult());        return "hello bolts";
    }
}, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation() {    @Override
    public Void then(Task task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " continueWith " + task.getResult());        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

列印結果如下

main calling
pool-2-thread-1 onSuccess truemain continueWith hello bolts

透過函式式的鏈式程式設計, 輕鬆地實現了Task的同步和多執行緒的切換(怎麼忽然就想到了了呢? 看來下期可以來個Bolts-Android大戰RxJava的專題)

Group Tasks

Group Task是我取得名字哈, 所謂Group Tasks, 就是指多個Task組成的一組Tasks

Task.call(new Callable() {    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " calling");        return true;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation>>() {    @Override
    public List> then(Task task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " tasks start");
        List> tasks = new ArrayList>();
        tasks.add(asyncOperation1());
        tasks.add(asyncOperation2());
        Task.whenAll(tasks).waitForCompletion();
        XLog.d(Thread.currentThread().getName() + " tasks end");        return tasks;
    }
}, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation>, Void>() {    @Override
    public Void then(Task>> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " done");        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

其中asyncOperation1和asyncOperation2的定義如下

private Task asyncOperation1() {    return Task.callInBackground(new Callable() {        @Override
        public Void call() throws Exception {
            XLog.i("asyncOperation1 start");            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                XLog.e(e.getMessage());
            }

            XLog.i("asyncOperation1 end");            return null;
        }
    });
}private Task asyncOperation2() {    return Task.callInBackground(new Callable() {        @Override
        public Void call() throws Exception {
            XLog.i("asyncOperation2 start");            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                XLog.e(e.getMessage());
            }

            XLog.i("asyncOperation2 end");            return null;
        }
    });
}

後面的continueWith會等asyncOperation1和asyncOperation2都執行完成後再執行

列印結果如下

main calling
pool-2-thread-1 tasks start
asyncOperation1 start
asyncOperation2 start
asyncOperation1 end
asyncOperation2 end
pool-2-thread-1 tasks end
main done

有沒有小試牛刀的感覺? 我們接著來看下面的用法

Tasks in Parallel

final Executor PARALLEL_EXECUTOR = Executors.newCachedThreadPool();

Task.call(new Callable() {    @Override
    public Void call() throws Exception {        for (int i = 0; i () {    @Override
    public Void call() throws Exception {        for (int i = 0; i 

列印結果如下

pool-2-thread-1 i = 0
pool-2-thread-2 i = 50
pool-2-thread-1 i = 1
pool-2-thread-2 i = 51
pool-2-thread-1 i = 2
pool-2-thread-2 i = 52
pool-2-thread-1 i = 3
pool-2-thread-2 i = 53
pool-2-thread-1 i = 4
pool-2-thread-2 i = 54
pool-2-thread-1 i = 5
pool-2-thread-1 i = 6
pool-2-thread-2 i = 55
pool-2-thread-2 i = 56
pool-2-thread-1 i = 7
pool-2-thread-1 i = 8
pool-2-thread-2 i = 57
pool-2-thread-2 i = 58
pool-2-thread-1 i = 9
pool-2-thread-2 i = 59
pool-2-thread-1 i = 10
pool-2-thread-2 i = 60
pool-2-thread-1 i = 11
pool-2-thread-2 i = 61
pool-2-thread-1 i = 12
pool-2-thread-2 i = 62
pool-2-thread-1 i = 13
pool-2-thread-2 i = 63
pool-2-thread-1 i = 14
pool-2-thread-2 i = 64
pool-2-thread-1 i = 15
pool-2-thread-2 i = 65
pool-2-thread-1 i = 16
pool-2-thread-2 i = 66
pool-2-thread-1 i = 17
pool-2-thread-2 i = 67
pool-2-thread-1 i = 18
pool-2-thread-1 i = 19
pool-2-thread-2 i = 68
pool-2-thread-1 i = 20
pool-2-thread-2 i = 69
pool-2-thread-1 i = 21
pool-2-thread-2 i = 70
pool-2-thread-1 i = 22
pool-2-thread-2 i = 71
pool-2-thread-1 i = 23
pool-2-thread-2 i = 72
pool-2-thread-1 i = 24
pool-2-thread-2 i = 73
pool-2-thread-2 i = 74
pool-2-thread-1 i = 25
pool-2-thread-2 i = 75
pool-2-thread-1 i = 26
pool-2-thread-2 i = 76
pool-2-thread-1 i = 27
pool-2-thread-2 i = 77
pool-2-thread-1 i = 28
pool-2-thread-2 i = 78
pool-2-thread-1 i = 29
pool-2-thread-2 i = 79
pool-2-thread-1 i = 30
pool-2-thread-1 i = 31
pool-2-thread-2 i = 80
pool-2-thread-1 i = 32
pool-2-thread-2 i = 81
pool-2-thread-1 i = 33
pool-2-thread-2 i = 82
pool-2-thread-1 i = 34
pool-2-thread-2 i = 83
pool-2-thread-2 i = 84
pool-2-thread-1 i = 35
pool-2-thread-2 i = 85
pool-2-thread-1 i = 36
pool-2-thread-2 i = 86
pool-2-thread-1 i = 37
pool-2-thread-2 i = 87
pool-2-thread-1 i = 38
pool-2-thread-1 i = 39
pool-2-thread-2 i = 88
pool-2-thread-1 i = 40
pool-2-thread-2 i = 89
pool-2-thread-1 i = 41
pool-2-thread-2 i = 90
pool-2-thread-1 i = 42
pool-2-thread-2 i = 91
pool-2-thread-1 i = 43
pool-2-thread-2 i = 92
pool-2-thread-1 i = 44
pool-2-thread-2 i = 93
pool-2-thread-1 i = 45
pool-2-thread-2 i = 94
pool-2-thread-1 i = 46
pool-2-thread-2 i = 95
pool-2-thread-1 i = 47
pool-2-thread-2 i = 96
pool-2-thread-1 i = 48
pool-2-thread-2 i = 97
pool-2-thread-1 i = 49
pool-2-thread-2 i = 98
pool-2-thread-2 i = 99

列印結果有兩個發現

  • 執行緒池有兩個執行緒: pool-2-thread-1和pool-2-thread-2

  • 執行緒池裡的執行緒是並行執行的: 數字都是

Tasks in Serial

那有沒有方法可以讓執行緒池中只有一個執行緒, 所有Tasks按照queue的方式FIFO依次執行呢? 辦法如下

final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor();

Task.call(new Callable() {    @Override
    public Void call() throws Exception {        for (int i = 0; i () {    @Override
    public Void call() throws Exception {        for (int i = 50; i 

列印結果如下

pool-2-thread-1 i = 0
pool-2-thread-1 i = 1
pool-2-thread-1 i = 2
pool-2-thread-1 i = 3
pool-2-thread-1 i = 4
pool-2-thread-1 i = 5
pool-2-thread-1 i = 6
pool-2-thread-1 i = 7
pool-2-thread-1 i = 8
pool-2-thread-1 i = 9
pool-2-thread-1 i = 10
pool-2-thread-1 i = 11
pool-2-thread-1 i = 12
pool-2-thread-1 i = 13
pool-2-thread-1 i = 14
pool-2-thread-1 i = 15
pool-2-thread-1 i = 16
pool-2-thread-1 i = 17
pool-2-thread-1 i = 18
pool-2-thread-1 i = 19
pool-2-thread-1 i = 20
pool-2-thread-1 i = 21
pool-2-thread-1 i = 22
pool-2-thread-1 i = 23
pool-2-thread-1 i = 24
pool-2-thread-1 i = 25
pool-2-thread-1 i = 26
pool-2-thread-1 i = 27
pool-2-thread-1 i = 28
pool-2-thread-1 i = 29
pool-2-thread-1 i = 30
pool-2-thread-1 i = 31
pool-2-thread-1 i = 32
pool-2-thread-1 i = 33
pool-2-thread-1 i = 34
pool-2-thread-1 i = 35
pool-2-thread-1 i = 36
pool-2-thread-1 i = 37
pool-2-thread-1 i = 38
pool-2-thread-1 i = 39
pool-2-thread-1 i = 40
pool-2-thread-1 i = 41
pool-2-thread-1 i = 42
pool-2-thread-1 i = 43
pool-2-thread-1 i = 44
pool-2-thread-1 i = 45
pool-2-thread-1 i = 46
pool-2-thread-1 i = 47
pool-2-thread-1 i = 48
pool-2-thread-1 i = 49
pool-2-thread-1 i = 50
pool-2-thread-1 i = 51
pool-2-thread-1 i = 52
pool-2-thread-1 i = 53
pool-2-thread-1 i = 54
pool-2-thread-1 i = 55
pool-2-thread-1 i = 56
pool-2-thread-1 i = 57
pool-2-thread-1 i = 58
pool-2-thread-1 i = 59
pool-2-thread-1 i = 60
pool-2-thread-1 i = 61
pool-2-thread-1 i = 62
pool-2-thread-1 i = 63
pool-2-thread-1 i = 64
pool-2-thread-1 i = 65
pool-2-thread-1 i = 66
pool-2-thread-1 i = 67
pool-2-thread-1 i = 68
pool-2-thread-1 i = 69
pool-2-thread-1 i = 70
pool-2-thread-1 i = 71
pool-2-thread-1 i = 72
pool-2-thread-1 i = 73
pool-2-thread-1 i = 74
pool-2-thread-1 i = 75
pool-2-thread-1 i = 76
pool-2-thread-1 i = 77
pool-2-thread-1 i = 78
pool-2-thread-1 i = 79
pool-2-thread-1 i = 80
pool-2-thread-1 i = 81
pool-2-thread-1 i = 82
pool-2-thread-1 i = 83
pool-2-thread-1 i = 84
pool-2-thread-1 i = 85
pool-2-thread-1 i = 86
pool-2-thread-1 i = 87
pool-2-thread-1 i = 88
pool-2-thread-1 i = 89
pool-2-thread-1 i = 90
pool-2-thread-1 i = 91
pool-2-thread-1 i = 92
pool-2-thread-1 i = 93
pool-2-thread-1 i = 94
pool-2-thread-1 i = 95
pool-2-thread-1 i = 96
pool-2-thread-1 i = 97
pool-2-thread-1 i = 98
pool-2-thread-1 i = 99

Error Handling

看完上面的內容, 想必各位從此都是android多執行緒的"磚家"了吧, 不過請稍等片刻, 還有一個重要的問題沒有解釋

Task.call(new Callable() {    @Override
    public String call() throws Exception {
        Thread.sleep(2000);        if (1 () {    @Override
    public Void then(Task task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " onSuccess");        return null;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation() {    @Override
    public Void then(Task task) throws Exception {        if (task.isFaulted()) {
            XLog.e(Thread.currentThread().getName() + " error");            return null;
        }
        XLog.d(Thread.currentThread().getName() + " done");        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

請問這裡的列印結果是如何呢?

  • A: onSuccess

  • B: error

  • C: done

  • D: onSuccess & done

正確答案是B

小結

android多執行緒的方案還有諸如: , 不過經過實測, 和Bolts-Android比較起來, android-priority-jobqueue還是"略重量級"了一些, 如果你有更好的方案和實現, 歡迎留言和分享



作者:諾之林
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2144/viewspace-2802936/,如需轉載,請註明出處,否則將追究法律責任。

相關文章