BoltsフレームにおけるTaskの使い方

15813 ワード

プロジェクト住所:Bolts-Android
Taskは複雑で非同期な作業をよりよく書くために設計されたもので、JavascriptのPromise思想を運用しています.
レスポンスの速いAndroidアプリケーションを構築するためには、UIスレッド内で任意の時間消費動作を実行してはいけません.つまり、UIスレッドのブロックを避けるためには、バックグラウンドで大量の操作を行う必要があります.このプロセスをより簡単にするために、Taskというクラスを追加しました.一つのTaskは、非同期動作を表す.通常、Taskに戻る方法を書きます.このTaskは、タスクの結果を継続的に操作する能力を持っています.このTaskが方法によって返されると、それは既にタスクの実行を開始している.Taskは、動作を実行する場所ではなく、特定のスレッドモデルとバインディングされていない.Taskは、他の非同期方法(CallbacksAsyncTask)と比較して多くの利点がある.
  • Taskは、他のTaskを待つ間、スレッドを占有しないので、より少ないシステムリソースを占有する.
  • が一連のTaskを実行するときは、Taskを使用するときのように、ピラミッド型のネストコードを書く必要はありません.
  • CallBackは、入れ子コードと様々な複雑な名前のTaskを必要としないように、分岐、並列、複合型のエラー処理を実行することができます.
  • は、タスクに基づくコードを整理して実行することができます.あなたの論理をバラバラなコールバック関数に分散させるのではありません.
  • CallBack方法
    continueWithは、Taskパラメータを有するcontinueWith方法を有する.Continuationはインタフェースです.Continuationの方法を実現できます.thenの方法はタスクが完了したときに呼び出します.ここでタスクの完了状態を確認し、対応する処理をしてもいいです.
    saveAsync(obj).continueWith(new Continuation() {
      public Void then(Task 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;
      }
    });
    
    Taskysは強いタイプのJava泛型を使っています.最初から文法の正しいコードを書きたいと思っています.次は一例を通して詳しく調べてみます.
    /**
     Gets a String asynchronously.
     */
    public Task getStringAsync() {
      // Let's suppose getIntAsync() returns a Task.
      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() {
          // The Task getIntAsync() returned is passed to "then" for convenience.
          public String then(Task task) throws Exception {
            Integer number = task.getResult();
            return String.format("%d", Locale.US, number);
          }
        }
      );
    }
    
    多くの場合、前のタスクが成功した時にわずかな作業をしたいだけかもしれません.エラーとジョブがキャンセルされた場合は後で処理してください.then方法の代わりにonSuccess方法を使ってもいいです.
    saveAsync(obj).onSuccess(new Continuation() {
      public Void then(Task task) throws Exception {
        // the object was saved successfully.
        return null;
      }
    });
    
    Taskチェーンプログラミング
    私たちはcontinueWithに対していくつかの膜法を行い、複雑な入れ子論理を作成する代わりにチェーン呼び出しをサポートした.Taskの代わりにcontinueWithTaskを使用してもいいです.新しいTaskを返します.continueWithによって返されたTaskは、新たなタスクの実行が終了する前に終了するとは考えられない.また、continueWithTaskは、新しいTaskバージョンに戻ることができるonSuccessTaskであり、onSuccessonSuccessを使用してより多くの同期動作を実行したり、continueWithonSuccessTaskを使用してより多くの非同期動作を実行したりすることができる.
    final ParseQuery query = ParseQuery.getQuery("Student");
    query.orderByDescending("gpa");
    findAsync(query).onSuccessTask(new Continuation, Task>() {
      public Task then(Task> task) throws Exception {
        List students = task.getResult();
        students.get(0).put("valedictorian", true);
        return saveAsync(students.get(0));
      }
    }).onSuccessTask(new Continuation>>() {
      public Task> then(Task task) throws Exception {
        ParseObject valedictorian = task.getResult();
        return findAsync(query);
      }
    }).onSuccessTask(new Continuation, Task>() {
      public Task then(Task> task) throws Exception {
        List students = task.getResult();
        students.get(1).put("salutatorian", true);
        return saveAsync(students.get(1));
      }
    }).onSuccess(new Continuation() {
      public Void then(Task task) throws Exception {
        // Everything is done!
        return null;
      }
    });
    
    異常処理
    あなたのアプリケーションを書くとき、慎重な選択呼び出しcontinueWithTaskまたはcontinueWithは、異常な伝達方式の制御を助けることができます.onSuccessを使用して、発生した異常を伝達したり、または何らかの処理を行うことができる.continueWithを失敗させる異常な方法で考えられますが、実際には、contionationで異常を投げたら、Taskの結果は失敗を示し、この異常を返します.
    final ParseQuery query = ParseQuery.getQuery("Student");
    query.orderByDescending("gpa");
    findAsync(query).onSuccessTask(new Continuation, Task>() {
      public Task then(Task> task) throws Exception {
        List students = task.getResult();
        students.get(0).put("valedictorian", true);
        // Force this callback to fail.
        throw new RuntimeException("There was an error.");
      }
    }).onSuccessTask(new Continuation>>() {
      public Task> then(Task task) throws Exception {
        // Now this continuation will be skipped.
        ParseObject valedictorian = task.getResult();
        return findAsync(query);
      }
    }).continueWithTask(new Continuation, Task>() {
      public Task then(Task> 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 students = task.getResult();
        students.get(1).put("salutatorian", true);
        return saveAsync(students.get(1));
      }
    }).onSuccess(new Continuation() {
      public Void then(Task task) throws Exception {
        // Everything is done! This gets called.
        // The task's result is null.
        return null;
      }
    });
    
    
    これは、長い文章を書くのに役立ちます.成功した状況だけを扱うチェーンコールは、呼び出しチェーンの最後にエラー処理を記入すればいいです.
    Taskを作成
    最初は、TaskまたはfindAsyncのような方法を用いて単純にsaveAsyncに戻ることができる.しかし、より高級なスキームについては、カスタムTaskを作成したいかもしれません.この需要を実現するために、Taskを作成しました.このオブジェクトは、新しいTaskCompletionSourceを作成し、その実行結果を制御することができます.Taskを作成した後、TasksetResultseterrorを呼び出して、その後の動作をトリガする必要があります.
    public Task succeedAsync() {
      TaskCompletionSource successful = new TaskCompletionSource<>();
      successful.setResult("The good result.");
      return successful.getTask();
    }
    
    public Task failAsync() {
      TaskCompletionSource failed = new TaskCompletionSource<>();
      failed.setError(new RuntimeException("An error message."));
      return failed.getTask();
    }
    
    setCancelledの作成時に彼のある結果が実行されるべき操作を知っていたら、次のような便利な方法を使ってもいいです.
    Task successful = Task.forResult("The good result.");
    
    Task failed = Task.forError(new RuntimeException("An error message."));
    
    非同期方法を作成
    次の方法を使うと、あなた自身の非同期タスクを簡単に作成し、Taskに戻ります.
    public Task fetchAsync(ParseObject obj) {
      final TaskCompletionSource 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();
    }
    
    私たちは同様に、あなたがコードブロックでTaskを作成するのに便利な方法を提供します.Taskコードブロックに実行されると、callは、バックグラウンドスレッド池内でcallInBackgroundを実行する.
    Task.callInBackground(new Callable() {
      public Void call() {
        // Do a bunch of stuff.
      }
    }).continueWith(...);
    
    順番にTaskを実行します
    Taskはあなたが一連の非同期タスクを実行することを許可します.各タスクは前のタスクが完了したら実行します.例えば、ブログのコメントを全部削除したいです.
    ParseQuery query = ParseQuery.getQuery("Comments");
    query.whereEqualTo("post", 123);
    
    findAsync(query).continueWithTask(new Continuation, Task>() {
      public Task then(Task> results) throws Exception {
        // Create a trivial completed task as a base case.
        Task 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>() {
            public Task then(Task 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() {
      public Void then(Task ignored) throws Exception {
        // Every comment was deleted.
        return null;
      }
    });
    
    複数のタスクを同時に実行Task方法を呼び出して、複数のwhenallを同期して実行することができます.Taskは、入力されたすべてのTask.whenallが実行された後に完了状態としてマークされる新しいTaskを作成します.同時にタスクを実行することは、順番に実行するよりも速いが、システムリソースと帯域幅をより多く消費することができる.
    ParseQuery query = ParseQuery.getQuery("Comments");
    query.whereEqualTo("post", 123);
    
    findAsync(query).continueWithTask(new Continuation, Task>() {
      public Task then(Task> results) throws Exception {
        // Collect one task for each delete into an array.
        ArrayList> tasks = new ArrayList>();
        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() {
      public Void then(Task ignored) throws Exception {
        // Every comment was deleted.
        return null;
      }
    });
    
    Task ExectorsTaskおよびTaskのすべての方法は、Taskの例をパラメータとして入力することができる.これは後続のタスクをどこで実行するかをコントロールできます.デフォルトの状態では、continueWithは現在のスレッドでonSuccessを実行し、java.util.concurrent.Executorは自分のスレッドプールで実行し、他のスレッドで自分のExectorを提供しても良い.例えば、特別なスレッドプールでタスクを実行したい場合.
    static final Executor NETWORK_EXECUTOR = Executors.newCachedThreadPool();
    static final Executor DISK_EXECUTOR = Executors.newCachedThreadPool();
    
    final Request request = ...
    Task.call(new Callable() {
      @Override
      public HttpResponse call() throws Exception {
        // Work is specified to be done on NETWORK_EXECUTOR
        return client.execute(request);
      }
    }, NETWORK_EXECUTOR).continueWithTask(new Continuation>() {
      @Override
      public Task then(Task task) throws Exception {
        // Since no executor is specified, it's continued on NETWORK_EXECUTOR
        return processResponseAsync(response);
      }
    }).continueWithTask(new Continuation>() {
      @Override
      public Task then(Task 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);
    
    通常のシーンについては、例えば、メインスレッド実行に配信され、デフォルトの実装を提供する.Task.call()Callable.
    fetchAsync(object).continueWith(new Continuation() {
      public Void then(Task object) throws Exception {
        TextView textView = (TextView)findViewById(R.id.name);
        textView.setText(object.get("name"));
        return null;
      }
    }, Task.UI_THREAD_EXECUTOR);
    
    変数を取り込む
    コードを複数のコールバックに再構成する難しさは、彼らが異なる変数のスコープを持つことにある.Javaは外部ドメインの変数を使用することができますが、前提は彼がfinalとして宣言しなければなりません.これは非常に不便です.これも私たちがTask.callInBackgorundを追加した理由です.各コールバック間で変数を共有することができます.Task.UI_THREAD_EXECUTORTask.BACKGROUND_EXECUTOR方法を呼び出して、その値を変えるだけでいいです.
    // Capture a variable to be modified in the Task callbacks.
    final Capture successfulSaveCount = new Capture(0);
    
    saveAsync(obj1).onSuccessTask(new Continuation>() {
      public Task then(Task obj1) throws Exception {
        successfulSaveCount.set(successfulSaveCount.get() + 1);
        return saveAsync(obj2);
      }
    }).onSuccessTask(new Continuation>() {
      public Task then(Task obj2) throws Exception {
        successfulSaveCount.set(successfulSaveCount.get() + 1);
        return saveAsync(obj3);
      }
    }).onSuccessTask(new Continuation>() {
      public Task then(Task obj3) throws Exception {
        successfulSaveCount.set(successfulSaveCount.get() + 1);
        return saveAsync(obj4);
      }
    }).onSuccess(new Continuation() {
      public Void then(Task obj4) throws Exception {
        successfulSaveCount.set(successfulSaveCount.get() + 1);
        return null;
      }
    }).continueWith(new Continuation() {
      public Integer then(Task ignored) throws Exception {
        // successfulSaveCount now contains the number of saves that succeeded.
        return successfulSaveCount.get();
      }
    });
    
    Taskをキャンセル
    Taskをキャンセルするには、まずCaptureを作成し、キャンセルしたいTaskを作成する方法にtokenを送る必要があります.その後、getを呼び出したら、tokenに関連するすべてのTaskが終了します.
    CancellationTokenSource cts = new CancellationTokenSource();
    
    Task stringTask = getIntAsync(cts.getToken());
    
    cts.cancel();
    
    非同期タスクをキャンセルするには、変更方法が必要である.setを受諾し、CancellationTokenSourceを呼び出して、いつ終了するかを決定する.
    /**
     Gets an Integer asynchronously.
     */
    public Task getIntAsync(final CancellationToken ct) {
      // Create a new Task
      final TaskCompletionSource 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();
    }