تُعد واجهة برمجة التطبيقات 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 الفرعية، التي تنصح بها مكتبة AndroidX،
ما يتيح لك استخدام الأسلوب الأنسب لاحتياجاتك.
إليك مثال على استخدام Task
:
// ... simpleTask.addOnCompleteListener(this) { completedTask -> textView.text = completedTask.result }
كوروتين Kotlin
لاستخدام كوروتينات 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() }
جوافة 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 }