Android Bolts中如何快速完成線程調(diào)度和任務(wù)管理,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
創(chuàng)新互聯(lián)是專業(yè)的興寧網(wǎng)站建設(shè)公司,興寧接單;提供成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行興寧網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊,希望更多企業(yè)前來合作!
使用 Bolts 可以將一個完整的操作拆分成多個子任務(wù),這些子任務(wù)可以自由的拆分、組合和替換,每個任務(wù)作為整個任務(wù)鏈的一環(huán)可以運行在指定線程中,同時既能從上行任務(wù)中獲取任務(wù)結(jié)果,又可以向下行任務(wù)發(fā)布當(dāng)前任務(wù)的結(jié)果,而不必考慮線程之間的交互。
Bolts-Android Bolts 在 Android 下的實現(xiàn) Bolts-ObjC Bolts 在 OC 下的實現(xiàn) Bolts-Swift Bolts 在 Swift 下的實現(xiàn)
前言
一個關(guān)于線程調(diào)度的簡單需求,在子線程從網(wǎng)絡(luò)下載圖片,并返回下載的圖片,在主線程使用該圖片更新到 UI,同時返回當(dāng)前 UI 的狀態(tài) json,在子線程將 json 數(shù)據(jù)保存到本地文件,完成后在主線程彈出提示,這中間涉及到了 4 次線程切換,同時后面的任務(wù)需要前面任務(wù)完成后的返回值作為參數(shù)。
使用 Thread + Handler 實現(xiàn),線程調(diào)度很不靈活,代碼可讀性差,不美觀,擴(kuò)展性差,錯誤處理異常麻煩。
String url = "http://www.baidu.com"; Handler handler = new Handler(Looper.getMainLooper()); new Thread(() -> { // 下載 Bitmap bitmap = downloadBitmap(url); handler.post(() -> { // 更新 UI String json = updateUI(bitmap); new Thread(() -> { // 向存儲寫入UI狀態(tài) saveUIState(json); // 保存成功后,提示 handler.post(() -> toastMsg("save finish.")); }).start(); }); }).start();
使用 RxJava 實現(xiàn),線程調(diào)度非常靈活,鏈?zhǔn)秸{(diào)用,代碼清晰,擴(kuò)展性好,有統(tǒng)一的異常處理機(jī)制,不過 Rx 是一個很強(qiáng)大的庫,如果只用來做線程調(diào)度的話, Rx 就顯得有點太重了。
Observable.just(URL) // 下載 .map(this::downloadBitmap) .subscribeOn(Schedulers.newThread()) // 更新UI .observeOn(AndroidSchedulers.mainThread()) .map(this::updateUI) // 存儲 UI 狀態(tài) .observeOn(Schedulers.io()) .map(this::saveUIState) // 顯示提示 .observeOn(AndroidSchedulers.mainThread()) .subscribe(rst -> toastMsg("save to " + rst), // handle error Throwable::printStackTrace);
使用 bolts 實現(xiàn),線程調(diào)度靈活,鏈?zhǔn)秸{(diào)用,代碼清晰,具有良好的擴(kuò)展性,具有統(tǒng)一的異常處理機(jī)制,雖然沒有 Rx 那么豐富的操作符,但是勝在類庫非常非常小,只有 38 KB。
Task .forResult(URL) // 下載 .onSuccess(task -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) // 更新UI .onSuccess(task -> updateUI(task.getResult()), Task.UI_THREAD_EXECUTOR) // 存儲UI狀態(tài) .onSuccess(task -> saveUIState(task.getResult()), Task.BACKGROUND_EXECUTOR) // 提示 .onSuccess(task -> toastMsg("save to " + task.getResult()), Task.UI_THREAD_EXECUT // handle error .continueWith(task -> { if (task.isFaulted()) { task.getError().printStackTrace(); return false; } return true; });
線程調(diào)度器
共有 4 種類型執(zhí)行線程,將任務(wù)分發(fā)到指定線程執(zhí)行,分別是
backgroud - 后臺線程池,可以并發(fā)執(zhí)行任務(wù)。
scheduled - 單線程池,只有一個線程,主要用來執(zhí)行 delay 操作。
immediate - 即時線程,如果線程調(diào)用棧小于 15,則在當(dāng)前線程執(zhí)行,否則代理給 background 。
uiThread - 針對 Android 設(shè)計,使用 Handler 發(fā)送到主線程執(zhí)行。
backgroud
主要用來在后臺并發(fā)執(zhí)行多任務(wù)
public static final ExecutorService BACKGROUND_EXECUTOR = BoltsExecutors.background();
在 Android 平臺下根據(jù) CPU 核數(shù)創(chuàng)建線程池,其他情況下,創(chuàng)建緩存線程池。
background = !isAndroidRuntime() ? java.util.concurrent.Executors.newCachedThreadPool() : AndroidExecutors.newCachedThreadPool();
scheduled
主要用于任務(wù)之間做 delay 操作,并不實際執(zhí)行任務(wù)。
scheduled = Executors.newSingleThreadScheduledExecutor();
immediate
主要用來簡化那些不指定運行線程的方法,默認(rèn)在當(dāng)前線程去執(zhí)行任務(wù),使用 ThreadLocal 保存每個線程調(diào)用棧的深度,如果深度不超過 15,則在當(dāng)前線程執(zhí)行,否則代理給 backgroud 執(zhí)行。
private static final Executor IMMEDIATE_EXECUTOR = BoltsExecutors.immediate(); // 關(guān)鍵方法 @Override public void execute(Runnable command) { int depth = incrementDepth(); try { if (depth <= MAX_DEPTH) { command.run(); } else { BoltsExecutors.background().execute(command) } } finally { decrementDepth(); } }
uiThread
為 Android 專門設(shè)計,在主線程執(zhí)行任務(wù)。
public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread();
private static class UIThreadExecutor implements Executor { @Override public void execute(Runnable command) { new Handler(Looper.getMainLooper()).post(command); } }
核心類
Task ,最核心的類,每個子任務(wù)都是一個 Task ,它們負(fù)責(zé)自己需要執(zhí)行的任務(wù)。每個 Task 具有 3 種狀態(tài) Result 、 Error 和 Cancel ,分別代表成功、異常和取消。
Continuation ,是一個接口,它就像鏈接子任務(wù)每一環(huán)的鎖扣,把一個個獨立的任務(wù)鏈接在一起。
通過 Task - Continuation - Task - Continuation ... 的形式組成完整的任務(wù)鏈,順序在各自線程執(zhí)行。
創(chuàng)建 Task
根據(jù) Task 的 3 種狀態(tài),創(chuàng)建簡單的 Task ,會復(fù)用已有的任務(wù)對象
public staticTask forResult(TResult value) public static Task forError(Exception error) public static Task cancelled()
使用 delay 方法,延時執(zhí)行并創(chuàng)建 Task
public static Task delay(long delay) public static Task delay(long delay, CancellationToken cancellationToken)
使用 whenAny 方法,執(zhí)行多個任務(wù),當(dāng)任意任務(wù)返回結(jié)果時,保存這個結(jié)果
public static Task> whenAnyResult(Collection> tasks) public static Task> whenAny(Collection> tasks)
使用 whenAll 方法,執(zhí)行多個任務(wù),當(dāng)全部任務(wù)執(zhí)行完后,返回結(jié)果
public static Task whenAll(Collection> tasks) public static Task> whenAllResult(final Collection> tasks)
使用 call 方法,執(zhí)行一個任務(wù),同時創(chuàng)建 Task
public static Task call(final Callable callable, Executor executor, final CancellationToken ct)
鏈接子任務(wù)
使用 continueWith 方法,鏈接一個子任務(wù),如果前行任務(wù)已經(jīng)執(zhí)行完成,則立即執(zhí)行當(dāng)前任務(wù),否則加入隊列中,等待。
publicTask continueWith( final Continuation continuation, final Executor executor, final CancellationToken ct)
使用 continueWithTask 方法,在當(dāng)前任務(wù)之后鏈接另一個任務(wù)鏈,這種做法是為了滿足那種將部分任務(wù)組合在一起分離出去,作為公共任務(wù)的場景,他接受將另外一個完全獨立的任務(wù)鏈,追加在當(dāng)前執(zhí)行的任務(wù)后面。
publicTask continueWithTask( final Continuation > continuation, final Executor executor, final CancellationToken ct)
使用 continueWhile 方法鏈接子任務(wù),與 continueWith 區(qū)別在于,他有一個 predicate 表達(dá)式,只有當(dāng)表達(dá)式成立時,才會追加子任務(wù),這樣做是在執(zhí)行任務(wù)前可以做一個攔截操作,也是為了不破環(huán)鏈?zhǔn)秸{(diào)用的整體風(fēng)格。
public TaskcontinueWhile(final Callable predicate, final Continuation > continuation, final Executor executor, final CancellationToken ct)
使用 onSuccess 和 onSuccessTask 鏈接單個任務(wù)個任務(wù)鏈,區(qū)別于 continueWith 在于, onSuccess 方法,前行任務(wù)如果失敗了,后行的任務(wù)也會直接失敗,不會再執(zhí)行,但是 continueWith 的各個子任務(wù)之間沒有關(guān)聯(lián),就算前行任務(wù)失敗,后行任務(wù)也會執(zhí)行。
publicTask onSuccess( final Continuation continuation, Executor executor, final CancellationToken ct)
取消任務(wù)
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); CancellationToken token = cancellationTokenSource.getToken(); Task.call((Callable) () -> null, Task.BACKGROUND_EXECUTOR, token); // 取消任務(wù) cancellationTokenSource.cancel();
異常的處理
關(guān)于異常的處理,整個機(jī)制下來,每個任務(wù)作為一個獨立的單位,異常會被統(tǒng)一捕捉,因此不必針對任務(wù)中的方法進(jìn)行單獨的處理。
如果使用了 continueWith 鏈接任務(wù),那么當(dāng)前任務(wù)的的異常信息,將會保存在當(dāng)前 Task 中在下行任務(wù)中進(jìn)行處理,下行任務(wù)也可以不處理這個異常,直接執(zhí)行任務(wù),那么這個異常就到這里停止了,不會再向下傳遞,也就是說,只有下行任務(wù)才知道當(dāng)前任務(wù)的結(jié)果,不管是成功還是異常。
當(dāng)然了,如果任務(wù)之間有關(guān)聯(lián),由于上行任務(wù)的異常極大可能造成當(dāng)前任務(wù)的異常,那么當(dāng)前任務(wù)異常的信息,又會向下傳遞,但是上行任務(wù)的異常就到這里為止了。
如果使用 onSuccess 之類的方法,如果上行任務(wù)異常了,那么下行任務(wù)根本不會執(zhí)行,而是直接將異常往下面?zhèn)鬟f,直到被處理掉。
任務(wù)的分離和組合
我們可以將一個完整的操作細(xì)分成多個任務(wù),每個任務(wù)都遵循單一職責(zé)的原則而盡量簡單,這樣可以在任務(wù)之間再穿插新的任務(wù),或者將部分任務(wù)分離出來組合到一起等。
擴(kuò)展性
我們可以在兩個細(xì)分的任務(wù)之間添加一個新的操作,而不影響上行和下行任務(wù),如我們給文章開頭的需求中更新 UI 之前,將 Bitmap 先保存到本地。
Task .forResult(URL) // 下載 .onSuccess(task -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) // 保存在本地 .onSuccess(task -> saveBitmapToFile(task.getResult()),Task.BACKGROUND_EXECUTOR) // 更新UI .onSuccess(task -> updateUI(task.getResult()), Task.UI_THREAD_EXECUTOR) ...
復(fù)用性
對一些公共的操作,可以單獨分離成新的任務(wù),當(dāng)需要做類似操作時,即可復(fù)用這部份功能,如可以將 下載圖片并更新 UI 、 保存狀態(tài)并彈出提示 兩塊功能分離出來,作為公共的任務(wù)。
// 下載圖片->更新UI public Continuation> downloadImageAndUpdateUI() { return task -> Task.call(() -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) .continueWith(taskWithBitmap -> updateUI(taskWithBitmap.getResult()), Task.UI_THREAD_EXECUTOR); } // 保存狀態(tài)->提示信息 public Continuation > saveStateAndToast() { return task -> Task.call(() -> saveUIState(task.getResult()), Task.BACKGROUND_EXECUTOR) .continueWith(taskWithPath -> toastMsg("save to " + taskWithPath.getResult())); }
使用分離的任務(wù)
Task .forResult(URL) .continueWithTask(downloadImageAndUpdateUI()) .continueWithTask(saveStateAndToast()) ...
在 Task 中有一個 continuations 是當(dāng)前任務(wù)后面追加的任務(wù)列表,當(dāng)當(dāng)前任務(wù)成功、異?;蛘呷∠麜r,會去執(zhí)行列表中的后續(xù)任務(wù)。
通常情況下,我們使用鏈?zhǔn)秸{(diào)用構(gòu)建任務(wù)鏈,結(jié)果就是一條沒有分支的任務(wù)鏈。
添加任務(wù)時:每次添加一個 Continuation ,就會生成一個 Task ,加到上行任務(wù)的 continuations 列表中,等待執(zhí)行,同時返回當(dāng)前的 Task ,以便后面的任務(wù)可以鏈接到當(dāng)前任務(wù)后面。
執(zhí)行任務(wù)時:當(dāng)前任務(wù)執(zhí)行完之后,結(jié)果可能有 3 種,都會被保存到當(dāng)前的 Task 中,然后檢查 continuations 列表中的后續(xù)任務(wù),而當(dāng)前的 Task 就會作為參數(shù),傳遞到后續(xù)鏈接的任務(wù)中,來讓后面的任務(wù)得知上行任務(wù)的結(jié)果。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。