Bolts框架中Task的用法

項(xiàng)目地址:Bolts-Android

Task是為了更好的書寫復(fù)雜異步操作而設(shè)計(jì)的,運(yùn)用了Javascript的Promise思想。

如果想要構(gòu)建一個(gè)響應(yīng)迅速的Android應(yīng)用,那么你就不能在UI線程中運(yùn)行任何耗時(shí)操作,避免阻塞UI線程,這也就意味著你需要在后臺中執(zhí)行大量的操作。為了讓這一過程變得更簡單,我們增加了這個(gè)叫做Task的類。一個(gè)Task代表一個(gè)異步操作。通常情況下,我們會寫一個(gè)方法返回一個(gè)Task,這個(gè)Task具有繼續(xù)操作任務(wù)結(jié)果的能力。當(dāng)這個(gè)Task被方法返回時(shí),它已經(jīng)開始執(zhí)行它的任務(wù)了。Task不與特定的線程模型進(jìn)行綁定:它代表要被完成的操作,而不是執(zhí)行操作的地點(diǎn)。Task與其他異步方法(CallbacksAsyncTask)相比有許多優(yōu)勢。

  • Task占用更少的系統(tǒng)資源,因?yàn)?code>Task在等待其他Tasks的時(shí)候不占用線程。
  • 執(zhí)行一系列Task的時(shí)候不需要像你使用CallBack時(shí)一樣寫出金字塔式的嵌套代碼。
  • Task是可以組合的,允許你執(zhí)行分支、并行和復(fù)合型的錯(cuò)誤處理,不需要用到嵌套的代碼和各種復(fù)雜命名的CallBack。
  • 你可以有序的整理基于任務(wù)的代碼并執(zhí)行它們,而不是將你的邏輯分散在凌亂的回調(diào)函數(shù)中。

continueWith方法

每個(gè)Task都有一個(gè)continueWith方法,帶有一個(gè)Continuation參數(shù)。Continuation是一個(gè)接口,你可以實(shí)現(xiàn)它的then方法,then方法會在任務(wù)完成的時(shí)候調(diào)用,你可以在這里檢查任務(wù)的完成狀態(tài)并做相應(yīng)的處理。

saveAsync(obj).continueWith(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    if (task.isCancelled()) {
      // the save was cancelled.
    } else if (task.isFaulted()) {
      // the save failed.
      Exception error = task.getError();
    } else {
      // the object was saved successfully.
      ParseObject object = task.getResult();
    }
    return null;
  }
});

Tasks使用了強(qiáng)類型的Java泛型,所有一開始就想要書寫語法正確的代碼可能需要一點(diǎn)點(diǎn)技巧。下面通過一個(gè)例子深入了解一下。

/**
 Gets a String asynchronously.
 */
public Task<String> getStringAsync() {
  // Let's suppose getIntAsync() returns a Task<Integer>.
  return getIntAsync().continueWith(
    // This Continuation is a function which takes an Integer as input,
    // and provides a String as output. It must take an Integer because
    // that's what was returned from the previous Task.
    new Continuation<Integer, String>() {
      // The Task getIntAsync() returned is passed to "then" for convenience.
      public String then(Task<Integer> task) throws Exception {
        Integer number = task.getResult();
        return String.format("%d", Locale.US, number);
      }
    }
  );
}

在許多情況下,你可能只是想在前一個(gè)任務(wù)成功結(jié)束時(shí)做一點(diǎn)微小的工作,并把發(fā)生錯(cuò)誤和任務(wù)取消的情況留到以后處理,那么你可以使用onSuccess方法代替continueWith方法。

saveAsync(obj).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // the object was saved successfully.
    return null;
  }
});

Task鏈?zhǔn)骄幊?/h3>

我們對Task做了一些膜法,支持鏈?zhǔn)秸{(diào)用而不用編寫復(fù)雜的嵌套邏輯。你可以使用continueWithTask來代替continueWith,它會返回一個(gè)新的Task。由continueWithTask返回的Task在新的任務(wù)執(zhí)行結(jié)束之前不會被認(rèn)為結(jié)束。另外,onSuccessTask是能夠返回新Task版的onSuccess,你可以使用onSuccess/continueWith來執(zhí)行更多的同步操作,或者使用onSuccessTask/continueWithTask來執(zhí)行更多的異步操作。

final ParseQuery<ParseObject> query = ParseQuery.getQuery("Student");
query.orderByDescending("gpa");
findAsync(query).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(0).put("valedictorian", true);
    return saveAsync(students.get(0));
  }
}).onSuccessTask(new Continuation<ParseObject, Task<List<ParseObject>>>() {
  public Task<List<ParseObject>> then(Task<ParseObject> task) throws Exception {
    ParseObject valedictorian = task.getResult();
    return findAsync(query);
  }
}).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(1).put("salutatorian", true);
    return saveAsync(students.get(1));
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // Everything is done!
    return null;
  }
});

異常處理

在書寫你的應(yīng)用時(shí),謹(jǐn)慎的選擇調(diào)用continueWithonSuccess可以幫助你控制異常的傳遞方式。使用continueWith可以傳遞發(fā)生的異?;?qū)λM(jìn)行某些處理。你可以考慮用拋出異常的方式使一個(gè)Task失敗,事實(shí)上,如果你在continuation中拋出了一個(gè)異常,Task的結(jié)果會顯示失敗并返回這個(gè)異常。

final ParseQuery<ParseObject> query = ParseQuery.getQuery("Student");
query.orderByDescending("gpa");
findAsync(query).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(0).put("valedictorian", true);
    // Force this callback to fail.
    throw new RuntimeException("There was an error.");
  }
}).onSuccessTask(new Continuation<ParseObject, Task<List<ParseObject>>>() {
  public Task<List<ParseObject>> then(Task<ParseObject> task) throws Exception {
    // Now this continuation will be skipped.
    ParseObject valedictorian = task.getResult();
    return findAsync(query);
  }
}).continueWithTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    if (task.isFaulted()) {
      // This error handler WILL be called.
      // The exception will be "There was an error."
      // Let's handle the error by returning a new value.
      // The task will be completed with null as its value.
      return null;
    }

    // This will also be skipped.
    List<ParseObject> students = task.getResult();
    students.get(1).put("salutatorian", true);
    return saveAsync(students.get(1));
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // Everything is done! This gets called.
    // The task's result is null.
    return null;
  }
});

這有利于書寫很長的只處理成功情況的鏈?zhǔn)秸{(diào)用,只需要在調(diào)用鏈末尾書寫一個(gè)錯(cuò)誤處理即可。

創(chuàng)建Task

一開始,你可能只是使用類似findAsyncsaveAsync這種方法返回的簡單的Task。但是,對于更高級的方案,你可能想創(chuàng)建自定義的Task。為了實(shí)現(xiàn)這個(gè)需求,你創(chuàng)建了一個(gè)TaskCompletionSource。這個(gè)對象允許你創(chuàng)建新的Task并且控制它的執(zhí)行結(jié)果是已完成或取消。在你創(chuàng)建了一個(gè)Task之后,你需要調(diào)用setResult,seterror,setCancelled來觸發(fā)它之后的操作。

public Task<String> succeedAsync() {
  TaskCompletionSource<String> successful = new TaskCompletionSource<>();
  successful.setResult("The good result.");
  return successful.getTask();
}

public Task<String> failAsync() {
  TaskCompletionSource<String> failed = new TaskCompletionSource<>();
  failed.setError(new RuntimeException("An error message."));
  return failed.getTask();
}

如果你在一個(gè)Task創(chuàng)建時(shí)就知道他某個(gè)結(jié)果需要執(zhí)行的操作,你可以使用下面這些比較方便的方法。

Task<String> successful = Task.forResult("The good result.");

Task<String> failed = Task.forError(new RuntimeException("An error message."));

創(chuàng)建異步方法

使用如下方法,你可以很輕松的創(chuàng)建你自己的異步任務(wù)并返回一個(gè)Task。

public Task<ParseObject> fetchAsync(ParseObject obj) {
  final TaskCompletionSource<ParseObject> tcs = new TaskCompletionSource<>();
  obj.fetchInBackground(new GetCallback() {
    public void done(ParseObject object, ParseException e) {
     if (e == null) {
       tcs.setResult(object);
     } else {
       tcs.setError(e);
     }
   }
  });
  return tcs.getTask();
}

我們同樣提供了方法方便你在代碼塊中創(chuàng)建Task。當(dāng)執(zhí)行到call代碼塊時(shí),callInBackground會在后臺線程池中執(zhí)行Task。

Task.callInBackground(new Callable<Void>() {
  public Void call() {
    // Do a bunch of stuff.
  }
}).continueWith(...);

順次執(zhí)行Task

Task允許你執(zhí)行一連串的異步任務(wù),每個(gè)任務(wù)都會在前一個(gè)任務(wù)完成后再執(zhí)行。舉個(gè)例子,你想要?jiǎng)h除你博客上的所有評論。

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments");
query.whereEqualTo("post", 123);

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() {
  public Task<Void> then(Task<List<ParseObject>> results) throws Exception {
    // Create a trivial completed task as a base case.
    Task<Void> task = Task.forResult(null);
    for (final ParseObject result : results) {
      // For each item, extend the task with a function to delete the item.
      task = task.continueWithTask(new Continuation<Void, Task<Void>>() {
        public Task<Void> then(Task<Void> ignored) throws Exception {
          // Return a task that will be marked as completed when the delete is finished.
          return deleteAsync(result);
        }
      });
    }
    return task;
  }
}).continueWith(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    // Every comment was deleted.
    return null;
  }
});

同時(shí)執(zhí)行多個(gè)Task

你可以調(diào)用whenall方法來同步執(zhí)行多個(gè)TaskTask.whenall會創(chuàng)建一個(gè)新的Task,此Task會在輸入的所有Task都執(zhí)行完畢后再標(biāo)記為完成狀態(tài),此Task只會在所有傳入Task都成功時(shí)標(biāo)記為成功狀態(tài)。同時(shí)執(zhí)行任務(wù)比順次執(zhí)行任務(wù)更快,但可能消耗更多的系統(tǒng)資源和帶寬。

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments");
query.whereEqualTo("post", 123);

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() {
  public Task<Void> then(Task<List<ParseObject>> results) throws Exception {
    // Collect one task for each delete into an array.
    ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
    for (ParseObject result : results) {
      // Start this delete immediately and add its task to the list.
      tasks.add(deleteAsync(result));
    }
    // Return a new task that will be marked as completed when all of the deletes are
    // finished.
    return Task.whenAll(tasks);
  }
}).onSuccess(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    // Every comment was deleted.
    return null;
  }
});

Task Executors

所有continueWithonSuccess方法都可將java.util.concurrent.Executor的實(shí)例作為參數(shù)傳入。這讓你可以控制后續(xù)任務(wù)在哪里執(zhí)行。默認(rèn)狀態(tài)下,Task.call()會在當(dāng)前線程執(zhí)行Callable,Task.callInBackgorund會在自己的線程池中執(zhí)行,你也可以提供自己的Executor在其它線程中執(zhí)行任務(wù)。舉個(gè)例子,假如你想要在一個(gè)特別的線程池中執(zhí)行任務(wù)。

static final Executor NETWORK_EXECUTOR = Executors.newCachedThreadPool();
static final Executor DISK_EXECUTOR = Executors.newCachedThreadPool();
final Request request = ...
Task.call(new Callable<HttpResponse>() {
  @Override
  public HttpResponse call() throws Exception {
    // Work is specified to be done on NETWORK_EXECUTOR
    return client.execute(request);
  }
}, NETWORK_EXECUTOR).continueWithTask(new Continuation<HttpResponse, Task<byte[]>>() {
  @Override
  public Task<byte[]> then(Task<HttpResponse> task) throws Exception {
    // Since no executor is specified, it's continued on NETWORK_EXECUTOR
    return processResponseAsync(response);
  }
}).continueWithTask(new Continuation<byte[], Task<Void>>() {
  @Override
  public Task<Void> then(Task<byte[]> task) throws Exception {
    // We don't want to clog NETWORK_EXECUTOR with disk I/O, so we specify to use DISK_EXECUTOR
    return writeToDiskAsync(task.getResult());
  }
}, DISK_EXECUTOR);

對于常用場景,例如分發(fā)至主線程執(zhí)行,我們提供了默認(rèn)的實(shí)現(xiàn):Task.UI_THREAD_EXECUTOR,Task.BACKGROUND_EXECUTOR。

fetchAsync(object).continueWith(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> object) throws Exception {
    TextView textView = (TextView)findViewById(R.id.name);
    textView.setText(object.get("name"));
    return null;
  }
}, Task.UI_THREAD_EXECUTOR);

捕獲變量

將代碼重構(gòu)為多個(gè)回調(diào)的難點(diǎn)在于他們具有不同的變量作用域。Java允許你使用外部域的變量,但前提是他必須被聲明為final,這非常的不方便,因?yàn)闀?dǎo)致這些變量不可變。這也是我們添加Capture這個(gè)類的原因,它允許你在各個(gè)回調(diào)之間共享變量。只需要你調(diào)用get,set方法來改變它的值即可。

// Capture a variable to be modified in the Task callbacks.
final Capture<Integer> successfulSaveCount = new Capture<Integer>(0);

saveAsync(obj1).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj1) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj2);
  }
}).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj2) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj3);
  }
}).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj3) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj4);
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> obj4) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return null;
  }
}).continueWith(new Continuation<Void, Integer>() {
  public Integer then(Task<Void> ignored) throws Exception {
    // successfulSaveCount now contains the number of saves that succeeded.
    return successfulSaveCount.get();
  }
});

取消Task

如果想要取消Task,需要先創(chuàng)建一個(gè)CancellationTokenSource,并把token傳給所有你想要取消的創(chuàng)建Task的方法,之后只要調(diào)用cancel()就會結(jié)束所有與該token關(guān)聯(lián)的Task。

CancellationTokenSource cts = new CancellationTokenSource();

Task<Integer> stringTask = getIntAsync(cts.getToken());

cts.cancel();

取消異步任務(wù)需要修改方法接受一個(gè)CancellationToken并調(diào)用isCancellationRequested()來決定什么時(shí)候終止操作。

/**
 Gets an Integer asynchronously.
 */
public Task<Integer> getIntAsync(final CancellationToken ct) {
  // Create a new Task
  final TaskCompletionSource<Integer> tcs = new TaskCompletionSource<>();

  new Thread() {
    @Override
    public void run() {
      // Check if cancelled at start
      if (ct.isCancellationRequested()) {
        tcs.setCancelled();
        return;
      }

      int result = 0;
      while (result < 100) {
        // Poll isCancellationRequested in a loop
        if (ct.isCancellationRequested()) {
          tcs.setCancelled();
          return;
        }
        result++;
      }
      tcs.setResult(result);
    }
  }.start();

  return tcs.getTask();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,675評論 25 709
  • 譯序 本指南根據(jù) Jakob Jenkov 最新博客翻譯,請隨時(shí)關(guān)注博客更新:http://tutorials.j...
    高廣超閱讀 5,457評論 1 68
  • /** * 截取中文字符串 * @param string $string 中文字符串 * @param int ...
    上善若水_900e閱讀 171評論 0 0
  • 今天上午第一節(jié)晨讀課一下課,我們就匆匆下樓,到一樓實(shí)驗(yàn)室聽王心理老師講書法課,王老師是我們學(xué)校的退休美術(shù)老...
    自由自在綠葉閱讀 874評論 0 0
  • 『論病氣』這個(gè)問題困擾了我很久也思考了很久,至今也找不到解決的辦法。 病氣是什么?簡單講就是已經(jīng)進(jìn)入人體形成疾病的...
    Zoe媽inUK閱讀 2,421評論 1 0

友情鏈接更多精彩內(nèi)容