استخدام الـ WorkManager من حزمة JetPack

Mohammad Laifمنذ سنة

بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته

 

في الدروس السابقة رأينا طرق كثيرة لجدولة الاعمال والمهام منها الـ Alarm Manager و الـ Android JobScheduler والـ Firebase JobDispatcher. في هذا الدرس سنتعرف على احدث الطرق وافضلها وهو الـ Work Manager.

 

بماذا يمتاز عن البقية

  • يستطيع انشاء خيط حاسوبي لمهامك, لذلك لاداعي لإستخدام احد انماط التصميم.
  • ربما يقوم بإستخدام كلاً من الـ Android JobScheduler او Firebase JobDispatcher او AlarmManager اذا دعى الامر لذلك.
  • يريحك من قيامك بكتابة الـ Logic للحصول على مواصفات هاتف مستخدم تطبيقك. حتى ترى ايهم المناسب له JobScheduler (تم دعمه من API 21 واحدث) او JobDispatcher (تم دعمه من API 14 واحدث, و يتطلب وجود Google Play Services مثبت في هاتف المستخدم). اي لاداعي للخيره بعد الان فقط قم بإستخدام الـ Work Manager.
  • يستطيع عمل Chaining لإعمالك, اي تستطيع ترتيب الاعمال ايهم يبدء اولاً وثانياً وثالثاً.

 

يعتبر الـ WorkManager احدث شئ في نظام الاندرويد لجدولة المهام والاعمال وتشغيلها في الخلفيه, فهو يحتل جزء مرموق في الـ Android Architecture Components التي اتت مع حزمة الـ JetPack. فهو يستطيع استخدام اي من الـ AlarmManager والـ Firebase JobDispatcher و الـ JobScheduler بدون التدخل منك (فقط سلم عملك الى الـ WorkManager ودع الباقي له في اختيار افضل شئ يراه مناسباً).

 

رابط اعتماديات المكتبة

WorkManager Dependencies.

 

خطوات انشاء الـ WorkManager

  • بما انه مكتبه خارجية سنحتاج الى اضافة الـ Dependencies له في ملف الـ Gradle.
  • ننشئ كلاس جديدة تعمل extends للـ Worker.
  • نقوم بعمل Constructor بداخلها يإخد Context و معلومات العمل WorkerParameters.
  • نقوم بعمل Override للدالة doWork وبها نضع شفرتنا البرمجية المراد تشغيلها.

 

الخطوات:

اضافة الـ Dependencies له في ملف الـ Gradle:

    // Work Manager
    def work_version = "1.0.0-beta05"
    implementation "android.arch.work:work-runtime:$work_version"

 

انشاء كلاس الـ Work:

public class MyJobManager extends Worker {}

 

عمل Constructor بداخلها يإخد Context و معلومات العمل WorkerParameters:

public class MyJobManager extends Worker {

    public MyJobManager(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
...
..

 

عمل Override للدالة doWork وبها نضع شفرتنا البرمجية المراد تشغيلها:

public class MyJobManager extends Worker {

    public MyJobManager(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        // Our Code goes here
        // Our Code goes here
        // Our Code goes here
        return Result.success();
    }

}

لاحظ التالي:

  • في دالة الـ doWork نقوم بوضع شفرتنا البرمجة المراد تشغيلها.
  • نقوم بعمل return للقيم التالية:
    • Result.success: في حالة نجاح واتمام الشفرة البرمجية لعملنا.
    • Result.retry: في الفشل وهنا نطلب من نظام الاندرويد ان يقوم بإعادة المحاولة.
    • Result.failure: في الفشل وهنا نطلب من نظام الاندرويد ان لايقوم بإعادة المحاولة.

 

ماذا لو اردنا ارسال البيانات من الـ Activity او اي مكون اخر الى هذه الكلاس لإستخدامهم هنا؟

في هذه الحاله نقوم بإستخدام الدالة getInputData للحصول على المعلومات المرسلة, واذا قمنا بمعالجتهم نستطيع ارسالهم الى المكون من خلال وضعهم بداخل عنصر Data وارفاقه في الدالة Result.success كمعطى. للمزيد من التفصيل اطلع على الخطوة التالية:

 

استقبال البيانات في كلاس الـ Work:

public class MyJobManager extends Worker {

    public MyJobManager(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        String name = getInputData().getString("NAME_KEY");
        int age = getInputData().getInt("AGE_KEY", -1);

        String result = name + age;

        Data output = new Data.Builder()
                .putString("RESULT_KEY", result)
                .build();

        return Result.success(output);
    }

}

لاحظ التالي:

  • قمنا بإستقبال عنصرين بإستخدام الدالة getInputData وهما: اسم بنوع String وعمر بنوع int.
  • قمنا بإجراء العملية المراده ووضعنا النتيجه في عنصر String بإسم result.
  • قمنا بإنشاء عنصر من نوع Data ووضع النتيجه في داخله.
  • قمنا بإرسال النتيجه الى المكون الذي قام بتشغيل هذه الكلاس (سنرى الخطوه لاحقاً) من خلال وضعها كمعطى في الدالة Result.success.

 

تجهيز العمل للـ WorkManager

والان بعد ان قمنا بإنشاء كلاس خاصة بنا للـ WorkManager نحتاج الى انشاء بيانات هذا العمل وتجهيزه ثم جدولته.

اولاً تجهيز البيانات التي نريد ارسالها الى كلاسنا السابقه:

        Data myData = new Data.Builder()
                .putString("NAME_KEY", "Mohammad")
                .putInt("AGE_KEY", 14)
                .build();

 

تجيهز القيود او الشروط لعملنا:

        Constraints constraints = new Constraints.Builder()
                .setRequiresDeviceIdle(true)
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();

لاحظ التالي:

  • سيعمل عملنا عندما يكون الجهاز في وضع الـ Idle ويجب كذلك ان يكون متصل بالانترنت.

 

تجهيز وانشاء عملنا لتشغيله لمرة واحدة فقط:

        OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(MyJobManager.class)
                .setInputData(myData)
                .setConstraints(constraints)
                .addTag("MY_WORK_MANAGER_TAG_ONE_TIME")
                .build();

لاحظ التالي:

  • قمنا بإنشاء عملنا بإستخدام كلاس الـ OneTimeWorkRequest وهذا يعني اننا نريد من علمنا ان يتنفذ فقط مرة واحدة.
  • لاحظ ايضاً اعطاء الـ Builder كلاس الـ Work الذي انشئناها.
  • ايضاً قمنا بإرفاق الـ Data بإستخدام setInputData.
  • وكذلك قمنا بإرفاق القيود او الشروط بإستخدام setConstraints.
  • واضفنا Tag لهذا العمل, حتى اذا اردنا ان نحدثه او نلغيه في المستقبل (تستطيع وضع Tag مشابه لإكثر من عمل).
  • واخيراً قمنا ببنائه.

 

تجهيز وانشاء عملنا لتشغيله لإكثر من مرة:

        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(MyJobManager.class, 12, TimeUnit.HOURS)
                .setInputData(myData)
                .setConstraints(constraints)
                .addTag("MY_WORK_MANAGER_PERIODIC_TAG")
                .build();

لاحظ التالي:

  • قمنا بإنشاء علمنا في هذه المره بإستخدام كلاس الـ PeriodicWorkRequest لتشغيله لإكثر من مره.
  • لاحظ ايضاً اعطاء الـ Builder كلاس الـ Work الذي انشئناها وايضاً الوقت المراد تكرار العمل (كل ١٢ ساعه)٠.
  • اما ماتبقى فهو مشابه للمثال السابق.

 

جدولة العمل

الان نستطيع ارسال العمل الى النظام من خلال الـ Work Manager حتى يقوم بجدولته كالتالي:

WorkManager.getInstance().enqueue(oneTimeWorkRequest);

لاحظ التالي:

قمنا بإستخدام كلاس الـWorkManager ويتضح بإنها صممت بنمط الـ Singleton ثم نداء الدالة enqueue لوضع عملنا في قائمة النظام لتشغيله عندما يحين وقته وشروطه.

 

الغاء العمل

لإلغاء العمل نحتاج الى الحصول على الـ Id الخاص به, ثم الغائه بإستخدام الدالة cancelWorkById وفق الطريقة التالية:

UUID oneTimeWorkRequestId = oneTimeWorkRequest.getId();
WorkManager.getInstance().cancelWorkById(oneTimeWorkRequestId);

 

نستطيع ايضاً الغاء الاعمال كلها, او بعضها بإستخدام الـ Tag كالتالي:

WorkManager.getInstance().cancelAllWork();
WorkManager.getInstance().cancelAllWorkByTag("MY_WORK_MANAGER_TAG_ONE_TIME");

 

استقبال النتيجة من كلاس الـ Worker

بعد انجاز عملنا نستطيع استقبال النتيجة من خلال عنصر الـ LiveData (ماهي الـ LiveData؟) ,كالتالي:

WorkManager.getInstance().getWorkInfoByIdLiveData(oneTimeWorkRequestId).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(@Nullable WorkInfo workInfo) {
                if (workInfo != null && workInfo.getState().isFinished()){
                    String result = workInfo.getOutputData().getString("RESULT");
                    Log.d(TAG, "onChanged: " + result);
                }
            }
        });

لاحظ التالي:

  • قمنا بإستخدام الدالة getWorkInfoByIdLiveData ثم اعطيناها الـ Id الخاص بعملنا (انظر طريقة الحصول عليه في الخطوة السابقه بعنوان: الغاء العمل).
  • ثم بعد ذلك اعطيناتها الـ Activity كـ this لإنها تتطلب LifeCyclerAware وايضاً اعطيناها متنصت ليراقب متى تجهز البيانات, ومن ثم نستطيع استخدامهم كما نريد.

 

ربط Chaining اكثر من عمل

اذا كان يوجد لدينا اعمال نريد تشغيلهم بالتسلسل نستطيع جدولتهم هكذا:

        WorkManager.getInstance()
                .beginWith(downLoadImageWorkRequest)
                .then(saveDownloadedImageToDatabaseWorkRequest)
                .then(cleanTempWorkRequest)
                .enqueue();

 

اذا اردنا تشغيل بعضهم متوازي Parallel فيكون الوضع هكذا:

        WorkManager.getInstance()
                .beginWith(Arrays.asList(downloadImagesWorkRequest, downloadTextsWorkRequest, downloadLinksWorkRequest))    // Runs in Parallel.
                .then(cleanTempWorkRequest)
                .enqueue();

 

في هذا الدرس تعرفنا على الـ Work Manager ورئينا ابرز خصائصه ومميزاته.

 

رابط الكلاسات المستخدمه في هذا الدرس

للحصول على رابط المشروع راجع درس المقدمة.
 

المصادر والمراجع

للمزيد راجع درس المقدمة.

 

نهاية الدرس
لاتنسى تتبع الدرس والدورة كذلك لإشعارك عندما يتم التعديل على المتحوى او اضافة المزيد من المعلومات. ايضاً لاتنسى الاعجاب بالدرس ومشاركته مع الاخرين.

المحاضر

Mohammad Laif

محتوى الدورة

تمهيد
1 مقدمة
2 تعرف على التزامن (الـ Concurrency)
3 اخطاء التزامن (الـ Concurrency) الشائعة
العمليات Processes
1 الهيكلة الهندسية لبيئة نظام الاندرويد
2 طبقة الـ Android Runtime و العمليات Processes
3 انواع العمليات (Processes)
الخيوط الحاسوبية Threads
1 الخيوط الحاسوبية (Threads)
2 مكونات الخيوط الحاسوبية (Thread)
3 انشاء الخيط الحاسوبي
أنماط التصميم للخيوط الحاسوبية
1 انشاء نمط الـ Handler و Looper و Thread
2 انشاء نمط الـ HandlerThreads
3 ماهو نمط الـ Thread Pools (الـ Executors)
4 استخدام نمط الـ Thread Pools كـ Singleton
5 استخدام الـ Callable مع نمط الـ Thread Pools
6 انشاء بركة خيوط حاسوبية Thread Pools لتسريع قاعدة البيانات
7 انشاء نمط الـ AsyncTask
8 انشاء نمط الـ Loader
الـ Broadcasts
1 تعرف على الـ Broadcast Receiver
2 انشاء الـ Broadcast Receiver بشكل ثابت
3 انشاء الـ Broadcast Receiver بشكل ديناميكي
4 استخدام الـ Local Broadcast للتخاطب بين المكونات
الـ Services
1 تعرف على الـ Services
2 انشاء الـ Started Service
3 انشاء الـ Intent Service
4 انشاء الـ Bound Service
5 استخدام الـ ResultReceiver للتخاطب مع الـ Intent Service
6 استخدام الـ Broadcast للتخاطب مع الـ Started Service
الـ Alarm Manager
1 تعرف على الـ Alarm Manager
2 طرق استخدام الـ Alarm Manager
الـ Jobs
1 استخدام الـ Android JobSchedualer
2 استخدام الـ Firebase JobDispatcher
3 استخدام الـ WorkManager من حزمة JetPack الدرس الحالي

الكلمات الدليلية

عن الدرس

0 إعجاب
1 متابع
0 مشاركة
591 مشاهدات
منذ سنة

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !