Tasks API

Stay organized with collections Save and categorize content based on your preferences.

Google Play 開発者サービス バージョン 9.0.0 以降では、Task API と、Task またはそのサブクラスを返す多数のメソッドを使用できます。Task は、以前のバージョンの Google Play 開発者サービスの PendingResult のような非同期メソッド呼び出しを表す API です。

タスクの結果の処理

Task を返す一般的なメソッドは FirebaseAuth.signInAnonymously() です。これは Task<AuthResult> を返します。これは、タスクが成功すると AuthResult オブジェクトを返すことを意味します。

Task<AuthResult> task = FirebaseAuth.getInstance().signInAnonymously();

タスクが成功したときに通知を受けるには、OnSuccessListener をアタッチします。

task.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
    @Override
    public void onSuccess(AuthResult authResult) {
        // Task completed successfully
        // ...
    }
});

タスクが失敗したときに通知を受け取るには、OnFailureListener をアタッチします。

task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // Task failed with an exception
        // ...
    }
});

同じリスナーで成功と失敗を処理するには、OnCompleteListener をアタッチします。

task.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
            // Task completed successfully
            AuthResult result = task.getResult();
        } else {
            // Task failed with an exception
            Exception exception = task.getException();
        }
    }
});

スレッド化

スレッドにアタッチされたリスナーは、デフォルトでアプリのメイン(UI)スレッドで実行されます。リスナーをアタッチするときに、リスナーのスケジュール設定に使用する Executor を指定することもできます。

// Create a new ThreadPoolExecutor with 2 threads for each processor on the
// device and a 60 second keep-alive time.
int numCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(numCores * 2, numCores *2,
        60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

task.addOnCompleteListener(executor, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        // ...
    }
});

アクティビティ スコープのリスナー

Activity でタスクの結果をリッスンしている場合は、タスクにアクティビティ スコープのリスナーを追加することをおすすめします。これらのリスナーはアクティビティの onStop メソッドの中で削除されるため、アクティビティが非表示になったときにリスナーは呼び出されません。

Activity activity = MainActivity.this;
task.addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        // ...
    }
});

チェーン

Task を返す複数の API を使用する場合は、継続処理を使用してチェーン化できます。これにより、深くネストされたコールバックを回避し、タスクチェーンのエラー処理を統合できます。

たとえば、メソッド doSomethingTask<String> を返しますが、AuthResult はタスクから非同期に取得します。

public Task<String> doSomething(AuthResult authResult) {
    // ...
}

Task.continueWithTask メソッドを使用すると、次の 2 つのタスクを連結できます。

Task<AuthResult> signInTask = FirebaseAuth.getInstance().signInAnonymously();

signInTask.continueWithTask(new Continuation<AuthResult, Task<String>>() {
    @Override
    public Task<String> then(@NonNull Task<AuthResult> task) throws Exception {
        // Take the result from the first task and start the second one
        AuthResult result = task.getResult();
        return doSomething(result);
    }
}).addOnSuccessListener(new OnSuccessListener<String>() {
    @Override
    public void onSuccess(String s) {
        // Chain of tasks completed successfully, got result from last task.
        // ...
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // One of the tasks in the chain failed with an exception.
        // ...
    }
});

ブロック

プログラムがすでにバックグラウンド スレッドで実行されている場合は、タスクを同期して同期的に結果を取得し、コールバックを回避できます。

try {
    // Block on a task and get the result synchronously. This is generally done
    // when executing a task inside a separately managed background thread. Doing this
    // on the main (UI) thread can cause your application to become unresponsive.
    AuthResult authResult = Tasks.await(task);
} catch (ExecutionException e) {
    // The Task failed, this is the same exception you'd get in a non-blocking
    // failure handler.
    // ...
} catch (InterruptedException e) {
    // An interrupt occurred while waiting for the task to complete.
    // ...
}

タスクをブロックする際のタイムアウトを指定して、アプリケーションがハングしないようにすることもできます。

try {
    // Block on the task for a maximum of 500 milliseconds, otherwise time out.
    AuthResult authResult = Tasks.await(task, 500, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
    // ...
} catch (InterruptedException e) {
    // ...
} catch (TimeoutException e) {
    // Task timed out before it could complete.
    // ...
}

相互運用性

Task は、非同期コードを管理するための一般的な Android アプローチの概念に沿ったものです。また、AndroidX が推奨する ListenableFuture や Kotlin のコルーチンなど、他のプリミティブに簡単に変換できます。

Task を使用した例を次に示します。

// ...
simpleTask.addOnCompleteListener(this) {
  completedTask -> textView.text = completedTask.result
}

Kotlin コルーチン

使用方法

プロジェクトに次の依存関係を追加し、以下のコードを使用して Task から変換します。

Gradle(モジュール レベルの build.gradle(通常は app/build.gradle))
// Source: https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.1'
スニペット
import kotlinx.coroutines.tasks.await
// ...
  textView.text = simpleTask.await()
}

Guava による ListenableFuture

プロジェクトに次の依存関係を追加し、以下のコードを使用して Task から変換します。

Gradle(モジュール レベルの build.gradle(通常は app/build.gradle))
implementation "androidx.concurrent:concurrent-futures:1.1.0"
スニペット
import com.google.common.util.concurrent.ListenableFuture
// ...
/** Convert Task to ListenableFuture. */
fun <T> taskToListenableFuture(task: Task<T>): ListenableFuture<T> {
  return CallbackToFutureAdapter.getFuture { completer ->
    task.addOnCompleteListener { completedTask ->
      if (completedTask.isCanceled) {
        completer.setCancelled()
      } else if (completedTask.isSuccessful) {
        completer.set(completedTask.result)
      } else {
        val e = completedTask.exception
        if (e != null) {
          completer.setException(e)
        } else {
          throw IllegalStateException()
        }
      }
    }
  }
}
// ...
this.listenableFuture = taskToListenableFuture(simpleTask)
this.listenableFuture?.addListener(
  Runnable {
    textView.text = listenableFuture?.get()
  },
  ContextCompat.getMainExecutor(this)
)

RxJava2 Observable

任意の相対的非同期ライブラリに加えて、以下の依存関係をプロジェクトに追加し、次のコードを使用して Task から変換します。

Gradle(モジュール レベルの build.gradle(通常は app/build.gradle))
// Source: https://github.com/ashdavies/rx-tasks
implementation 'io.ashdavies.rx.rxtasks:rx-tasks:2.2.0'
スニペット
import io.ashdavies.rx.rxtasks.toSingle
import java.util.concurrent.TimeUnit
// ...
simpleTask.toSingle(this).subscribe { result -> textView.text = result }

次のステップ