Android Kotlin Fundamentals 09.2: WorkManager

يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات Android Kotlin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لدروس الترميز Android Kotlin Fundamentals.

مقدمة

تحتاج معظم التطبيقات الحقيقية إلى تنفيذ مهام طويلة الأمد في الخلفية. على سبيل المثال، قد يحمّل التطبيق ملفات إلى خادم أو يزامن البيانات من خادم ويحفظها في قاعدة بيانات Room أو يرسل سجلات إلى خادم أو ينفذ عمليات باهظة التكلفة على البيانات. يجب إجراء هذه العمليات في الخلفية، خارج سلسلة محادثات واجهة المستخدم (سلسلة المحادثات الرئيسية). تستهلك المهام في الخلفية موارد محدودة للجهاز، مثل ذاكرة الوصول العشوائي والبطارية. وقد يؤدي ذلك إلى تجربة سيئة للمستخدم إذا لم تتم معالجته بشكل صحيح.

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

ما يجب معرفته

  • كيفية استخدام ViewModel وLiveData ومكوّنات Android Room.
  • كيفية إجراء تغييرات على صف LiveData.
  • كيفية إنشاء كوروتين وإطلاقه
  • كيفية استخدام محوّلات الربط في ربط البيانات.
  • كيفية تحميل بيانات ذاكرة التخزين المؤقت باستخدام نمط مستودع.

ما ستتعرَّف عليه

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

الإجراءات التي ستنفذّها

  • يمكنك إنشاء مشغّل لتنفيذ مهمة في الخلفية من أجل جلب قائمة تشغيل الفيديو DevBytes من الشبكة مسبقًا.
  • جدوِل تشغيل العمّال بشكل دوري.
  • يمكنك إضافة قيود على WorkRequest.
  • يمكنك جدولة WorkRequest دورية يتم تنفيذها مرة واحدة في اليوم.

في هذا الدرس التطبيقي حول الترميز، تعمل على تطبيق DevBytes الذي طوّرته في درس تطبيقي سابق حول الترميز. (إذا لم يكن لديك هذا التطبيق، يمكنك تنزيل رمز بدء الدرس.)

يعرض تطبيق DevBytes قائمة فيديوهات DevByte، وهي فيديوهات تعليمية قصيرة أنشأها فريق علاقات مطوّري برامج Google لنظام التشغيل Android. تقدم الفيديوهات ميزات مطوّري البرامج وأفضل الممارسات لتطوير Android.

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

في هذه المهمة، يمكنك تنزيل رمز البدء وفحصه.

الخطوة 1: تنزيل تطبيق بدء التشغيل وتشغيله

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

في هذه المَهمة، نزِّل تطبيق إجراء التفعيل وتحقَّق من رمز إجراء التفعيل.

  1. إذا لم يكن لديك تطبيق DevBytes من قبل، نزِّل رمز DevBytes للمبتدئين في هذا الدرس التطبيقي من DevBytesRepository من GitHub.
  2. فكّ ضغط الرمز وافتح المشروع في Android Studio.
  3. أصلح جهازك الاختباري أو المحاكي بالإنترنت إذا لم يكن متصلاً من قبل. إنشاء التطبيق وتشغيله، يجلب التطبيق قائمة فيديوهات DevByte من الشبكة ويعرضها.
  4. في التطبيق، انقر على أي فيديو لفتحه في تطبيق YouTube.

الخطوة 2: استكشاف الرمز

يتضمّن تطبيق المبتدئين الكثير من الرموز التي تمّ تقديمها في الدرس التطبيقي السابق حول الترميز. يتضمّن رمز بدء الدرس التطبيقي هذا الرمز وتم تخصيص واجهة مستخدم وذاكرة تخزين مؤقت بلا اتصال بالإنترنت ووحدات تخزين. يمكنك التركيز على جدولة مهمة الخلفية باستخدام WorkManager.

  1. في "استوديو Android"، وسِّع كل الحِزم.
  2. استكشِف حزمة database. تحتوي الحزمة على كيانات قاعدة البيانات وقاعدة البيانات المحلية التي يتم تنفيذها باستخدام Room.
  3. استكشِف حزمة repository. تحتوي الحزمة على فئة VideosRepository التي تستخرج طبقة البيانات من بقية التطبيق.
  4. اكتشف بقية رموز إجراء التفعيل بنفسك وبمساعدة الدرس التطبيقي السابق.

WorkManager هي أحد مكوّنات Android الأساسية وجزء من Android Jetpack. WorkManager مخصّص للعمل في الخلفية الذي يمكن تأجيله ويتطلب التنفيذ المضمون:

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

وعلى الرغم من أنّ نظام WorkManager يُشغِّل العمل في الخلفية، فإنه يتولى مشاكل التوافق وأفضل الممارسات لسلامة البطارية والنظام. يوفر WorkManager التوافق مع المستوى 14 لواجهة برمجة التطبيقات. سيختار WorkManager طريقة مناسبة لجدولة مهمة في الخلفية، بناءً على مستوى واجهة برمجة تطبيقات الجهاز. وقد يستخدم الإصدار JobScheduler (على الإصدار 23 والإصدارات الأحدث من واجهة برمجة التطبيقات) أو مزيجًا من AlarmManager وBroadcastReceiver.

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

في هذا الدرس التطبيقي حول الترميز، يمكنك جدولة مهمة للحصول مسبقًا على قائمة تشغيل الفيديو DevBytes من الشبكة مرة واحدة في اليوم. لجدولة هذه المهمة، يمكنك استخدام مكتبة WorkManager.

  1. افتح ملف build.gradle (Module:app) وأضِف اعتمادية WorkManager إلى المشروع.

    إذا كنت تستخدم أحدث إصدار من المكتبة، يجب أن يجمع تطبيق الحل كما هو متوقع. وإذا لم يحدث ذلك، جرِّب حل المشكلة، أو يمكنك الرجوع إلى إصدار المكتبة الموضَّح أدناه.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. عليك مزامنة مشروعك والتأكُّد من عدم حدوث أخطاء في التجميع.

قبل إضافة الرمز إلى المشروع، اطّلِع على الصفوف التالية في مكتبة WorkManager:

  • Worker
    هذا الصف هو المكان الذي تحدِّد فيه العمل الفعلي (المهمة) الذي سيتم تشغيله في الخلفية. يمكنك تمديد هذه الفئة وإلغاء طريقة doWork(). والطريقة doWork() هي المكان الذي يمكنك من خلاله وضع رمز لتنفيذه في الخلفية، مثل مزامنة البيانات مع الخادم أو معالجة الصور. لقد نفّذت Worker في هذه المهمة.
  • WorkRequest
    يمثّل هذا الصف طلبًا لتشغيل العاملين في الخلفية. استخدِم WorkRequest لضبط كيفية تشغيل مهمة المشغِّل ووقتها، وذلك بمساعدة Constraints، مثل توصيل الجهاز أو اتصاله بشبكة Wi-Fi. لقد نفّذت WorkRequest في مهمة لاحقة.
  • WorkManager
    يعمل هذا الصف على تحديد موعد WorkRequest وتشغيله. WorkManager هو جدول زمني لطلبات العمل بطريقة تنشر الحِمل على موارد النظام مع الالتزام بالقيود التي تحدّدها. لقد نفّذت WorkManager في مهمة لاحقة.

الخطوة 1: إنشاء مشغّل

في هذه المهمة، يمكنك إضافة Worker لجلب قائمة تشغيل الفيديو DevBytes مسبقًا في الخلفية.

  1. داخل حزمة devbyteviewer، أنشِئ حزمة جديدة باسم work.
  2. داخل الحزمة work، أنشئ فئة Kotlin جديدة باسم RefreshDataWorker.
  3. تمديد الصف الدراسي RefreshDataWorker من الصف CoroutineWorker. أدخِل context وWorkerParameters كمعلّمات وضع.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. لحل خطأ الفئة المجرّدة، يمكنك إلغاء طريقة doWork() داخل الصف RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

دالة التعليق هي وظيفة يمكن إيقافها مؤقتًا واستئنافها لاحقًا. يمكن أن تنفّذ وظيفة التعليق عملية تشغيل طويلة وتنتظر اكتمال العملية بدون حظر سلسلة المحادثات الرئيسية.

الخطوة 2: تنفيذ DoWork()

يتم استدعاء طريقة doWork() داخل الفئة Worker في سلسلة محادثات في الخلفية. تعمل الطريقة بشكل متزامن، ومن المفترض أن تعرض كائن ListenableWorker.Result. يمنح نظام Android Worker مدة 10 دقائق كحد أقصى لإكمال تنفيذه وعرض كائن ListenableWorker.Result. بعد انتهاء هذه الفترة، سيوقف النظام فرض Worker.

لإنشاء عنصر ListenableWorker.Result، يمكنك استدعاء إحدى الطرق الثابتة التالية للإشارة إلى حالة اكتمال العمل في الخلفية:

في هذه المَهمّة، يمكنك استخدام طريقة doWork() لجلب قائمة تشغيل الفيديو من شبكة DevBytes من الشبكة. يمكنك إعادة استخدام الطرق الحالية في الفئة VideosRepository لاسترداد البيانات من الشبكة.

  1. في الصف RefreshDataWorker، داخل doWork()، يمكنك إنشاء عنصر VideosDatabase وكائن VideosRepository.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. في الصف RefreshDataWorker، داخل doWork()، فوق عبارة return، عليك استدعاء الطريقة refreshVideos() في الكتلة try. إضافة سجل لتتبع وقت تشغيل العامل.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

لحل الخطأ "&&;;لم يتم حلها" كمرجع &&;;; استيراد retrofit2.HttpException".

  1. في ما يلي الصف RefreshDataWorker الكامل كمرجع لك:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

يحدّد Worker وحدة عمل، ويحدّد WorkRequest كيفية تنفيذ العمل ووقته. هناك تطبيقان ملموسان للصف WorkRequest:

  • الصف OneTimeWorkRequest مخصّص للمهام الفردية. (تحدث مهمة مرة واحدة مرة واحدة فقط.)
  • إنّ صف PeriodicWorkRequest مخصّص للعمل بشكل دوري، وهو متكرّر على فترات زمنية.

يمكن أن تكون المهام فردية أو دورية، لذلك اختر الصف وفقًا لذلك. لمزيد من المعلومات حول جدولة عمل متكرّر، يُرجى الاطّلاع على مستندات العمل المتكرّرة.

في هذه المهمة، يمكنك تحديد WorkRequest وجدولتها لتشغيل عامل التشغيل الذي أنشأته في المهمة السابقة.

الخطوة 1: إعداد عمل متكرّر

ضمن تطبيق Android، تكون الفئة Application هي الفئة الأساسية التي تحتوي على جميع المكونات الأخرى، مثل الأنشطة والخدمات. عند إنشاء عملية التطبيق أو الحزمة، يتم إنشاء مثيل للفئة Application (أو أي فئة فرعية من Application) قبل أي فئة أخرى.

في هذا النموذج للتطبيق، الفئة DevByteApplication هي فئة فرعية من الصف Application. يمثّل الصف DevByteApplication مكانًا مناسبًا لتحديد موعد WorkManager.

  1. في الصف DevByteApplication، أنشِئ طريقة تُسمى setupRecurringWork() لإعداد العمل في الخلفية المتكرّر.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. داخل طريقة setupRecurringWork()، يمكنك إنشاء طلب عمل دوري وإعداده مرة واحدة في اليوم، باستخدام طريقة PeriodicWorkRequestBuilder(). سجّل في صف RefreshDataWorker الذي أنشأته في المهمة السابقة. تمرّر فاصلاً متكررًا مدته 1 مع وحدة زمنية من TimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

لإصلاح الخطأ، استورِد java.util.concurrent.TimeUnit.

الخطوة 2: جدولة WorkRequest مع WorkManager

بعد تعريف WorkRequest، يمكنك جدولته مع WorkManager، باستخدام طريقة enqueueUniquePeriodicWork(). تسمح لك هذه الطريقة بإضافة PeriodicWorkRequest يحمل اسمًا فريدًا إلى قائمة الانتظار، حيث لا يمكن تفعيل أكثر من PeriodicWorkRequest واحد من الاسم نفسه في كل مرة.

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

لمزيد من المعلومات حول طرق تحديد موعد WorkRequest، يُرجى الاطّلاع على مستندات WorkManager.

  1. في الصف RefreshDataWorker، أضِف كائنًا مصاحبًا في بداية الصف. حدِّد اسم عمل لتحديد هذا العامل بشكلٍ فريد.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. في الصف DevByteApplication، في نهاية طريقة setupRecurringWork()، يمكنك جدولة العمل باستخدام الطريقة enqueueUniquePeriodicWork(). أدخِل تعداد KEEP لـ CurrentPeriodicWorkPolicy. أدخِل repeatingRequest كمَعلمة PeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

في حال توفر عمل في انتظار المراجعة (غير مكتمل) بالاسم نفسه، ستعمل المعلمة ExistingPeriodicWorkPolicy.KEEP على إبقاء العمل الدائري السابق WorkManager وتجاهل طلب العمل الجديد.

  1. في بداية الصف DevByteApplication، أنشئ كائن CoroutineScope. أدخِل Dispatchers.Default كمعلَمة طريقة الإنشاء.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. في الصف DevByteApplication، أضِف طريقة جديدة تُسمّى delayedInit() لبدء كوروتين.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. اتّبِع الرقم setupRecurringWork() داخل طريقة delayedInit().
  2. حرِّك إعداد الأخشاب من طريقة onCreate() إلى طريقة delayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. في الصف DevByteApplication، في نهاية طريقة onCreate()، أضف استدعاءً للطريقة delayedInit().
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. افتح الجزء Logcat في أسفل نافذة Android Studio. الفلترة على RefreshDataWorker
  2. شغِّل التطبيق. وحدِّد التطبيق WorkManager جدول الأعمال المتكررة على الفور.

    في جزء Logcat، لاحِظ بيانات السجلّ التي تشير إلى جدولة طلب العمل، ثم يتم تشغيلها بنجاح.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

يتم عرض سجل WM-WorkerWrapper من مكتبة WorkManager، لذلك لا يمكنك تغيير رسالة السجل هذه.

الخطوة 3: (اختياري) جدولة أداة WorkRequest للحصول على الحد الأدنى من الفاصل الزمني

في هذه الخطوة، عليك تقليل الفاصل الزمني من يوم واحد إلى 15 دقيقة. ويمكنك إجراء ذلك حتى تتمكن من الاطلاع على السجلات لطلب عمل دوري.

  1. في الصف DevByteApplication، ضمن طريقة setupRecurringWork()، علِّق تعريف repeatingRequest الحالي. أضِف طلب عمل جديدًا بفاصل زمني متكرر متكرر مدته 15 دقيقة.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. افتح لوحة Logcat في "استوديو Android" وفلترًا في RefreshDataWorker. لمحو السجلات السابقة، انقر على رمز محو السجلّات .
  2. شغِّل التطبيق وسيحدِّد تطبيق WorkManager عملك المتكرّر على الفور. في لوحة Logcat، لاحظ السجلات: يتم تشغيل طلب العمل مرة واحدة كل 15 دقيقة. انتظِر 15 دقيقة للاطِّلاع على مجموعة أخرى من سجلّات طلبات العمل. يمكنك ترك التطبيق قيد التشغيل أو إغلاقه، علمًا بأنّه يجب أن يعمل مدير العمل.

    يُرجى العِلم أنّ الفاصل الزمني يكون في بعض الأحيان أقل من 15 دقيقة، وأحيانًا أكثر من 15 دقيقة. (يخضع التوقيت الدقيق لعمليات تحسين بطارية نظام التشغيل.)
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

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

في المهمة التالية، يمكنك معالجة هذه المشكلة عن طريق إضافة قيود.

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

عند تحديد WorkRequest، يمكنك وضع قيود على الحالات التي يجب فيها تشغيل Worker. على سبيل المثال، قد تريد تحديد عدم تشغيل العمل إلا عندما يكون الجهاز غير نشِط لفترة قصيرة، أو فقط عندما يكون الجهاز متصلاً بشبكة Wi-Fi. ويمكنك أيضًا تحديد سياسة التراجع عن إعادة محاولة العمل. القيود المتوافقة هي الطرق المحدَّدة في Constraints.Builder. لمعرفة مزيد من المعلومات، يُرجى الاطِّلاع على تحديد طلبات العمل.

الخطوة 1: إضافة كائن قيود ووضع قيود واحدة

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

  1. في الصف DevByteApplication، في بداية setupRecurringWork()، حدِّد val من النوع Constraints. استخدِم الطريقة Constraints.Builder().
val constraints = Constraints.Builder()

لإصلاح الخطأ، استورِد androidx.work.Constraints.

  1. استخدِم الطريقة setRequiredNetworkType() لإضافة قيد من نوع الشبكة إلى العنصر constraints. يمكنك استخدام تعداد UNMETERED بحيث لا يتم تشغيل طلب العمل إلا عندما يكون الجهاز متصلاً بشبكة غير تفرض تكلفة استخدام.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. استخدام الطريقة build() لإنشاء القيود من أداة الإنشاء.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

عليك الآن ضبط الكائن Constraints الذي تم إنشاؤه حديثًا على طلب العمل.

  1. في الصف DevByteApplication، داخل طريقة setupRecurringWork()، اضبط الكائن Constraints على طلب العمل الدوري repeatingRequest. لضبط القيود، أضِف طريقة setConstraints() أعلى استدعاء طريقة build().
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

الخطوة 2: تشغيل التطبيق وملاحظة السجلات

في هذه الخطوة، يمكنك تشغيل التطبيق وملاحظة تنفيذ طلب العمل المحدود في الخلفية على فترات زمنية.

  1. ألغِ تثبيت التطبيق من الجهاز أو المحاكي لإلغاء أي مهام مجدولة مسبقًا.
  2. افتح لوحة Logcat في "استوديو Android". في جزء Logcat (محو سجلّات)، عليك محو السجلّات السابقة بالنقر على رمز محو سجلّات السجلّ () على يمين الصفحة. الفلترة على work
  3. أوقِف شبكة Wi-Fi في الجهاز أو المحاكي حتى تتمكّن من معرفة آلية عمل القيود. ويحدّد الرمز الحالي قيدًا واحدًا فقط، ما يشير إلى ضرورة تشغيل الطلب على شبكة غير تفرض تكلفة استخدام. نظرًا لأن شبكة Wi-Fi متوقفة، فإن الجهاز ليس متصلاً بالشبكة أو فيه قياس أو قياسه. وبالتالي، لن يتم الوفاء بهذه القيود.
  4. شغِّل التطبيق ولاحظ لوحة Logcat (لوحة السجل). تُحدِّد الخاصية WorkManager مهمة الخلفية على الفور. وبسبب عدم استيفاء قيد الشبكة، لن يتم تنفيذ المهمة.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. شغِّل شبكة Wi-Fi في الجهاز أو المحاكي وشاهد لوحة Logcat. يتم الآن تشغيل مهمة الخلفية المجدولة كل 15 دقيقة تقريبًا، طالما تم استيفاء قيد الشبكة.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

الخطوة 3: إضافة المزيد من القيود

في هذه الخطوة، يمكنك إضافة القيود التالية إلى PeriodicWorkRequest:

  • مستوى شحن البطارية منخفض.
  • جارٍ شحن الجهاز.
  • الجهاز غير نشِط لفترة قصيرة، ولا يتوفّر إلا في مستوى واجهة برمجة التطبيقات 23 (Android M) والإصدارات الأحدث.

نفِّذ ما يلي في صف DevByteApplication.

  1. في الصف DevByteApplication، وضمن طريقة setupRecurringWork()، أشِر إلى أن طلب العمل يجب أن يعمل فقط إذا لم تكن طاقة البطارية منخفضة. أضِف القيود قبل استدعاء طريقة build()، واستخدِم الطريقة setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. يمكنك تعديل طلب العمل ليتم تشغيله فقط عندما يكون الجهاز قيد الشحن. أضِف القيود قبل استدعاء طريقة build()، واستخدِم الطريقة setRequiresCharging().
.setRequiresCharging(true)
  1. يمكنك تعديل طلب العمل ليتم تشغيله فقط عندما يكون الجهاز في وضع عدم النشاط. أضِف القيود قبل استدعاء طريقة build()، واستخدِم الطريقة setRequiresDeviceIdle(). يشغِّل هذا القيد طلب العمل فقط عندما لا يستخدم المستخدم الجهاز بشكل نشط. لا تتوفر هذه الميزة إلا على الإصدار Android 6.0 (Marshmallow) والإصدارات الأحدث، لذا أضِف شرطًا لإصدار SDK M والإصدارات الأحدث.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

في ما يلي التعريف الكامل للكائن constraints.

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. داخل طريقة setupRecurringWork()، غيِّر الفاصل الزمني للطلب مرة واحدة في اليوم.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

في ما يلي التنفيذ الكامل لطريقة setupRecurringWork()، باستخدام سجلّ يمكنك تتبّعه عند جدولة طلب العمل الدوري.

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. لإزالة طلب العمل المُجدوَل سابقًا، يجب إلغاء تثبيت تطبيق DevBytes من جهازك أو من أداة المحاكاة.
  2. شغِّل التطبيق وسيجدون WorkManager على الفور طلب العمل. يتم تشغيل طلب العمل مرة واحدة في اليوم، عند استيفاء جميع القيود.
  3. سيتم تشغيل طلب العمل هذا في الخلفية طالما كان التطبيق مثبتًا، حتى في حال عدم تشغيل التطبيق. ولهذا السبب، يجب إلغاء تثبيت التطبيق من الهاتف.

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

مشروع "استوديو Android": DevBytesWorkManager

  • تسهِّل واجهة برمجة تطبيقات WorkManager جدولة المهام المؤجلة وغير المتزامنة التي يجب تشغيلها بشكلٍ موثوق.
  • تحتاج معظم التطبيقات الحقيقية إلى تنفيذ مهام طويلة الأمد في الخلفية. لجدولة مهمة في الخلفية بطريقة فعّالة وفعالة، استخدِم WorkManager.
  • الصفوف الرئيسية في مكتبة WorkManager هي Worker وWorkRequest وWorkManager.
  • يمثل الصف Worker وحدة عمل. لتنفيذ مهمة الخلفية، وسِّع الفئة Worker وتجاوز طريقة doWork().
  • تمثّل الفئة WorkRequest طلبًا لتنفيذ وحدة عمل. WorkRequest هي الفئة الأساسية لتحديد معلّمات العمل الذي جدولته في WorkManager.
  • هناك تطبيقان ملموسان للصف WorkRequest: OneTimeWorkRequest للمهام الفردية، وPeriodicWorkRequest لطلبات العمل الدورية.
  • عند تحديد WorkRequest، يمكنك تحديد Constraints للإشارة إلى وقت تشغيل Worker. تشمل القيود عناصر مثل ما إذا كان الجهاز متصلاً، أو ما إذا كان الجهاز غير نشِط لفترة قصيرة، أو ما إذا كان اتصال Wi-Fi متصلاً.
  • لإضافة قيود على WorkRequest، استخدِم الطرق المحدَّدة في مستندات Constraints.Builder. مثلاً، للإشارة إلى ضرورة عدم تشغيل WorkRequest إذا كانت بطارية الجهاز منخفضة، استخدِم الطريقة setRequiresBatteryNotLow() المحدَّدة.
  • بعد تحديد WorkRequest، عليك تسليم المهمة إلى نظام Android. ولإجراء ذلك، يمكنك جدولة المهمة باستخدام إحدى طرقenqueue WorkManager.
  • يعتمد الوقت الدقيق لتنفيذ Worker على القيود المستخدمة في WorkRequest وعلى تحسينات النظام. تم تصميم WorkManager لتقديم أفضل سلوك ممكن في ظل هذه القيود.

دورة Udacity:

مستندات مطوّر برامج Android:

غير ذلك:

يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:

  • يمكنك تخصيص واجب منزلي إذا لزم الأمر.
  • التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
  • وضع درجات للواجبات المنزلية.

ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.

إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.

السؤال 1

ما هي عمليات التنفيذ الملموسة للصف WorkRequest؟

OneTimeWorkPeriodicRequest

OneTimeWorkRequest وPeriodicWorkRequest

OneTimeWorkRequest وRecurringWorkRequest

OneTimeOffWorkRequest وRecurringWorkRequest

السؤال 2

أي من الصفوف التالية يستخدمها WorkManager لجدولة مهمة الخلفية على واجهة برمجة التطبيقات 23 والإصدارات الأحدث؟

▢ فقط JobScheduler

BroadcastReceiver وAlarmManager

AlarmManager وJobScheduler

Scheduler وBroadcastReceiver

السؤال 3

ما هي واجهة برمجة التطبيقات التي تستخدمها لإضافة قيود إلى WorkRequest؟

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

انتقل إلى الدرس التالي: 10.1 الأنماط والمظاهر

وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.