Task API ها

Task API روشی استاندارد برای مدیریت عملیات ناهمزمان در خدمات Google Play است. این روشی قدرتمند و انعطاف‌پذیر برای مدیریت تماس‌های ناهمزمان ارائه می‌کند و جایگزین الگوی قدیمی‌تر PendingResult می‌شود. با Task می‌توانید چندین تماس را زنجیره‌ای کنید، جریان‌های پیچیده را مدیریت کنید، و کنترل‌کننده‌های موفقیت و شکست را واضح بنویسید.

رسیدگی به نتایج کار

بسیاری از APIها در سرویس‌های Google Play و Firebase یک شی Task را برای نمایش عملیات ناهمزمان برمی‌گردانند. برای مثال، FirebaseAuth.signInAnonymously() Task<AuthResult> را برمی گرداند که نشان دهنده نتیجه عملیات ورود به سیستم است. Task<AuthResult> نشان می دهد که وقتی کار با موفقیت کامل شد، یک شی AuthResult را برمی گرداند.

می‌توانید با پیوست کردن شنوندگانی که به انجام موفقیت‌آمیز، شکست یا هر دو پاسخ می‌دهند، نتیجه یک Task را مدیریت کنید:

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();
        }
    }
});

رشته ها را مدیریت کنید

به طور پیش‌فرض، شنونده‌های متصل به یک Task در رشته اصلی برنامه (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 مدیریت کنید، مهم است که چرخه عمر شنوندگان را مدیریت کنید تا از فراخوانی آنها در زمانی که Activity دیگر قابل مشاهده نیست جلوگیری کنید. برای این کار می توانید از شنونده های محدوده فعالیت استفاده کنید. هنگامی که متد onStop از Activity شما فراخوانی می شود، این شنوندگان به طور خودکار حذف می شوند تا پس از توقف Activity اجرا نشوند.

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

وظایف زنجیره ای

اگر از مجموعه‌ای از APIها استفاده می‌کنید که اشیاء Task را در یک تابع پیچیده برمی‌گردانند، می‌توانید با استفاده از ادامه‌ها آن‌ها را به هم متصل کنید. این به شما کمک می‌کند از تماس‌های عمیق تو در تو اجتناب کنید و مدیریت خطا را برای کارهای زنجیره‌ای چندگانه ادغام می‌کند.

به عنوان مثال، سناریویی را در نظر بگیرید که در آن شما یک متد doSomething دارید که یک Task<String> را برمی گرداند، اما به یک پارامتر AuthResult نیاز دارد. می توانید این AuthResult به صورت ناهمزمان از Task دیگر بدست آورید:

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

با استفاده از متد Task.continueWithTask ، می توانید این دو وظیفه را به صورت زنجیره ای درآورید:

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 به گونه ای طراحی شده است که با سایر الگوهای برنامه نویسی ناهمزمان رایج اندروید به خوبی کار کند. می‌توان آن را به و از دیگر موارد اولیه مانند ListenableFuture و Kotlin که توسط AndroidX توصیه می‌شود، تبدیل کرد و به شما امکان می‌دهد از رویکردی استفاده کنید که به بهترین وجه با نیازهای شما مطابقت دارد.

در اینجا یک مثال با استفاده از Task آورده شده است:

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

کاتلین کوروتین

برای استفاده از کوروتین های Kotlin با Task ، وابستگی زیر را به پروژه خود اضافه کنید و سپس از قطعه کد برای تبدیل از یک 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.7.3'
قطعه
import kotlinx.coroutines.tasks.await
// ...
  textView.text = simpleTask.await()
}

Guava ListenableFuture

برای استفاده از Guava ListenableFuture با Task ، وابستگی زیر را به پروژه خود اضافه کنید و سپس از قطعه کد برای تبدیل از یک Task استفاده کنید.

Gradle (سطح ماژول build.gradle ، معمولا app/build.gradle )
implementation "androidx.concurrent:concurrent-futures:1.2.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 }

مراحل بعدی