واجهة برمجة التطبيقات 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 }