استخدام الـ Android JobSchedualer
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
في هذا الدرس سنرى طريقة عمل الـ JobScheduler لتشغيل المهام في فتره معينه وعلى خيط حاسوبي اخر. والذي يعتبر نوع من انواع الخدمات Services كما جاء في درس سابق: تعرف على الـ Services.
متى نستخدم الـ JobScheduler؟
عندما تريد اجراء عمل معين (كإظهار اشعار للمستخدم او تحميل شئ من الانترنت او عمل نسخة احتياطية او رفع شئ الى الانترنت وما الى ذلك) في ظروف معينه (كعندما يقوم المستخدم بالإتصال بشبكة الانترنت من خلال الواي فاي) في وقت معين (كتشغيل العمل كل ٢٤ ساعة).
نقاط يجب عليك مراعاتها
- الـ JobScheduler هو عبارة عن Service في نظام الاندرويد.
- نظام الاندرويد هو المتحكم في دقة وقت التشغيل (عكس الـ Alarm Manager الذي تستطيع التحكم انت في وقت التشغيل بدقة).
- يعمل في الخيط الحاسوبي الرئيسي, لذلك من الافضل استخدام احد انماط التصميم للخيوط الحاسوبية (كما جاء في الدروس السابقه) عند اجراء مهمة ثقيله.
العيوب
- بدء دعمه من الـ Android 5, لذلك يعتبر غير مناسب اذا كنت تريد دعم اجهزة هاتف متنوعة او قديمة (الافضل استخدام Firebase JobDispatcher كما سيأتي في الدرس القادم).
خطوات انشاء الـ JobSchedualer
- ننشئ كلاس جديدة تعمل extends للـ JobService.
- اضافته الى ملف الـ manifest.xml.
- نعمل override للدوال onStartJob و onStopJob.
- نقوم بكتابة الشفرة البرمجية المراد تشغيلها في دالة الـ onStartJob.
- نقوم ببناء بيانات العمل (التي تحتوي على ظروف وأوقات معينه) حتى نرسلها الى نظام الاندرويد.
- جدولة العمل.
الخطوات:
انشاء كلاس JobService وعمل extends للدوال onStartJob و onStopJob:
تأكد ان تعمل import للـ:
import android.app.job.JobParameters; import android.app.job.JobService;
public class MyJobScheduler extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
اضافته الى ملف الـ manifest.xml:
<service
android:name=".task.MyJobScheduler"
android:enabled="true"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
>
</service>
نحتاج الى اضافته الى ملف الـ Manifest.xml لإنه عباره عن Service مثل ماجاء في دروسها, مع فرق بسيط كما هو ملاحظ.
نقوم بكتابة الشفرة البرمجية المراد تشغيلها في دالة الـ onStartJob:
@Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob: ");
// Our code goes here
// Our code goes here
// Our code goes here
// Returning false to let the android knows our job is done, and we don't want to reschedule it
return false;
}
عليك الانتباه للنقاط التالية:
- اي شفرة برمجة نضعها هنا سوف تعمل في الخيط الحاسوبي الرئيسي. لذلك قم بوضع الاعمال الخفيفه, وإلا قم بأستخدام احد انماط الخيوط الحاسوبية للإعمال الثقيله.
- يجب علينا ارجاع قيمة False لإعلام نظام الاندرويد ان هذا العمل انتهى تنفيدة في الخيط الحاسوبي الرئيسي.
- تستطيع استقبال البيانات التي تريد استخدامهم من خلال الـ Params الذي سنقوم بتمريره لاحقاً عندما نريد جدولة وتشغيل هذا العمل.
الشفرة البرمجية للدالة onStopJob:
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
يجيب عليك الانتباه للنقاط التالية:
- نظام الاندرويد لن يقوم بنداء (اي تشغيل) هذه الدالة عندما تقوم بإرجاع قيمة False في onStartJob.
- النظام سيقوم بنداء هذه الدالة في حالة انهاء عملك قسرياً.
تشغيل العمل في خيط حاسوبي اخر عن الخيط الحاسوبي الرأيسي
اذا كان عملك ثقيل (كتعامله مع الشبكه العنكبوتيه او قواعد البيانات او تحويل الصور والتشفير وما الى ذلك) ستحتاج لإنشاء خيط حاسوبي بإستخدام احد انماط التصميم لهم, كما سنرى في الخطوة القادمة.
انشاء كلاس JobService تعمل بالخلفيه وتستخدم خيط حاسوبي مختلف عن الخيط الرئيسي
بإستخدام نمط الـ HandlerThread بشكل سريع, كما جاء شرحه في الدرس انشاء نمط الـ HandlerThreads:
@Override
public boolean onStartJob(final JobParameters params) {
// With Handler
HandlerThread handlerThread = new HandlerThread("MyJobBackgroundThread");
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
// Our code goes here
// Our code goes here
// Our code goes here
jobFinished(params, false);
}
});
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
تذكر ان افضل الانماط هي الـ ThreadPools كما جاء في الدروس الخاصة بها ماهو نمط الـ Thread Pools (الـ Executors).
او بإستخدام نمط الـ AsyncTask بشكل سريع, كما جاء شرحه في الدرس انشاء نمط الـ AsyncTask:
@Override
public boolean onStartJob(final JobParameters params) {
// With AsyncTask
AsyncTask<JobParameters, Void, Void> task = new AsyncTask<JobParameters, Void, Void>() {
@Override
protected Void doInBackground(JobParameters... jobParameters) {
JobParameters currentParams = jobParameters[0];
String passedData = currentParams.getExtras().getString(EXTRA_DATA);
// Our code goes here
// Our code goes here
// Our code goes here
Log.d(TAG, "doInBackground: Thread: " + Thread.currentThread().getName());
Log.d(TAG, "doInBackground: passedData: " + passedData);
jobFinished(currentParams, false);
return null;
}
};
task.execute(params);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
لاحظ التالي في دالة الـ onStartJob:
- لاحظ طريقة استخراج البيانات المراد استخدامها في شفرتنا البرمجة من الـ jobParameters.
- لاحظ نوع الـ return في الدالة onStartJob تحول الى True. وهكذا نعلم نظام الاندرويد اننا سوف نؤدي عملنا في خيط خلفي مختلف عن الخيط الرئيسي.
- لاحظ نداء الدالة jobFinished عند انتهاء عملنا. وذلك يعتبر من مسؤليتنا ان نناديها لإنهاء العمل عندما نستخدم احد انماط الخيوط الحاسوبية لإجراء المهام في الخلفيه.
- ايضاً لاحظ مدخلات الدالة jobFinished فهي تأخد الـ Params (معلومات العمل) لإرساله للنظام لاحقاً حتى يتعرف على عملنا. وايضاً تاخد قيمة boolean كـ False اذا لم ترد اعادة جدولة عملك. او قم بإستخدام True حتى تطلب من النظام اعادة جدولة هذا العمل.
ربما تريد ان تحيط شفرتك البرمجية بـ Try هكذا:
try { // Our code goes here } catch (Exception e) { // Errors appears } finally { // Stop our job, and reschedule it or not. jobFinished(params, false); }
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
لاحظ التالي في دالة الـ onStartJob:
- سيتم نداء هذه الدالة اذا حصل لبس او عدم تطابق في شروط عملك (الذي يحتويها الـ JobInfo).
مثال: لنفرض ان عملك هو تحميل صور من الانترنت, ومعلوماته (الـ JobInfo) تحتوي على الشرط التالي: اريد تشغيل هذا العمل عندما يقوم المستخدم بالاتصال الى شبكة الـ WIFI.
والان قام المستخدم بالاتصال الى شبكة الـ WIFI وهكذا قام نظام الاندرويد بتشغيل عملك عندما اتى وقته, وفجأه قام المستخدم بغلق شبكة الـ WIFI ولم ينتهي عملك! هنا سيقوم نظام الاندرويد بإنهاء عملك تعسفياً ونداء هذه الدالة.
ربما تسأل: اذن مافائدة هذه الدالة؟
تستطيع الاستفادة من هذه الدالة في:
- عملية التنظيف, مثلاً الغاء تحميل الصور او حذف ماتم تحميله واظهار رسالة فشل للتحميل وما الى ذلك (تذكر ان هذه الدالة تعمل على الخيط الحاسوبي الرئيسي).
- اعادة جدولة عملك عندما يفشل وذلك بإرجاع قيمة True بدلاً من False.
نستشف من هذا ان شروط وبيانات عملك (الـ JobInfo) يجب ان تكون متطابقه اثناء تشغيل عملك الى ان ينتهي.
تجهيز العمل للـ JobScheduler
والان بعد ان قمنا بإنشاء كلاس خاصة بنا للـ JobScheduler ووضع بها شفرتنا البرمجيه نحتاج الى انشاء بيانات العمل ومتى نريده ان يبدأ وجدولته وكل هذا باستخدام كلاس الـ JobInfo.
نجهز ID خاص بالعمل:
private static final int OUR_JOB_ID = 10101;
من خلال هذا الـ ID نستطيع الغاء العمل, او تحديثه.
نجهز Bundle من نوع PersistableBundle حتى نضع بداخله البيانات التي نريد ارسالها من خلال الـ Params الى كلاس الـ JobScheduler الخاصه بنا:
PersistableBundle extras = new PersistableBundle();
extras.putString(MyJS.EXTRA_DATA, "This is any type of primitive data");
نجهز الفترة الزمنيه المراد تشغيل عملنا فيها:
long ONE_DAY_INTERVAL = 24 * 60 * 60 * 1000L;
نجهز عنصر من الـ ComponentName (نحتاجة لإنشاء الـ JobInfo) كالتالي:
ComponentName componentName = new ComponentName(this, MyJS.class);
والان نستطيع البدء في انشاء معلومات العمل الـ JobInfo كالتالي:
JobInfo jobInfo = new JobInfo.Builder(OUR_JOB_ID, componentName)
.setExtras(extras)
.setPeriodic(ONE_DAY_INTERVAL)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
لاحظ التالي:
- قمنا بإنشاء عنصر باستخدام كلاس الـ JobInfo بنمط الـ Builder يأخد الـ ID الخاص بعملنا واسم المكون الذي انشئناه. ومن خلاله نحدد شرطيات العمل بحيث اذا اتمت كلها صحيحه سوف يقوم نظام الاندرويد بتشغيله.
جدولة العمل مع الـ JobScheduler
والان بعد ان قمنا بإنشاء كلاس خاصة بنا للـ JobScheduler وايضاً بعد قيامنا بتجهيز العمل يجب علينا جدولة العمل كخطوة نهائيه كالتالي:
JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
لاحظ التالي:
- قمنا بانشاء عنصر من كلاس الـ JobScheduler ومن خلال هذا العنصر قمنا بتمرير عنصر الـ JobInfo الخاص بعملنا ليقوم النظام بجدولته.
لتصبح الدالة كاملة كالشكل التالي:
private void scheduleJob() {
PersistableBundle extras = new PersistableBundle();
extras.putString(MyJS.EXTRA_DATA, "This is any type of data you want to pass it into the job to use it when job is triggered");
long ONE_DAY_INTERVAL = 24 * 60 * 60 * 1000L;
// Building the Information about our job
ComponentName componentName = new ComponentName(this, MyJS.class);
JobInfo jobInfo = new JobInfo.Builder(OUR_JOB_ID, componentName)
.setExtras(extras)
.setPeriodic(ONE_DAY_INTERVAL)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
// Schedule the Job
JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
}
في هذا الدرس تعرفنا على الـ JobScheduler الذي يأتي مع الـ Android SDK. وتعرفنا على طريقة انشاء كلاسه وجدولته. في الدرس القادم سنرى طريقة انشاء الـ JobDispatcher الذي يأتي من خلال الاعتماديات للـ Firebase فهو افضل بكثير.
رابط الكلاسات المستخدمه في هذا الدرس
- تم استخدام MyJobScheduler.java.
للحصول على رابط المشروع راجع درس المقدمة.
المصادر والمراجع
- JobScheduler | Android Developers.
- Background Execution Limits | Android Developers.
- Scheduling jobs like a pro with JobScheduler.
للمزيد راجع درس المقدمة.
نهاية الدرس
لاتنسى تتبع الدرس والدورة كذلك لإشعارك عندما يتم التعديل على المتحوى او اضافة المزيد من المعلومات. ايضاً لاتنسى الاعجاب بالدرس ومشاركته مع الاخرين.
محتوى الدورة
الكلمات الدليلية
عن الدرس
0 إعجاب |
1 متابع |
0 مشاركة |
3164 مشاهدات |
منذ 5 سنوات |
التعليقات (0)
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !