
المدة المقدرة للقراءة 15 دقيقة
تمهيد
في القسم السابق تحدثنا عن استخدام الصف AsyncTask لإنجاز المهمات الطويلة في تطبيقات الأندرويد بشكل غير متزامن.
كانت مشكلة هذا الصف هي صعوبة التزامن بين دورة حياة المهمة ودورة حياة الواجهة التي بدأت هذه المهمة والتي تؤدي إلى الكثير من الأخطاء.
يتميز الصف IntentService بأن دورة حياته غير مرتبطة بحياة النشاط أو السياق الذي قام بتشغيله، لكن حياته مرتبطة فقط بإنهاء المهمة، فهو يعمل في الخلفية حتى إنجاز العمل ولا يتوقف إلا بانتهاء العمل بشكل طبيعي أو بحدوث خطأ أو إيقافه من قبل نظام الأندرويد.
كيفية الاستخدام
سأقوم بشرح مثال يحاكي عملية تحميل ملف من الانترنت في الخلفية عبر IntentService.
لفهم المثال قم بإنشاء مشروع فارغ باستخدام برنامج Android Studio ثم قم بإنشاء IntentService عبر القائمة:
File->New->Service->Intent Service
وقم بتسميتها MyIntentService.
قم بفتح الملف MyIntentService.java و استبدل الكود الموجود فيه بالكود التالي ثم تابع الشرح
import android.app.IntentService;
import android.content.Intent;
public class MyIntentService extends IntentService {
public static final String ACTION_DOWNLOAD_COMPLETED = "com.mutasem4it.android.intentserviceexample.action.DOWNLOAD_COMPLETED";
public static final String ACTION_DOWNLOAD_FAILED = "com.mutasem4it.android.intentserviceexample.action.DOWNLOAD_FAILED";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Intent i = new Intent();
if (downloadFile()) {
i.setAction(ACTION_DOWNLOAD_COMPLETED);
sendBroadcast(i);
} else {
i.setAction(ACTION_DOWNLOAD_FAILED);
sendBroadcast(i);
}
}
private boolean downloadFile() {
//ToDO file downloading here
//Pseudo Code
for (int i = 0; i < 100; i++)
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
}
الدالة onHandleIntent
هي دالة مجردة على المبرمج تحقيقها بوضع الكود الخاص بالمهمة المطلوب من الخدمة إنجازها، يتم استدعائها بشكل تلقائي بمجرد تشغيل الخدمة، ويتم تمرير وسيط من نمط Intent يستخدم لتمرير المعلومات من السياق (Activity) الذي قام بتشغيل الخدمة.
الدالة downloadFile
تحوي كود وهمي يقوم بعمل تأخير زمني يحاكي عملية تحميل ملف من الانترنت، يتم استدعاؤها من قبل الدالة onHandleIntent.
الثابت ACTION_DOWNLOAD_COMPLETED
هو عبارة عن متحول عام يستخدم لإرسال BroadCast بأن عملية التحميل قد انتهت بنجاح، حيث يقوم المستقبل BroadCastReciver باستخدام هذا الثابت للتنصت على حالة تحميل الملف، ونفس الكلام ينطبق أيضاً على الثابت ACTION_DOWNLOAD_FAILED الذي يستخدم للدلالة على فشل تحميل الملف.
كيفية تشغيل الخدمة لبدء التحميل
يمكن تشغيل الخدمة انطلاقاً من أي سياق، لذلك سنقوم بإنشاء سياق من نمط Activity تملك واجهة تحوي زر لبدء التحميل فقط.
قم بإنشاء Activity من القائمة:
File->New->Activity->Empty Activity
قم بتسميتها MainActivity.
انسخ الكود التالي إلى ملف activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.mutasem4it.android.intentservicetest.MainActivity">
<Button
android:text="Donwload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnClick"
android:layout_marginTop="14dp"
android:id="@+id/button" />
</RelativeLayout>
كيف نقوم بتشغيل IntenetService
إن IntentService هو نوع من أنواع الخدمات (Service) بالتالي يمكن تشغيله بنفس الطريقة التي نقوم فيها بتشغيل الخدمة باستخدام الصف Intent والدالة startService.
يجب أن تتم عملية التشغيل في حدث النقر على الزر لذلك قم بنسخ الكود التالي إلى ملف MainActivity.java
public void btnClick(View view) {
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
}
قمنا بإنشاء Intent وتمرير السياق الحالي كوسيط أول، أما الوسيط الثاني فهو الخدمة المطلوب تشغيلها.
بعد ذلك قمنا بتشغيل الخدمة عن طريق الدالة startService.
بعد تشغيل الخدمة تبدأ عملية تحميل الملف، لكن السؤال كيف نعرف أنها انتهت من تحميل الملف؟
بالعودة إلى الدالة onHandleIntent نلاحظ بعد استدعاء الدالة downloadFile قمنا باختبار حالة عملية التحميل، وفي حال النجاح قمنا بعمل BroadCast من نوع ACTION_DOWNLOAD_COMPLETED وهذا يعني أنه لمعرفة حالة عملية التحميل كل ماعلينا هو التنصت على هذا ال BroadCast، بالتالي نحن بحاجة إلى إنشاء BroadCastReciver. في الملف MainActivity.java قم بوضع الشيفرة التالية في المنطقة المخصصة لتعريف الأعضاء البيانية
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyIntentService.ACTION_DOWNLOAD_COMPLETED))
Toast.makeText(MainActivity.this, "Completed", Toast.LENGTH_LONG).show();
else if (intent.getAction().equals(MyIntentService.ACTION_DOWNLOAD_FAILED))
Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_LONG).show();
}
};
في الشيفرة السابقة قمنا بتعريف عضو بياني من نمط BroadcastReceiver وقمنا بتزويده بالشيفرة اللازمة لمعالجة أي BroadCast يصل إليه، لكن لم نقم بإخباره ما نوع ال broadcast الذي يجب أن قوم بانتظاره. لتحقيق ذلك قم بإضافة الكود البرمجي التالي داخل الدالة onCreat()
registerReceiver(downloadReceiver, new IntentFilter(MyIntentService.ACTION_DOWNLOAD_COMPLETED));
registerReceiver(downloadReceiver, new IntentFilter(MyIntentService.ACTION_DOWNLOAD_FAILED));
هذه الشيفرة تخبر المستقبل بأن ينتظر نوعين من ال BroadCast الأول حدث انتهاء التحميل بنجاح والآخر فشل التحميل.
رابط السلسة على Github
https://github.com/mutasemhajhasan/Android-Async
المراجع
Google. (n.d.). Retrieved from Android Developers: developer.android.com
Murphy, M. L. (2017). The Busy Coder’s Guide to Android Development. COMMONSWARE.