編輯:關於Android編程
bolts簡介及使用教程:
bolts是Parse被FaceBook收購後開源的一個工具庫組合,
Parse聲稱,與Android" target="_blank">Android AsyncTask和iOS NSOperation相比,Tasks有若干優勢,其中包括:
l連續執行數個任務不會像只使用回調函數時那樣創建嵌套的“金字塔(pyramid)”代碼。
lTasks是完全可組合的,允許開發人員執行分支、並行和復雜的錯誤處理。
l開發人員可以按照執行順序安排基於任務的代碼,而不必將邏輯分解到分散的回調函數中。
github地址 https://github.com/BoltsFramework/Bolts-Android
疑似官方解釋:http://gold.xitu.io/entry/55cdcca040ac79db35731770
我們先來看一下如何使用
gradle下加入
dependencies { compile 'com.parse.bolts:bolts-android:1.2.0' }
代碼裡面使用:
Task.call(new Callable() { @Override public Boolean call() throws Exception { // 處理一些具體的動作 return false; } }, Task.BACKGROUND_EXECUTOR);
第一個參數是一個Callable,conCurrent包的,常與線程池、Future搭配使用。
這裡有兩個線程可以供我們使用Task.UI_THREAD_EXECUTOR、Task.BACKGROUND_EXECUTOR,不傳這個參數的話默認為Task.IMMEDIATE_EXECUTOR,也就是在當前線程下執行。
如果只是這麼簡單的話我們用個線程池也可以,沒必要搞這麼多,下面我們來看看進階用法,這個call方法會返回生成的Task對象,我們可以繼續對這個對象進行處理
Task.continueWith(new Continuation() { @Override public String then(Task task) throws Exception { return null; } })
這個api可謂是簡單易懂,無論這個Task成功或者失敗,這個Continuation都能接受到結果並進行你的處理,最重要的是,會返回一個新的Task,你可以在後面再接一個continueWith();然後實現無限續杯,這比RxJava的使用map來轉換不知道高到哪裡去了。
當然,還要學習一個:
task.onSuccess(new Continuation() { @Override public Object then(Task task) throws Exception { return null; } });
這個onSuccess方法只有Task被成功執行並且沒有報錯的情況下才會被調用,也就是說,Task內部幫我們處理了(其實只是簡單的把報錯信息封裝起來)運行時可能出現的各種錯誤,這樣我們就只會在一切正常的情況下調用我們的代碼,以防出現各種奇怪的錯誤。
還要再學習一個:
task.continueWhile(new Callable() { @Override public Boolean call() throws Exception { return null; } }, new Continuation >() { @Override public Task then(Task task) throws Exception { return null; } });
continueWhile這個方法需要兩個參數,一個是Callable,要求返回Boolean值,表示你的任務是否被成功執行,如果成功執行,會調用接下來的另一個參數Continuation。
接下來我們要搞個大新聞:
task.continueWithTask(new Continuation>() { @Override public Task then(Task task) throws Exception { return null; } });
continueWithTask這個方法用起來就麻煩啦,使用的時候泛型參數一定要返回一個Task對象,否則內部會報錯,至於為什麼我們等下源碼分析再說。
那這個方法干什麼用的呢?你調用continueWithTask 返回的Task_A會在Continuation裡面你自己返回的Task_B完成之後完成,這個方法解決了金字塔嵌套問題。
以上這四個方法都可以選擇線程Task.UI_THREAD_EXECUTOR、Task.BACKGROUND_EXECUTOR,不傳這個參數的話默認為Task.IMMEDIATE_EXECUTOR。
那麼接下來我們來講一下bolts的源代碼
在開始之前要提一下CancellationToken,這個參數,目前來說這個參數並沒有用上,我們也不可以在包外訪問這個類,在bolts內部是傳null進行參數填充的,並且在源碼中也不會被用到,可能會在後面的版本更新中派上用場,以下內容不會對該類再進行解釋,也不會提及指定了該參數的傳參方法。
從Task.Call開始講吧
public static Task call(final Callable callable) { return call(callable, IMMEDIATE_EXECUTOR, null); } public static Task call(final Callable callable, Executor executor){ return call(callable, executor, null); } public static Task callInBackground(Callable callable) { return call(callable, BACKGROUND_EXECUTOR, null); } //上面這三個方法都會調用到下面這個,我們只需要看這個就好了 public static Task call(final Callable callable, Executor executor, final CancellationToken ct) { //這個TaskCompletionSource是一個包裝類,構造方法內實例化了一個Task,裡面所有//的setXXXX方法都會調用到所持有的Task本身的trySetXXXX方法,這種trySet方法//會返回一個boolean值表示是否執行成功,如果沒有成功,TaskCompletionSource會拋//出一個IllegalStateException異常。 final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); try { executor.execute(new Runnable() { @Override public void run() { //這裡ct 必定為null,不用管 if (ct != null && ct.isCancellationRequested()) { tcs.setCancelled(); return; } try { tcs.setResult(callable.call()); } catch (CancellationException e) { tcs.setCancelled(); } catch (Exception e) { tcs.setError(e); } } }); } catch (Exception e) { tcs.setError(new ExecutorException(e)); } //返回內部task實例 return tcs.getTask(); }
通過指定的Exeutor在特定線程執行一個Runnable,並調用我們所傳進來的callable的call方法,並返回已經設置了結果的task。
那麼我們來看看設置結果的方法,這三個方法都不能從包外部訪問:
/** * Sets the cancelled flag on the Task if the Task hasn't already been completed. */ /* package */ boolean trySetCancelled() { synchronized (lock) { if (complete) { return false; } complete = true; cancelled = true; lock.notifyAll(); runContinuations(); return true; } } /** * Sets the result on the Task if the Task hasn't already been completed. */ /* package */ boolean trySetResult(TResult result) { synchronized (lock) { if (complete) { return false; } complete = true; Task.this.result = result; lock.notifyAll(); runContinuations(); return true; } } /** * Sets the error on the Task if the Task hasn't already been completed. */ /* package */ boolean trySetError(Exception error) { synchronized (lock) { if (complete) { return false; } complete = true; Task.this.error = error; errorHasBeenObserved = false; lock.notifyAll(); runContinuations(); //這個UnobservedExceptionHandler是我們自己設置的異常處理器,沒設就不用管下面這一句了 if (!errorHasBeenObserved && getUnobservedExceptionHandler() != null) unobservedErrorNotifier = new UnobservedErrorNotifier(this); return true; } }
上面這三個方法就是剛剛說過的trySetXXXX系列方法,基本上都是大同小異,首先判斷任務是否已經結束,false就讓TaskCompletionSource拋異常,true則設置complete 狀態,並且解開lock對象鎖,設置結果還是設置異常信息就不解釋了。
我們來看一下這個共同調用的runContinuations()方法
private void runContinuations() { //對象鎖 synchronized (lock) { for (Continuation continuation : continuations) { try { //這裡傳入task continuation.then(this); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } continuations = null; } }
上面所遍歷的continuations是一個ArrayList對象,當我們調用continueWith等方法時會往裡面塞Continuation,這個也沒什麼好說的。
我們繼續說下一個
Task.continueWith publicTask continueWith( Continuation continuation) { return continueWith(continuation, IMMEDIATE_EXECUTOR, null); } public Task continueWith( final Continuation continuation, final Executor executor) { return continueWith(continuation, executor, null); } //上面兩個會調用下面這個 public Task continueWith( final Continuation continuation, final Executor executor, final CancellationToken ct) { boolean completed; final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); //同步鎖 synchronized (lock) { completed = this.isCompleted(); if (!completed) { //往列表裡面加東西,當任務完成時遍歷此列表並調用then()方法 this.continuations.add(new Continuation() { @Override public Void then(Task task) { completeImmediately(tcs, continuation, task, executor, ct); return null; } }); } } if (completed) { completeImmediately(tcs, continuation, this, executor, ct); } return tcs.getTask(); }
上面判斷了一下任務是否已經完成,視情況調用completeImmediately()方法
private staticvoid completeImmediately( final bolts.TaskCompletionSource tcs, final Continuation continuation, final Task task, Executor executor, final CancellationToken ct) { try { executor.execute(new Runnable() { @Override public void run() { //這裡ct 必定為null,不用管 if (ct != null && ct.isCancellationRequested()) { tcs.setCancelled(); return; } try { //這裡跟上面Call()方法中調用callable.call()差不多 TContinuationResult result = continuation.then(task); tcs.setResult(result); } catch (CancellationException e) { tcs.setCancelled(); } catch (Exception e) { tcs.setError(e); } } }); } catch (Exception e) { tcs.setError(new ExecutorException(e)); } }
這個方法裡面給傳進來的tcs設置了一個結果,然後在continueWith()方法的最後取出該tcs的task實例返回給調用者。
接下來這個就有點麻煩了
continueWithTask:
publicTask continueWithTask( Continuation> continuation) { return continueWithTask(continuation, IMMEDIATE_EXECUTOR, null); } public Task continueWithTask( final Continuation> continuation, final Executor executor) { return continueWithTask(continuation, executor, null); } public Task continueWithTask( final Continuation> continuation, final Executor executor, final CancellationToken ct) { boolean completed; final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); synchronized (lock) { completed = this.isCompleted(); if (!completed) { this.continuations.add(new Continuation() { @Override public Void then(Task task) { completeAfterTask(tcs, continuation, task, executor, ct); return null; } }); } } if (completed) { completeAfterTask(tcs, continuation, this, executor, ct); } return tcs.getTask(); }
這個跟上面提到的continueWith結構相同,只是最後調用的complete方法有所差別
我們先來解釋一下這個思路:
我們調用Task.call()獲得一個taskA,然後調用taskA.continueWithTask()獲得返回的taskB,然後再continueWithTask方法中我們會傳進去一個返回結果為Task的taskC對象,taskB會在taskC完成之後再setResult
private staticvoid completeAfterTask( final bolts.TaskCompletionSource tcs, final Continuation> continuation, final Task task, final Executor executor, final CancellationToken ct) { try { executor.execute(new Runnable() { @Override public void run() { //這裡ct 必定為null,不用管 if (ct != null && ct.isCancellationRequested()) { tcs.setCancelled(); return; } try { //這裡這個result就是我們說的taskC,tcs裡面的Task實例就是taskB,並且會返回taskB給調用者 Task result = continuation.then(task); if (result == null) { tcs.setResult(null); } else { result.continueWith(new Continuation () { @Override public Void then(Task task) { if (ct != null && ct.isCancellationRequested()) { tcs.setCancelled(); return null; } if (task.isCancelled()) { tcs.setCancelled(); } else if (task.isFaulted()) { tcs.setError(task.getError()); } else { tcs.setResult(task.getResult()); } return null; } }); } } catch (CancellationException e) { tcs.setCancelled(); } catch (Exception e) { tcs.setError(e); } } }); } catch (Exception e) { tcs.setError(new ExecutorException(e)); } }
除了上面注釋所說的還有一點要注意的是,taskB會將taskC的結果作為自己的結果,也就是多嵌套了一層而已。
onSuccessTask
publicTask onSuccessTask( final Continuation> continuation) { return onSuccessTask(continuation, IMMEDIATE_EXECUTOR); } public Task onSuccessTask( final Continuation> continuation, Executor executor) { return onSuccessTask(continuation, executor, null); } public Task onSuccessTask( final Continuation> continuation, Executor executor, final CancellationToken ct) { return continueWithTask(new Continuation>() { @Override public Task then(Task task) { if (ct != null && ct.isCancellationRequested()) { return Task.cancelled(); } if (task.isFaulted()) { return Task.forError(task.getError()); } else if (task.isCancelled()) { return Task.cancelled(); } else { return task.continueWithTask(continuation); } } }, executor); }
我們可以從源代碼看出,taskA調用了continueWithTask傳入一個匿名continuation得到taskB,在taskA正常的、無報錯,即成功的狀態下,taskA會調用continueWithTask(),此時傳入一開始我們傳進來的continuation,然後再執行我們所期望的操作。此時return一個taskC,taskC的結果為我們的continuation返回的taskD的結果。
接下來是continueWhile:
public TaskcontinueWhile(Callable predicate, Continuation > continuation) { return continueWhile(predicate, continuation, IMMEDIATE_EXECUTOR, null); } public Task continueWhile(final Callable predicate, final Continuation > continuation, final Executor executor) { return continueWhile(predicate, continuation, executor, null); } //上面兩個調用下面這個 public Task continueWhile(final Callable predicate, final Continuation > continuation, final Executor executor, final CancellationToken ct) { final Capture >> predicateContinuation = new Capture<>(); predicateContinuation.set(new Continuation >() { @Override public Task then(Task task) throws Exception { //這裡ct 必定為null,不用管 if (ct != null && ct.isCancellationRequested()) { return Task.cancelled(); } if (predicate.call()) { return Task. forResult(null).onSuccessTask(continuation, executor) .onSuccessTask(predicateContinuation.get(), executor); } //返回一個一個已經結束且result為null的task return Task.forResult(null); } }); //此時makeVoid()會調用Task.forResult(null) return makeVoid().continueWithTask(predicateContinuation.get(), executor); }
這段代碼中我們可以直接閱讀最後的return語句,這個時候就調用到了我們之前講的continueWithTask,
而Capture是一個簡單的類,可以臨時存儲一個泛型變量,這次的predicateContinuation則存儲了一個返回值類型為Tsak的Continuation對象。
調用continueWhile之後我們獲取了一個taskA,這個taskA會在Continuation返回的taskB結束後結束,並將taskB的結果作為自己的結果,而taskB是第n行中ruturn的。我們把這一行中使用Task.
接下來是onSuccess():
publicTask onSuccess( final Continuation continuation) { return onSuccess(continuation, IMMEDIATE_EXECUTOR, null); } public Task onSuccess( final Continuation continuation, Executor executor) { return onSuccess(continuation, executor, null); } //上面兩個調用下面這個 public Task onSuccess( final Continuation continuation, Executor executor, final CancellationToken ct) { return continueWithTask(new Continuation>() { @Override public Task then(Task task) { if (ct != null && ct.isCancellationRequested()) { return Task.cancelled(); } if (task.isFaulted()) { return Task.forError(task.getError()); } else if (task.isCancelled()) { return Task.cancelled(); } else { return task.continueWith(continuation); } } }, executor); }
這個跟onSuccessTask差不多,成功的時候執行一個continuation而已,沒什麼特別需要解釋的。
到這裡,Task的api和相應的源碼解釋就差不多完成了,下面說一些零散的使用技巧
1、當你需要一個task來調用某些方法時可以使用Tsak.forResult(null)來獲取一個實例,或者直接使用new Task(null)來獲取一個實例。
2、多使用xxxxxTask方法來避免金字塔代碼堆疊
3、有需要可以自己傳一個異常處理器進去處理你可能出現的異常
4、多注意一下傳入的處理線程是否正確,否則是要搞出個大新聞的。
照例沒圖
做了一點微小的工作,謝謝
類似QQ分組的樣子,實現tableView的折疊與展開。其實要做這個效果我先想到的是在tableView中再嵌套多個tableView,這個想法實現起來就有點難了。所以還
在很多地方我們都會用到縱向列表樣式的菜單,比如微信首頁的我、發現頁面,微博的首頁的我頁面,QQ的動態頁面等等等等,大多數的應用中都會存在這樣的頁面。我們怎樣實現這種頁面比
用到的兩個png圖片首先是自定義theme,不能用默認的主題,會報錯;you cannot combined....。修改res/values/styles.xml:上面
本文實例講述了Android編程實現圓角邊框的方法。分享給大家供大家參考,具體如下:設置邊框圓角可以在drawable-mdpi目錄裡定義一個xml:<?x