إدارة العمل

بعد تحديد Worker و WorkRequest، تتمثل الخطوة الأخيرة في إضافة عملك إلى قائمة الانتظار. إنّ أبسط طريقة لإدراج العمل في قائمة الانتظار هي استدعاء طريقة enqueue() WorkManager، مع تمرير WorkRequest الذي تريد تشغيله.

Kotlin


val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java


WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

يُرجى توخي الحذر عند إضافة العمل إلى قائمة المحتوى التالي لتجنّب التكرار. على سبيل المثال، قد يحاول أحد التطبيقات تحميل سجلاته إلى خدمة خلفية كل 24 ساعة. وإذا لم تكن حريصًا، فقد ينتهي بك الأمر إلى إضافة نفس المهمة إلى قائمة الانتظار عدة مرات، على الرغم من أن المهمة تحتاج إلى تشغيلها مرة واحدة فقط. لتحقيق هذا الهدف، يمكنك جدولة العمل باعتباره عملاً فريدًا.

العمل الفريد

العمل الفريد هو مفهوم فعّال يضمن عدم توفّر سوى حدث واحد فقط مع اسم معيّن في كل مرة. على عكس المعرّفات، يمكن للمستخدمين قراءة الأسماء الفريدة ويتم تحديدها من قِبل المطوّر بدلاً من إنشاؤها تلقائيًا من خلال WorkManager. على عكس العلامات، يتم ربط الأسماء الفريدة بمثيل واحد فقط من العمل.

يمكن تطبيق العمل الفريد على كل من العمل لمرة واحدة والعمل الدوري. يمكنك إنشاء تسلسل عمل فريد عن طريق استدعاء إحدى هذه الطرق، اعتمادًا على ما إذا كنت تجدول عملاً متكررًا أو عملاً لمرة واحدة.

تقبل كلتا الطريقتين 3 وسيطات:

  • uniqueWorkName - يتم استخدام String لتعريف طلب العمل بشكل فريد.
  • existingWorkPolicy - enum لإعلام WorkManager بما يجب فعله إذا كانت هناك سلسلة أعمال غير مكتملة بهذا الاسم الفريد يمكنك الاطّلاع على سياسة حلّ النزاعات لمزيد من المعلومات.
  • work - WorkRequest المطلوب جدولته.

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

Kotlin


val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Java


PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

والآن، إذا كان الرمز البرمجي يتم تشغيله وكانت مهمة sendLogs في قائمة الانتظار، سيتم الاحتفاظ بالمهمة الحالية ولن تتم إضافة مهمة جديدة.

يمكن أن تكون تسلسلات العمل الفريدة مفيدة أيضًا إذا كنت بحاجة إلى بناء سلسلة طويلة من المهام تدريجيًا. على سبيل المثال، قد يتيح تطبيق تعديل الصور للمستخدمين التراجع عن سلسلة طويلة من الإجراءات. قد تستغرق كل عملية من عمليات التراجع هذه بعض الوقت، ولكن يجب تنفيذها بالترتيب الصحيح. في هذه الحالة، يمكن للتطبيق إنشاء سلسلة "تراجع" وإلحاق كل عملية تراجع بالسلسلة حسب الحاجة. راجع سلسلة الأعمال للحصول على مزيد من التفاصيل.

سياسة حل النزاعات

عند جدولة عمل فريد، يجب عليك إخبار WorkManager بالإجراء الذي يجب اتخاذه عند وجود تعارض. يمكنك القيام بذلك عن طريق تمرير تعداد عند وضع العمل في قائمة الانتظار.

بالنسبة إلى العمل لمرة واحدة، عليك تقديم السمة ExistingWorkPolicy التي توفّر 4 خيارات للتعامل مع التعارض.

  • REPLACE العمل الحالي مع العمل الجديد. يؤدي هذا الخيار إلى إلغاء العمل الحالي.
  • KEEP العمل الحالي وتجاهل العمل الجديد.
  • APPEND العمل الجديد حتى نهاية العمل الحالي. ستؤدي هذه السياسة إلى ربط عملك الجديد بالعمل الحالي وتشغيله بعد انتهاء العمل الحالي.

يصبح العمل الحالي شرطًا أساسيًا للعمل الجديد. إذا أصبح العمل الحالي CANCELLED أو FAILED، سيكون العمل الجديد أيضًا CANCELLED أو FAILED. إذا كنت تريد تنفيذ العمل الجديد بغض النظر عن حالة العمل الحالي، استخدِم السمة APPEND_OR_REPLACE بدلاً من ذلك.

  • تعمل APPEND_OR_REPLACE بشكل مشابه لـ APPEND، باستثناء أنها لا تعتمد على حالة العمل مطلوبة. وإذا كان العمل الحالي هو CANCELLED أو FAILED، سيستمر العمل الجديد.

بالنسبة إلى العمل خلال الدورة الشهرية، يمكنك تقديم سمة ExistingPeriodicWorkPolicy التي توفّر خيارَين، هما REPLACE وKEEP. تعمل هذه الخيارات بالطريقة نفسها التي تعمل بها نظيراتها في CurrentWorkPolicy.

ملاحظة عملك

في أي وقت بعد إضافة العمل إلى قائمة الانتظار، يمكنك التحقّق من حالته من خلال طلب بحث WorkManager عن طريق name أو id أو باستخدام tag مرتبط به.

Kotlin


// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Java


// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>


يعرض طلب البحث ListenableFuture لكائن WorkInfo، والذي يتضمن id العمل وعلاماته وStateه الحالية وأي بيانات للمخرجات من خلال Result.success(outputData).

يتيح لك خيار LiveData لكلّ طريقة من الطرق ملاحظة التغييرات التي تطرأ على WorkInfo من خلال تسجيل مستمع. على سبيل المثال، إذا كنت ترغب في عرض رسالة للمستخدم عند انتهاء بعض الأعمال بنجاح، يمكنك إعدادها كما يلي:

Kotlin


workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(), 
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}


Java


workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

استعلامات العمل المعقدة

يتيح الإصدار 2.4.0 من WorkManager والإصدارات الأحدث تنفيذ طلبات بحث معقدة للمهام المدرَجة في قائمة الانتظار باستخدام عناصر WorkQuery. يدعم WorkQuery الاستعلام عن العمل من خلال مزيج من علاماته والحالة واسم العمل الفريد.

يوضّح المثال التالي كيفية العثور على جميع الأعمال التي تتضمّن العلامة "syncTag" والتي تكون في الحالة FAILED أو CANCELLED ولها اسم عمل فريد إما "preProcess" أو "sync".

Kotlin


val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Java


WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

يتم ANDإضافة كل مكوّن (علامة أو حالة أو اسم) في WorkQuery مع العناصر الأخرى. كل قيمة في المكوِّن هي OR-ed. على سبيل المثال: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

تتوافق ميزة "WorkQuery" أيضًا مع مكافئ بيانات LiveData، getWorkInfosLiveData().

إلغاء العمل وإيقافه

إذا لم تعُد بحاجة إلى تنفيذ عملك الذي سبق إدراجه في قائمة الانتظار، يمكنك طلب إلغاؤه. يمكن إلغاء العمل من خلال name أو id أو tag المرتبط به.

Kotlin


// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")


Java


// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

لمزيد من التفاصيل، يتحقّق WorkManager من State العمل. إذا كان العمل منتهيًا بالفعل، فلا يحدث شيء. وإلا، سيتم تغيير حالة العمل إلى CANCELLED ولن يتم تنفيذ العمل في المستقبل. وأي وظائف في WorkRequest تعتمد على هذا العمل ستكون أيضًا CANCELLED.

يستقبل العمل الحالي RUNNING مكالمة إلى الرقم ListenableWorker.onStopped(). يمكنك تجاهل هذه الطريقة للتعامل مع أي تنظيف محتمل. راجِع إيقاف عامل قيد التشغيل للاطّلاع على مزيد من المعلومات.

إيقاف عامل قيد التشغيل

هناك بعض الأسباب المختلفة التي قد تمنع WorkManager عن تشغيل Worker:

  • لقد طلبت صراحة إلغاء الجهاز (من خلال الاتصال بـ WorkManager.cancelWorkById(UUID)، على سبيل المثال).
  • في حالة العمل الفريد، أضفت بشكل صريح WorkRequest جديد إلى قائمة الانتظار مع ExistingWorkPolicy لـ REPLACE. تُعتبر WorkRequest القديمة ملغاة على الفور.
  • لم تعد قيود عملك مستوفية.
  • طلب النظام من تطبيقك إيقاف العمل لسببٍ ما. يمكن أن يحدث ذلك إذا تجاوزت الموعد النهائي للتنفيذ وهو 10 دقائق. تمت جدولة العمل لإعادة المحاولة في وقت لاحق.

في ظل هذه الظروف، يتوقف العامل.

يجب أن تلغي بشكل تعاوني أي عمل قمت به قيد التقدم وتطلق أي موارد يحتفظ بها العامل لديك. على سبيل المثال، يجب عليك إغلاق الأسماء المعرِّفة المفتوحة لقواعد البيانات والملفات في هذه المرحلة. هناك آليتان تحت تصرفك لمعرفة متى يتوقف العامل لديك.

معاودة الاتصال onStopped()

يستدعي WorkManager ListenableWorker.onStopped() فور توقف عاملك. تجاوز هذه الطريقة لإغلاق أي موارد قد تحتفظ بها.

سمة isStopped()

يمكنك استدعاء طريقة ListenableWorker.isStopped() لمعرفة ما إذا كان قد تم إيقاف عاملك. إذا كنت تجري عمليات طويلة الأمد أو متكررة في عاملك، يجب عليك التحقق من هذه الخاصية بشكل متكرر واستخدامها كإشارة لإيقاف العمل في أسرع وقت ممكن.

ملاحظة: يتجاهل WorkManager Result الذي ضبطه العامل الذي تلقّى إشارة onStop، لأنّ "العامل" سبق أن تم اعتباره متوقفًا.