تحديد طلبات العمل

تناول دليل البدء كيفية إنشاء WorkRequest بسيط

ستتعلّم في هذا الدليل طريقة تحديد عناصر WorkRequest وتخصيصها للتعامل مع حالات الاستخدام الشائعة، مثل طريقة تنفيذ ما يلي:

  • جدولة العمل لمرة واحدة والمتكرر
  • ضبط قيود العمل، مثل طلب استخدام شبكة Wi-Fi أو الشحن
  • ضمان الحد الأدنى من التأخير في تنفيذ العمل
  • وضع استراتيجيات إعادة المحاولة والتراجع
  • تمرير بيانات الإدخال إلى العمل
  • تجميع الأعمال ذات الصلة معًا باستخدام العلامات

نظرة عامة

يتم تحديد العمل في WorkManager من خلال WorkRequest. لجدولة أيّ عمل باستخدام WorkManager، عليك أولاً إنشاء كائن WorkRequest ثم إدراجه في قائمة الانتظار.

Kotlin


val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)

Java


WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);

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

WorkRequest نفسها هي فئة أساسية مجرّدة. هناك عمليتَا تنفيذ مشتقتان لهذه الفئة يمكنك استخدامهما لإنشاء الطلب، وهما OneTimeWorkRequest وPeriodicWorkRequest. كما تشير أسماؤها، إلى أنّ السمة OneTimeWorkRequest مفيدة لجدولة الأعمال غير المتكررة، في حين أنّ السمة PeriodicWorkRequest أكثر ملاءمة لجدولة الأعمال التي تتكرّر مع مرور فترات زمنية معيّنة.

جدولة عمل لمرة واحدة

بالنسبة إلى الأعمال البسيطة التي لا تتطلّب إعدادات إضافية، استخدِم الطريقة الثابتة from:

Kotlin


val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

Java


WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);

بالنسبة إلى الأعمال الأكثر تعقيدًا، يمكنك استخدام منصة إنشاء:

Kotlin

val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

Java

WorkRequest uploadWorkRequest =
   new OneTimeWorkRequest.Builder(MyWork.class)
       // Additional configuration
       .build();

جدولة العمل المستعجل

قدم WorkManager 2.7.0 مفهوم العمل المعجل. يتيح هذا لـ WorkManager تنفيذ عمل مهم مع منح النظام تحكمًا أفضل في الوصول إلى الموارد.

يتميز العمل السريع بالخصائص التالية:

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

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

الحصص

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

تستند مدة تنفيذ التطبيق المتاحة إلى حزمة الاستعداد وأهمية العملية.

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

تنفيذ العمل المستعجل

بدءًا من WorkManager 2.7، يمكن لتطبيقك الاتصال بـ setExpedited() للإشارة إلى أنّه يجب تشغيل WorkRequest في أسرع وقت ممكن باستخدام مهمة سريعة. يقدم مقتطف الرمز التالي مثالاً على كيفية استخدام setExpedited():

Kotlin

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Java

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build();

في هذا المثال، نُشغِّل مثيل OneTimeWorkRequest ونستدعي setExpedited() عليه. ويصبح هذا الطلب عملاً عاجلاً. إذا سمحت الحصة، سيبدأ تشغيلها على الفور في الخلفية. في حال استخدام الحصة، تشير المَعلمة OutOfQuotaPolicy إلى أنّه يجب تنفيذ الطلب كالمعتاد وغير سريع.

التوافق مع الأنظمة القديمة والخدمات التي تعمل في المقدّمة

للحفاظ على التوافق مع الأنظمة القديمة للمهام التي يتم تسريعها، قد يشغِّل WorkManager خدمة تعمل في المقدّمة على إصدارات النظام الأساسي الأقدم من Android 12. يمكن للخدمات التي تعمل في المقدّمة عرض إشعار للمستخدم.

إنّ طريقتَي getForegroundInfoAsync() وgetForegroundInfo() في Worker تتيح لأداة WorkManager عرض إشعار عند استدعاء setExpedited() قبل إصدار Android 12.

يجب أن تنفّذ أي دالة ListenableWorker طريقة getForegroundInfo إذا كنت تريد طلب تشغيل المهمة كمهمة سريعة.

عند استهداف الإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث، تظل الخدمات التي تعمل في المقدّمة متاحة لك من خلال طريقة setForeground المناسبة.

عامل

لا يعرف العاملون ما إذا كان العمل الذي يقومون به يتم تسريعه أم لا. في المقابل، يمكن للعاملين عرض إشعار على بعض إصدارات Android عند تسريع عملية WorkRequest.

لتفعيل هذه الميزة، يوفّر WorkManager طريقة getForegroundInfoAsync()، التي يجب تنفيذها كي يتمكّن WorkManager من عرض إشعار ليبدأ عنصر ForegroundService نيابةً عنك عند الضرورة.

عامل CoroutineWorker

في حال استخدام CoroutineWorker، يجب تنفيذ getForegroundInfo(). ثم تمريره إلى setForeground() خلال doWork() سيؤدي ذلك إلى إنشاء الإشعار في إصدارات Android الأقدم من الإصدار 12.

بالنظر إلى المثال التالي:

  class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
   CoroutineWorker(appContext, workerParams) {

   override suspend fun getForegroundInfo(): ForegroundInfo {
       return ForegroundInfo(
           NOTIFICATION_ID, createNotification()
       )
   }

   override suspend fun doWork(): Result {
       TODO()
   }

    private fun createNotification() : Notification {
       TODO()
    }

}

سياسات الحصص

يمكنك التحكّم في ما يحدث للعمل المُسرّع عندما يصل تطبيقك إلى حصة التنفيذ المحدّدة. للمتابعة، يمكنك تجاوز setExpedited():

  • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST، والذي يتسبب في تشغيل المهمة كطلب عمل عادي. المقتطف أعلاه يوضّح ذلك.
  • OutOfQuotaPolicy.DROP_WORK_REQUEST، التي تؤدي إلى إلغاء الطلب في حال لم تكن هناك حصة كافية.

نموذج تطبيق

للاطّلاع على مثال كامل لكيفية استخدام WorkManager 2.7.0 للعمل المُسرَّع، يمكنك الاطّلاع على WorkManagerعيّنة على GitHub.

تأجيل العمل المُسرّع

ويحاول النظام تنفيذ مهمة سريعة معينة في أقرب وقت ممكن بعد استدعاء المهمة. ومع ذلك، كما هو الحال مع أنواع الوظائف الأخرى، قد يؤجِّل النظام بدء العمل المُسرّع الجديد، كما هو الحال في الحالات التالية:

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

جدولة العمل الدوري

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

إليك طريقة استخدام PeriodicWorkRequest لإنشاء كائن WorkRequest يتم تنفيذه بشكل دوري:

Kotlin


val saveRequest =
       PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
    // Additional configuration
           .build()

Java


PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
           // Constraints
           .build();

في هذا المثال، تتم جدولة العمل بفاصل ساعة واحدة.

إنّ الفترة الفاصلة هي أدنى فترة تفصل بين التكرارات. ويعتمد الوقت المحدّد الذي سيتم فيه تنفيذ العامل على القيود التي تستخدمها في كائن WorkRequest وعلى التحسينات التي يتم إجراؤها من خلال النظام.

فترات التشغيل المرنة

إذا كانت طبيعة عملك تؤثّر في تشغيل التوقيت، يمكنك ضبط PeriodicWorkRequest بحيث يتم تشغيله خلال فترة مرنة داخل كل فترة فاصلة، كما هو موضّح في الشكل 1.

يمكنك ضبط فاصل مرن لمهمة دورية. عليك تحديد فاصل تكرار وفاصل مرن يحدد قدرًا معيّنًا من الوقت في نهاية الفاصل الزمني للتكرار. يحاول WorkManager تشغيل مهمتك في مرحلة ما
خلال الفاصل الزمني المرن في كل دورة.

الشكل 1. يوضح الرسم التخطيطي فواصل متكررة مع الفترة المرنة التي يمكن تشغيل العمل فيها.

لتحديد العمل الدوري باستخدام فترة مرنة، عليك تمرير flexInterval مع repeatInterval عند إنشاء PeriodicWorkRequest. تبدأ الفترة المرنة في repeatInterval - flexInterval، وتنتقل إلى نهاية الفاصل.

فيما يلي مثال على العمل الدوري الذي يمكن إجراؤه خلال آخر 15 دقيقة من كل ساعة.

Kotlin


val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
       1, TimeUnit.HOURS, // repeatInterval (the period cycle)
       15, TimeUnit.MINUTES) // flexInterval
    .build()

Java


WorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
               1, TimeUnit.HOURS,
               15, TimeUnit.MINUTES)
           .build();

يجب أن تكون الفاصل الزمني للتكرار أكبر من أو يساوي PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS، ويجب أن يكون الفاصل الزمني المرن أكبر من أو يساوي PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS.

تأثير القيود على العمل الدوري

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

قيود العمل

تضمن القيود تأجيل العمل حتى يتم استيفاء الظروف المثلى. تتوفر القيود التالية لـ WorkManager.

NetworkType تقييد نوع الشبكة المطلوب لتشغيل عملك. على سبيل المثال، شبكة Wi-Fi (UNMETERED).
البطارية ليست منخفضة في حال ضبط هذه السياسة على "صحيح"، لن يتم تنفيذ عملك إذا كان الجهاز في وضع البطارية المنخفض.
تتطلّب الشحن وعند ضبطها على "صحيح"، لن يتم تنفيذ عملك إلا أثناء شحن الجهاز.
عدم استخدام الجهاز وعند ضبطها على "صحيح"، يتطلّب ذلك أن يكون جهاز المستخدم غير نشِط لفترة قصيرة قبل بدء العمل. ويمكن أن يكون ذلك مفيدًا لتشغيل عمليات مجمَّعة قد يكون لها تأثير سلبي على الأداء في التطبيقات الأخرى التي تعمل بنشاط على جهاز المستخدم.
مساحة التخزين ليست منخفضة وعند ضبط هذه السياسة على "صحيح"، لن يتم تنفيذ الإجراءات المطلوبة إذا كانت مساحة تخزين المستخدم على الجهاز منخفضة جدًا.

لإنشاء مجموعة من القيود وربطها ببعض الأعمال، يمكنك إنشاء مثيل Constraints باستخدام Contraints.Builder() وتعيينه إلى WorkRequest.Builder().

على سبيل المثال، ينشئ الرمز البرمجي التالي طلب عمل لا يتم تنفيذه إلا عند شحن جهاز المستخدم واستخدام شبكة Wi-Fi معًا:

Kotlin


val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       .setConstraints(constraints)
       .build()

Java


Constraints constraints = new Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresCharging(true)
       .build();

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setConstraints(constraints)
               .build();

عند تحديد قيود متعددة، لن يتم تنفيذ عملك إلا عند استيفاء جميع القيود.

في حال عدم استيفاء أحد القيد أثناء عملك، سيوقف WorkManager العامل. ستتم بعد ذلك إعادة محاولة العمل عند استيفاء جميع القيود.

العمل المتأخر

في حال عدم وجود قيود على عملك أو استيفاء جميع القيود عند وضع عملك في قائمة انتظار، قد يختار النظام تشغيل العمل على الفور. إذا كنت لا تريد تنفيذ العمل على الفور، يمكنك تحديد أن يبدأ عملك بعد حد أدنى من التأخير الأولي.

فيما يلي مثال على كيفية ضبط عملك على التشغيل بعد 10 دقائق على الأقل من وضعه في قائمة الانتظار.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java


WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

يوضّح هذا المثال كيفية تحديد مهلة مبدئية لـ OneTimeWorkRequest، ويمكنك أيضًا ضبط مهلة مبدئية لـ PeriodicWorkRequest. في هذه الحالة، سيتم تأخير عملية التشغيل الأولى لعملك الدوري فقط.

سياسة إعادة المحاولة والتراجع

إذا كنت تفرض على WorkManager إعادة محاولة عملك، يمكنك عرض Result.retry() من العامل. ثم تتم إعادة جدولة عملك وفقًا لتأخير التأخّر وسياسة التراجع.

  • يحدد تأخير التراجع الحد الأدنى لمقدار الانتظار قبل إعادة محاولة عملك بعد المحاولة الأولى. يمكن ألا تقل هذه القيمة عن 10 ثوانٍ (أو MIN_BACKOFF_MILLIS).

  • تحدِّد سياسة التراجع كيفية زيادة تأخير التراجع بمرور الوقت لمحاولات إعادة المحاولة اللاحقة. يتيح WorkManager سياستَي التراجع، هما LINEAR و EXPONENTIAL.

لكل طلب عمل سياسة تراجع وتأخير التراجع. السياسة التلقائية هي EXPONENTIAL مع تأخير 30 ثانية، ولكن يمكنك إلغاء ذلك في إعدادات طلبات العمل.

في ما يلي مثال على تخصيص مهلة التمهّل والسياسة.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                       TimeUnit.MILLISECONDS)
               .build();

في هذا المثال، يتم تعيين الحد الأدنى لمهلة التراجع على أدنى قيمة مسموح بها، وهي 10 ثوانٍ. بما أنّ السياسة LINEAR، سيزداد الفاصل الزمني لإعادة المحاولة بمقدار 10 ثوانٍ تقريبًا مع كل محاولة جديدة. على سبيل المثال، ستتم إعادة محاولة الانتهاء الأولى باستخدام Result.retry() بعد 10 ثوانٍ، يليها 20، 30، 40 وهكذا، إذا استمر العمل Result.retry() بعد المحاولات اللاحقة. في حال ضبط سياسة "التراجع" على EXPONENTIAL، سيكون تسلسل مدة إعادة المحاولة أقرب إلى 20 و40 و80 وهكذا.

عمل العلامة

لكل طلب عمل معرّف فريد يمكن استخدامه لتحديد العمل لاحقًا من أجل إلغاء العمل أو ملاحظة مستوى تقدُّمه.

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

على سبيل المثال، يلغي WorkManager.cancelAllWorkByTag(String) جميع طلبات العمل باستخدام علامة معيّنة، ويعرض WorkManager.getWorkInfosByTag(String) قائمة بكائنات WorkInfo التي يمكن استخدامها لتحديد حالة العمل الحالية.

يوضح الرمز البرمجي التالي كيفية إضافة علامة "cleanup" إلى عملك:

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
       .addTag("cleanup")
       .build();

وأخيرًا، يمكن إضافة علامات متعددة إلى طلب عمل واحد. داخليًا يتم تخزين هذه العلامات كمجموعة من السلاسل. للحصول على مجموعة العلامات المرتبطة بـ WorkRequest، يمكنك استخدام WorkInfo.getTags().

من صف Worker، يمكنك استرداد مجموعة العلامات الخاصة به من خلال ListenableWorker.getTags().

تحديد بيانات الإدخال

قد يتطلب عملك إدخال بيانات للقيام بعمله. على سبيل المثال، قد يتطلب العمل على الأسماء المعرِّفة لتحميل صورة أن يتم تحميل معرف الموارد المنتظم (URI) للصورة كإدخال.

يتم تخزين قيم الإدخال كأزواج المفتاح/القيمة في عنصر Data ويمكن ضبطها في طلب العمل. سيقوم WorkManager بتسليم المدخلات Data إلى عملك عند تنفيذه للعمل. يمكن للفئة Worker الوصول إلى وسيطات الإدخال من خلال استدعاء Worker.getInputData(). يوضح الرمز أدناه كيفية إنشاء مثيل Worker يتطلب إدخال بيانات وكيفية إرسالها في طلب العمل.

Kotlin


// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
   : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       val imageUriInput =
           inputData.getString("IMAGE_URI") ?: return Result.failure()

       uploadFile(imageUriInput)
       return Result.success()
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
   .setInputData(workDataOf(
       "IMAGE_URI" to "http://..."
   ))
   .build()

Java


// Define the Worker requiring input
public class UploadWork extends Worker {

   public UploadWork(Context appContext, WorkerParameters workerParams) {
       super(appContext, workerParams);
   }

   @NonNull
   @Override
   public Result doWork() {
       String imageUriInput = getInputData().getString("IMAGE_URI");
       if(imageUriInput == null) {
           return Result.failure();
       }

       uploadFile(imageUriInput);
       return Result.success();
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
      new OneTimeWorkRequest.Builder(UploadWork.class)
           .setInputData(
               new Data.Builder()
                   .putString("IMAGE_URI", "http://...")
                   .build()
           )
           .build();

وبالمثل، يمكن استخدام الفئة Data لإخراج قيمة معروضة. يتم تناول بيانات الإدخال والمخرج بمزيد من التفاصيل في القسم معلمات الإدخال والقيم التي تم إرجاعها.

الخطوات التالية

في صفحة الحالات والملاحظة، ستتعرف على المزيد من المعلومات حول حالات العمل وكيفية مراقبة تقدم عملك.