إدارة العمل

بعد تحديد 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 بالإجراء الذي يجب اتّخاذه عند حدوث نزاع. يمكنك إجراء ذلك عن طريق تمرير `enum` عند وضع العمل في قائمة الانتظار.

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

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

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

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

مراقبة عملك

في أي وقت بعد وضع العمل في قائمة الانتظار، يمكنك التحقّق من حالته عن طريق طلب البحث من 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 وFlow لكل من الطريقتَين مراقبة التغييرات التي تطرأ على WorkInfo عن طريق تسجيل متتبِّع. على سبيل المثال، إذا أردت عرض رسالة للمستخدم عند انتهاء بعض الأعمال بنجاح، يمكنك إعدادها على النحو التالي:

Kotlin

workManager.getWorkInfoByIdFlow(syncWorker.id)
          .collect{ 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();
   }
});

طلبات البحث المعقّدة عن العمل

يتيح WorkManager 2.4.0 والإصدارات الأحدث طلبات بحث معقّدة عن المهام التي تم وضعها في قائمة الانتظار باستخدام 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);

يتم ربط كل مكوّن (علامة أو حالة أو اسم) في WorkQuery بعامل التشغيل المنطقي AND مع المكوّنات الأخرى. يتم ربط كل قيمة في المكوّن بعامل التشغيل المنطقي OR. على سبيل المثال: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

WorkQuery يعمل أيضًا مع النظير في LiveData، getWorkInfosLiveData()، والنظير في Flow، getWorkInfosFlow().

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

إذا لم تعُد بحاجة إلى تشغيل العمل الذي سبق أن وضعته في قائمة الانتظار، يمكنك طلب إلغائه. يمكن إلغاء العمل حسب 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 ولن يتم تشغيل العمل في المستقبل. سيتم أيضًا CANCELLED أي مهام WorkRequest تعتمد على هذا العمل.

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

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

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

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

في ظلّ هذه الظروف، يتم إيقاف `Worker`.

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

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

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

السمة `isStopped()`

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

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