تناول دليل البدء كيفية إنشاء 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>()
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build()
WorkManager.getInstance(context)
.enqueue(request)
Java
OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
.setInputData(inputData)
<b>.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)</b>
.build();
في هذا المثال، نضبط مثيلاً من OneTimeWorkRequest ونستدعي setExpedited() عليه. يصبح هذا الطلب بعد ذلك مهمة مُعجَّلة. إذا كانت الحصة تسمح بذلك، سيبدأ تشغيله على الفور في الخلفية. إذا تم استخدام الحصة، تشير المَعلمة OutOfQuotaPolicy إلى ضرورة تشغيل الطلب كعمل عادي غير مُعجَّل.
التوافق مع الإصدارات السابقة والخدمات التي تعمل في المقدّمة
للحفاظ على التوافق مع الإصدارات السابقة للمهام المُعجَّلة، قد يشغّل WorkManager خدمة تعمل في المقدّمة على إصدارات المنصة الأقدم من Android 12. يمكن للخدمات التي تعمل في المقدّمة عرض إشعار للمستخدم.
تسمح الطريقتان getForegroundInfoAsync() وgetForegroundInfo() في الفئة Worker لـ WorkManager بعرض إشعار عند استدعاء setExpedited() قبل Android 12.
يجب أن تنفّذ أي ListenableWorker طريقة getForegroundInfo إذا كنت تريد طلب تشغيل المهمة كمهمة مُعجَّلة.
عند استهداف Android 12 أو الإصدارات الأحدث، تظل الخدمات التي تعمل في المقدّمة متاحة لك من خلال طريقة setForeground المقابلة.
عامل
لا تعرف الفئات Worker ما إذا كان العمل الذي تؤدّيه مُعجَّلاً أم لا. ولكن يمكن للفئات Worker عرض إشعار على بعض إصدارات 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، ما يؤدي إلى إلغاء الطلب إذا لم تكن هناك حصة كافية.
العمل المُعجَّل المؤجَّل
يحاول النظام تنفيذ مهمة مُعجَّلة معيّنة بأسرع وقت ممكن بعد استدعاء المهمة. ومع ذلك، كما هو الحال مع أنواع المهام الأخرى، قد يؤجّل النظام بدء مهمة مُعجَّلة جديدة، مثل الحالات التالية:
- الحِمل: يكون حِمل النظام مرتفعًا جدًا، ما قد يحدث عندما يكون عدد كبير جدًا من المهام قيد التشغيل ، أو عندما لا يكون لدى النظام ذاكرة كافية.
- الحصة: تم تجاوز الحد الأقصى لحصة المهمة المُعجَّلة. تستخدم المهمة المُعجَّلة نظام حصص يستند إلى مجموعات تطبيقات وضع الاستعداد ويحدّ من الحد الأقصى لوقت التنفيذ ضمن نافذة زمنية متجددة. تكون الحصص المستخدَمة للمهام المُعجَّلة أكثر تقييدًا من الحصص المستخدَمة لأنواع المهام الأخرى التي تعمل في الخلفية.
جدولة العمل المتكرّر
قد يتطلّب تطبيقك أحيانًا تشغيل عمل معيّن بشكل دوري. على سبيل المثال، قد تريد إجراء نُسخ احتياطية من بياناتك بشكل دوري، أو تنزيل محتوى جديد في تطبيقك، أو تحميل السجلّات إلى خادم.
إليك كيفية استخدام 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();
في هذا المثال، تتم جدولة العمل بفترة زمنية تبلغ ساعة واحدة.
يتم تحديد الفترة الزمنية على أنّها الحد الأدنى من الوقت بين التكرارات. يعتمد الوقت الدقيق لتنفيذ الفئة Worker على القيود التي تستخدمها في كائن WorkRequest وعلى التحسينات التي يجريها النظام.
فواصل زمنية مرنة للتشغيل
إذا كان طبيعة عملك حساسة لوقت التشغيل، يمكنك ضبط
PeriodicWorkRequest لتشغيله ضمن فترة
مرنة داخل كل فترة زمنية، كما هو موضّح في الشكل 1.

الشكل 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 إلى أن يتم استيفاء هذا الشرط. قد يؤدي ذلك إلى تأخير تشغيل عملك بشكل معيّن، أو حتى تخطّيه إذا لم يتم استيفاء الشروط خلال الفاصل الزمني للتشغيل.
قيود العمل
Constraints تضمن تأجيل العمل إلى أن يتم استيفاء الظروف المثالية. تتوفّر القيود التالية لـ WorkManager:
| NetworkType | يفرض هذا القيد نوع الشبكة المطلوبة لتشغيل عملك.
على سبيل المثال، شبكة Wi-Fi (UNMETERED)
|
| BatteryNotLow | عند ضبط هذه القيمة على "صحيح"، لن يتم تشغيل عملك إذا كان الجهاز في وضع البطارية المنخفضة. |
| RequiresCharging | عند ضبط هذه القيمة على "صحيح"، لن يتم تشغيل عملك إلا عندما يكون الجهاز قيد الشحن. |
| DeviceIdle | عند ضبط هذه القيمة على "صحيح"، يتطلّب ذلك أن يكون جهاز المستخدم في وضع عدم النشاط قبل تشغيل العمل. يمكن أن يكون هذا مفيدًا لتشغيل العمليات المجمّعة التي قد يكون لها تأثير سلبي على أداء التطبيقات الأخرى التي تعمل بنشاط على جهاز المستخدم. |
| StorageNotLow | عند ضبط هذه القيمة على "صحيح"، لن يتم تشغيل عملك إذا كانت مساحة التخزين المتاحة للمستخدم على الجهاز منخفضة جدًا. |
لإنشاء مجموعة من القيود وربطها ببعض الأعمال، عليك إنشاء مثيل
Constraints باستخدام Constraints.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 الفئة Worker. بعد ذلك، ستتم إعادة محاولة تشغيل العمل عندما يتم استيفاء جميع القيود.
العمل المؤجَّل
في حال عدم وجود قيود على عملك أو استيفاء جميع القيود عند إضافة عملك إلى قائمة الانتظار، قد يختار النظام تشغيل العمل على الفور. إذا كنت لا تريد تشغيل العمل على الفور، يمكنك تحديد بدء عملك بعد حد أدنى من التأخير الأولي.
في ما يلي مثال على كيفية ضبط عملك ليتم تشغيله بعد 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() من الفئة Worker. بعد ذلك، تتم إعادة جدولة عملك وفقًا لتأخير التراجع وسياسة التراجع.
يحدّد تأخير التراجع الحد الأدنى من الوقت الذي يجب الانتظار فيه قبل إعادة محاولة تشغيل عملك بعد المحاولة الأولى. يجب ألا تقل هذه القيمة عن 10 ثوانٍ (أو MIN_BACKOFF_MILLIS).
تحدّد سياسة التراجع كيفية زيادة تأخير التراجع بمرور الوقت لمحاولات إعادة المحاولة اللاحقة. تتوافق WorkManager مع سياستَي تراجع، هما
LINEARوEXPONENTIAL.
لكل طلب عمل سياسة تراجع وتأخير تراجع. السياسة التلقائية هي EXPONENTIAL مع تأخير يبلغ 30 ثانية، ولكن يمكنك إلغاء ذلك في إعدادات طلب العمل.
في ما يلي مثال على تخصيص تأخير التراجع والسياسة:
Kotlin
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
Java
WorkRequest myWorkRequest =
new OneTimeWorkRequest.Builder(MyWork.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
WorkRequest.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 التي يمكن استخدامها لتحديد حالة العمل الحالية.
يوضّح الرمز التالي كيفية إضافة علامة "تنظيف" إلى عملك:
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 لعرض قيمة الإرجاع.
الخطوات التالية
في صفحة الحالات والمراقبة، ستتعرّف على مزيد من المعلومات عن حالات العمل وكيفية مراقبة تقدّم عملك.