目錄
多線程編程對比: iOS與Android
android的多線程編程有
Thread
Handler
AsyncTask
IntentService
android多線程詳細(xì)可以參考android學(xué)習(xí) 之 Service
iOS的多線程編程有
NSthread
GCD
NSOperationQueue
iOS多線程詳細(xì)可以參考Objective-C學(xué)習(xí) 之 GCD / iOS最佳實踐 之 優(yōu)先使用NSOperationQueue而不是GCD / iOS開發(fā) 之 Queue和Thread
對比兩個平臺的多線程編程, android還是稍遜一些
Handler和IntentService的多線程編程, 開銷有些大, 而GCD配合Block, 簡潔好用并且功能也很強(qiáng)大
AsyncTask處理消息隊列和多線程同步, 是比較費勁的, 而NSOperationQueue, 仍然是簡單好用五顆星
那有沒有一種方案可以讓android變得和iOS一樣簡單好用呢?
答案就是今天的主角BoltsFramework/Bolts-Android
Bolts-Android
Bolts-Android由Parse"榮譽(yù)"出品, 為什么說是"榮譽(yù)"呢?
因為作為Baas的鼻祖, Parse已經(jīng)成功地死在了沙灘上, 但是被Facebook關(guān)門之后, 它仍然發(fā)揮了余熱, Parse的很多項目都在Github Parse上開源了
廢話不多說, 直接看例子吧
Tasks
想要在當(dāng)前線程執(zhí)行Task, 就是這么簡單
Task.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
XLog.d(Thread.currentThread().getName() + " " + "call in current thread");
return true;
}
});
如果想在后臺線程執(zhí)行Task
Task.callInBackground(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
XLog.d(Thread.currentThread().getName() + " " + "call in background thread");
return true;
}
});
如果想在UI主線程執(zhí)行Task
Task.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
XLog.d(Thread.currentThread().getName() + " " + "call in main thread");
return true;
}
}, Task.UI_THREAD_EXECUTOR);
如果想要延時執(zhí)行Task
Task.delay(2000).continueWith(new Continuation<Void, Void>() {
@Override
public Void then(Task<Void> task) throws Exception {
XLog.d(Thread.currentThread().getName() + " " + "call in main thread (after delay 2 seconds)");
return null;
}
});
是不是簡單到讓人無法相信? 其實這里的Task和iOS中的GCD編程是類似的(詳細(xì)可以參考Objective-C學(xué)習(xí) 之 GCD)
Chaining Tasks
熱完身, 我們來看下Bolts-Android的函數(shù)式編程(關(guān)于函數(shù)式編程詳細(xì)可以參考談?wù)労瘮?shù)式響應(yīng)式編程(Functional Reactive Programming))
Task.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
XLog.d(Thread.currentThread().getName() + " calling");
return true;
}
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Boolean, String>() {
@Override
public String then(Task<Boolean> task) throws Exception {
Thread.sleep(2000);
XLog.d(Thread.currentThread().getName() + " onSuccess " + task.getResult());
return "hello bolts";
}
}, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation<String, Void>() {
@Override
public Void then(Task<String> task) throws Exception {
XLog.d(Thread.currentThread().getName() + " continueWith " + task.getResult());
return null;
}
}, Task.UI_THREAD_EXECUTOR);
打印結(jié)果如下
main calling
pool-2-thread-1 onSuccess true
main continueWith hello bolts
通過函數(shù)式的鏈?zhǔn)骄幊? 輕松地實現(xiàn)了Task的同步和多線程的切換(怎么忽然就想到了RxJava了呢? 看來下期可以來個Bolts-Android大戰(zhàn)RxJava的專題)
Group Tasks
Group Task是我取得名字哈, 所謂Group Tasks, 就是指多個Task組成的一組Tasks
Task.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
XLog.d(Thread.currentThread().getName() + " calling");
return true;
}
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Boolean, List<Task<Void>>>() {
@Override
public List<Task<Void>> then(Task<Boolean> task) throws Exception {
XLog.d(Thread.currentThread().getName() + " tasks start");
List<Task<Void>> tasks = new ArrayList<Task<Void>>();
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<List<Task<Void>>, Void>() {
@Override
public Void then(Task<List<Task<Void>>> task) throws Exception {
XLog.d(Thread.currentThread().getName() + " done");
return null;
}
}, Task.UI_THREAD_EXECUTOR);
其中asyncOperation1和asyncOperation2的定義如下
private Task<Void> asyncOperation1() {
return Task.callInBackground(new Callable<Void>() {
@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<Void> asyncOperation2() {
return Task.callInBackground(new Callable<Void>() {
@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都執(zhí)行完成后再執(zhí)行
打印結(jié)果如下
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<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
XLog.i(Thread.currentThread().getName() + " i = " + i);
}
}
return null;
}
}, PARALLEL_EXECUTOR);
Task.call(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < 100; i++) {
if (i % 2 == 1) {
XLog.i(Thread.currentThread().getName() + " i = " + i);
}
}
return null;
}
}, PARALLEL_EXECUTOR);
打印結(jié)果如下
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
打印結(jié)果有兩個發(fā)現(xiàn)
線程池有兩個線程: pool-2-thread-1和pool-2-thread-2
線程池里的線程是并行執(zhí)行的: 數(shù)字都是
Tasks in Serial
那有沒有方法可以讓線程池中只有一個線程, 所有Tasks按照queue的方式FIFO依次執(zhí)行呢? 辦法如下
final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor();
Task.call(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < 50; i++) {
XLog.i(Thread.currentThread().getName() + " i = " + i);
}
return null;
}
}, SERIAL_EXECUTOR);
call(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 50; i < 100; i++) {
XLog.i(Thread.currentThread().getName() + " i = " + i);
}
return null;
}
}, SERIAL_EXECUTOR);
打印結(jié)果如下
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
看完上面的內(nèi)容, 想必各位從此都是android多線程的"磚家"了吧, 不過請稍等片刻, 還有一個重要的問題沒有解釋
Task.call(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(2000);
if (1 < 10) {
XLog.d(Thread.currentThread().getName() + " RuntimeException");
throw new RuntimeException("There was an error.");
}
return "hello bolts";
}
}, Task.BACKGROUND_EXECUTOR).onSuccess(new Continuation<String, Void>() {
@Override
public Void then(Task<String> task) throws Exception {
XLog.d(Thread.currentThread().getName() + " onSuccess");
return null;
}
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Void, Void>() {
@Override
public Void then(Task<Void> 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);
請問這里的打印結(jié)果是如何呢?
A: onSuccess
B: error
C: done
D: onSuccess & done
正確答案是B
小結(jié)
android多線程的方案還有諸如: yigit/android-priority-jobqueue, 不過經(jīng)過實測, 和Bolts-Android比較起來, android-priority-jobqueue還是"略重量級"了一些, 如果你有更好的方案和實現(xiàn), 歡迎留言和分享
參考
更多文章, 請支持我的個人博客