واجهة برمجة التطبيقات Task هي الطريقة العادية للتعامل مع العمليات غير المتزامنة في "خدمات Google Play". وهي توفّر طريقة قوية ومرنة لإدارة الطلبات غير المتزامنة، ما يحلّ محلّ النمط القديم PendingResult. باستخدام Task، يمكنك ربط طلبات متعدّدة، والتعامل مع التدفقات المعقّدة، وكتابة أدوات معالجة واضحة للنجاح والفشل.
التعامل مع نتائج المهام
تعرض العديد من واجهات برمجة التطبيقات في "خدمات 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 تلقائيًا في سلسلة المحادثات الرئيسية (واجهة المستخدم) للتطبيق. هذا يعني أنّه عليك تجنُّب إجراء عمليات طويلة الأمد في المستمعين. إذا كنت بحاجة إلى إجراء عملية طويلة الأمد، يمكنك تحديد 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) { // ... } });
ربط المهام
إذا كنت تستخدم مجموعة من واجهات برمجة التطبيقات التي تعرض كائنات 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 للعمل بشكل جيد مع أنماط البرمجة غير المتزامنة الشائعة الأخرى على Android. يمكن تحويله من وإلى عناصر أساسية أخرى، مثل
ListenableFuture وKotlin coroutines، التي
توصي بها AndroidX،
ما يتيح لك استخدام الطريقة التي تناسب احتياجاتك على أفضل وجه.
في ما يلي مثال على استخدام Task:
// ... simpleTask.addOnCompleteListener(this) { completedTask -> textView.text = completedTask.result }
Kotlin coroutine
لاستخدام Kotlin coroutines مع 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 }