日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Android Bolts-更簡單的完成線程調度和任務管理

尤塞恩·圣利奧·博爾特 Usain St Leo Bolt ,牙買加短跑運動員,男子100米、男子200米以及男子400米接力賽的世界紀錄保持人,同時是以上三項賽事的連續(xù)三屆奧運金牌得主。

創(chuàng)新互聯(lián)公司專注于肥西網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經驗。 熱誠為您提供肥西營銷型網(wǎng)站建設,肥西網(wǎng)站制作、肥西網(wǎng)頁設計、肥西網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務,打造肥西網(wǎng)絡公司原創(chuàng)品牌,更為您提供肥西網(wǎng)站排名全網(wǎng)營銷落地服務。

使用 Bolts 可以將一個完整的操作拆分成多個子任務,這些子任務可以自由的拆分、組合和替換,每個任務作為整個任務鏈的一環(huán)可以運行在指定線程中,同時既能從上行任務中獲取任務結果,又可以向下行任務發(fā)布當前任務的結果,而不必考慮線程之間的交互。

Bolts-Android Bolts 在 Android 下的實現(xiàn) Bolts-ObjC Bolts 在 OC 下的實現(xiàn) Bolts-Swift Bolts 在 Swift 下的實現(xiàn)

前言

一個關于線程調度的簡單需求,在子線程從網(wǎng)絡下載圖片,并返回下載的圖片,在主線程使用該圖片更新到 UI,同時返回當前 UI 的狀態(tài) json,在子線程將 json 數(shù)據(jù)保存到本地文件,完成后在主線程彈出提示,這中間涉及到了 4 次線程切換,同時后面的任務需要前面任務完成后的返回值作為參數(shù)。

使用 Thread + Handler 實現(xiàn),線程調度很不靈活,代碼可讀性差,不美觀,擴展性差,錯誤處理異常麻煩。

 
 
 
  1. String url = "http://www.baidu.com"; 
  2. Handler handler = new Handler(Looper.getMainLooper()); 
  3. new Thread(() -> { 
  4.     // 下載 
  5.     Bitmap bitmap = downloadBitmap(url); 
  6.     handler.post(() -> { 
  7.         // 更新 UI 
  8.         String json = updateUI(bitmap); 
  9.         new Thread(() -> { 
  10.             // 向存儲寫入UI狀態(tài) 
  11.             saveUIState(json); 
  12.             // 保存成功后,提示 
  13.             handler.post(() -> toastMsg("save finish.")); 
  14.         }).start(); 
  15.     }); 
  16. }).start(); 

使用 RxJava 實現(xiàn),線程調度非常靈活,鏈式調用,代碼清晰,擴展性好,有統(tǒng)一的異常處理機制,不過 Rx 是一個很強大的庫,如果只用來做線程調度的話, Rx 就顯得有點太重了。

 
 
 
  1. Observable.just(URL) 
  2.         // 下載 
  3.         .map(this::downloadBitmap) 
  4.         .subscribeOn(Schedulers.newThread()) 
  5.         // 更新UI 
  6.         .observeOn(AndroidSchedulers.mainThread()) 
  7.         .map(this::updateUI) 
  8.         // 存儲 UI 狀態(tài) 
  9.         .observeOn(Schedulers.io()) 
  10.         .map(this::saveUIState) 
  11.         // 顯示提示 
  12.         .observeOn(AndroidSchedulers.mainThread()) 
  13.         .subscribe(rst -> toastMsg("save to " + rst), 
  14.                 // handle error 
  15.                 Throwable::printStackTrace); 

使用 bolts 實現(xiàn),線程調度靈活,鏈式調用,代碼清晰,具有良好的擴展性,具有統(tǒng)一的異常處理機制,雖然沒有 Rx 那么豐富的操作符,但是勝在類庫非常非常小,只有 38 KB。

 
 
 
  1. Task 
  2.         .forResult(URL) 
  3.         // 下載 
  4.         .onSuccess(task -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) 
  5.         // 更新UI 
  6.         .onSuccess(task -> updateUI(task.getResult()), Task.UI_THREAD_EXECUTOR) 
  7.         // 存儲UI狀態(tài) 
  8.         .onSuccess(task -> saveUIState(task.getResult()), Task.BACKGROUND_EXECUTOR) 
  9.         // 提示 
  10.         .onSuccess(task -> toastMsg("save to " + task.getResult()), Task.UI_THREAD_EXECUT 
  11.         // handle error 
  12.         .continueWith(task -> { 
  13.             if (task.isFaulted()) { 
  14.                 task.getError().printStackTrace(); 
  15.                 return false; 
  16.             } 
  17.             return true; 
  18.         }); 

線程調度器

共有 4 種類型執(zhí)行線程,將任務分發(fā)到指定線程執(zhí)行,分別是

  1. backgroud - 后臺線程池,可以并發(fā)執(zhí)行任務。
  2. scheduled - 單線程池,只有一個線程,主要用來執(zhí)行 delay 操作。
  3. immediate - 即時線程,如果線程調用棧小于 15,則在當前線程執(zhí)行,否則代理給 background 。
  4. uiThread - 針對 Android 設計,使用 Handler 發(fā)送到主線程執(zhí)行。

backgroud

主要用來在后臺并發(fā)執(zhí)行多任務

 
 
 
  1. public static final ExecutorService BACKGROUND_EXECUTOR = BoltsExecutors.background(); 

在 Android 平臺下根據(jù) CPU 核數(shù)創(chuàng)建線程池,其他情況下,創(chuàng)建緩存線程池。

 
 
 
  1. background = !isAndroidRuntime() 
  2.     ? java.util.concurrent.Executors.newCachedThreadPool() 
  3.     : AndroidExecutors.newCachedThreadPool(); 

scheduled

主要用于任務之間做 delay 操作,并不實際執(zhí)行任務。

 
 
 
  1. scheduled = Executors.newSingleThreadScheduledExecutor(); 

immediate

主要用來簡化那些不指定運行線程的方法,默認在當前線程去執(zhí)行任務,使用 ThreadLocal 保存每個線程調用棧的深度,如果深度不超過 15,則在當前線程執(zhí)行,否則代理給 backgroud 執(zhí)行。

 
 
 
  1. private static final Executor IMMEDIATE_EXECUTOR = BoltsExecutors.immediate(); 
  2.  
  3. // 關鍵方法 
  4. @Override 
  5. public void execute(Runnable command) { 
  6.   int depth = incrementDepth(); 
  7.   try { 
  8.     if (depth <= MAX_DEPTH) { 
  9.       command.run(); 
  10.     } else { 
  11.       BoltsExecutors.background().execute(command) 
  12.     } 
  13.   } finally { 
  14.     decrementDepth(); 
  15.   } 

uiThread

為 Android 專門設計,在主線程執(zhí)行任務。

 
 
 
  1. public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread(); 

 
 
 
  1. private static class UIThreadExecutor implements Executor { 
  2.   @Override 
  3.   public void execute(Runnable command) { 
  4.     new Handler(Looper.getMainLooper()).post(command); 
  5.   } 

核心類

Task ,最核心的類,每個子任務都是一個 Task ,它們負責自己需要執(zhí)行的任務。每個 Task 具有 3 種狀態(tài) Result 、 Error 和 Cancel ,分別代表成功、異常和取消。

Continuation ,是一個接口,它就像鏈接子任務每一環(huán)的鎖扣,把一個個獨立的任務鏈接在一起。

通過 Task - Continuation - Task - Continuation ... 的形式組成完整的任務鏈,順序在各自線程執(zhí)行。

創(chuàng)建 Task

根據(jù) Task 的 3 種狀態(tài),創(chuàng)建簡單的 Task ,會復用已有的任務對象

 
 
 
  1. public static  Task forResult(TResult value)  
  2. public static  Task forError(Exception error)  
  3. public static  Task cancelled() 

使用 delay 方法,延時執(zhí)行并創(chuàng)建 Task

 
 
 
  1. public static Task delay(long delay)  
  2. public static Task delay(long delay, CancellationToken cancellationToken) 

使用 whenAny 方法,執(zhí)行多個任務,當任意任務返回結果時,保存這個結果

 
 
 
  1. public static Task> whenAnyResult(Collection> tasks)  
  2. public static Task> whenAny(Collection> tasks) 

使用 whenAll 方法,執(zhí)行多個任務,當全部任務執(zhí)行完后,返回結果

 
 
 
  1. public static Task whenAll(Collection> tasks)  
  2. public static Task> whenAllResult(final Collection> tasks) 

使用 call 方法,執(zhí)行一個任務,同時創(chuàng)建 Task

 
 
 
  1. public static Task call(final Callable callable, Executor executor,  
  2. final CancellationToken ct) 

鏈接子任務

使用 continueWith 方法,鏈接一個子任務,如果前行任務已經執(zhí)行完成,則立即執(zhí)行當前任務,否則加入隊列中,等待。

 
 
 
  1. public  Task continueWith( 
  2.       final Continuation continuation, final Executor executor, 
  3.       final CancellationToken ct) 

使用 continueWithTask 方法,在當前任務之后鏈接另一個任務鏈,這種做法是為了滿足那種將部分任務組合在一起分離出去,作為公共任務的場景,他接受將另外一個完全獨立的任務鏈,追加在當前執(zhí)行的任務后面。

 
 
 
  1. public  Task continueWithTask( 
  2.       final Continuation> continuation, final Executor executor, 
  3.       final CancellationToken ct) 

使用 continueWhile 方法鏈接子任務,與 continueWith 區(qū)別在于,他有一個 predicate 表達式,只有當表達式成立時,才會追加子任務,這樣做是在執(zhí)行任務前可以做一個攔截操作,也是為了不破環(huán)鏈式調用的整體風格。

 
 
 
  1. public Task continueWhile(final Callable predicate, 
  2.       final Continuation> continuation, final Executor executor, 
  3.       final CancellationToken ct) 

使用 onSuccess 和 onSuccessTask 鏈接單個任務個任務鏈,區(qū)別于 continueWith 在于, onSuccess 方法,前行任務如果失敗了,后行的任務也會直接失敗,不會再執(zhí)行,但是 continueWith 的各個子任務之間沒有關聯(lián),就算前行任務失敗,后行任務也會執(zhí)行。

 
 
 
  1. public  Task onSuccess( 
  2.       final Continuation continuation, Executor executor, 
  3.       final CancellationToken ct) 

取消任務

 
 
 
  1. CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 
  2. CancellationToken token =   cancellationTokenSource.getToken(); 
  3. Task.call((Callable) () -> null, 
  4.         Task.BACKGROUND_EXECUTOR, 
  5.         token); 
  6. // 取消任務 
  7. cancellationTokenSource.cancel(); 

異常的處理

關于異常的處理,整個機制下來,每個任務作為一個獨立的單位,異常會被統(tǒng)一捕捉,因此不必針對任務中的方法進行單獨的處理。

如果使用了 continueWith 鏈接任務,那么當前任務的的異常信息,將會保存在當前 Task 中在下行任務中進行處理,下行任務也可以不處理這個異常,直接執(zhí)行任務,那么這個異常就到這里停止了,不會再向下傳遞,也就是說,只有下行任務才知道當前任務的結果,不管是成功還是異常。

當然了,如果任務之間有關聯(lián),由于上行任務的異常極大可能造成當前任務的異常,那么當前任務異常的信息,又會向下傳遞,但是上行任務的異常就到這里為止了。

如果使用 onSuccess 之類的方法,如果上行任務異常了,那么下行任務根本不會執(zhí)行,而是直接將異常往下面?zhèn)鬟f,直到被處理掉。

任務的分離和組合

我們可以將一個完整的操作細分成多個任務,每個任務都遵循單一職責的原則而盡量簡單,這樣可以在任務之間再穿插新的任務,或者將部分任務分離出來組合到一起等。

擴展性

我們可以在兩個細分的任務之間添加一個新的操作,而不影響上行和下行任務,如我們給文章開頭的需求中更新 UI 之前,將 Bitmap 先保存到本地。

 
 
 
  1. Task 
  2.         .forResult(URL) 
  3.         // 下載 
  4.         .onSuccess(task -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) 
  5.         // 保存在本地 
  6.         .onSuccess(task -> saveBitmapToFile(task.getResult()),Task.BACKGROUND_EXECUTOR) 
  7.         // 更新UI 
  8.         .onSuccess(task -> updateUI(task.getResult()), Task.UI_THREAD_EXECUTOR) 
  9.         ... 

復用性

對一些公共的操作,可以單獨分離成新的任務,當需要做類似操作時,即可復用這部份功能,如可以將 下載圖片并更新 UI 、 保存狀態(tài)并彈出提示 兩塊功能分離出來,作為公共的任務。

 
 
 
  1. // 下載圖片->更新UI 
  2. public Continuation> downloadImageAndUpdateUI() { 
  3.     return task -> 
  4.             Task.call(() -> downloadBitmap(task.getResult()), Task.BACKGROUND_EXECUTOR) 
  5.                     .continueWith(taskWithBitmap -> updateUI(taskWithBitmap.getResult()), Task.UI_THREAD_EXECUTOR); 
  6.  
  7. // 保存狀態(tài)->提示信息 
  8. public Continuation> saveStateAndToast() { 
  9.     return task -> 
  10.             Task.call(() -> saveUIState(task.getResult()), Task.BACKGROUND_EXECUTOR) 
  11.                     .continueWith(taskWithPath -> toastMsg("save to " + taskWithPath.getResult())); 

使用分離的任務

 
 
 
  1. Task 
  2.         .forResult(URL) 
  3.         .continueWithTask(downloadImageAndUpdateUI()) 
  4.         .continueWithTask(saveStateAndToast()) 
  5.         ... 

總結

在 Task 中有一個 continuations 是當前任務后面追加的任務列表,當當前任務成功、異常或者取消時,會去執(zhí)行列表中的后續(xù)任務。

通常情況下,我們使用鏈式調用構建任務鏈,結果就是一條沒有分支的任務鏈。

添加任務時:每次添加一個 Continuation ,就會生成一個 Task ,加到上行任務的 continuations 列表中,等待執(zhí)行,同時返回當前的 Task ,以便后面的任務可以鏈接到當前任務后面。

執(zhí)行任務時:當前任務執(zhí)行完之后,結果可能有 3 種,都會被保存到當前的 Task 中,然后檢查 continuations 列表中的后續(xù)任務,而當前的 Task 就會作為參數(shù),傳遞到后續(xù)鏈接的任務中,來讓后面的任務得知上行任務的結果。


新聞名稱:Android Bolts-更簡單的完成線程調度和任務管理
文章位置:http://www.5511xx.com/article/cojisde.html