نقل بيانات الخدمات التي تعمل في المقدّمة إلى مهام نقل البيانات التي يبدؤها المستخدم

نظام التشغيل Android 14 يطبّق قواعد صارمة بشأن الحالات التي يُسمح فيها للتطبيقات باستخدام الخدمات التي تعمل في المقدّمة.

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

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

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

إذن لمهام نقل البيانات التي بدأها المستخدم

تتطلب مهام نقل البيانات التي يبدأها المستخدم إذنًا جديدًا لتشغيل: RUN_USER_INITIATED_JOBS. يمنح النظام هذا الإذن تلقائيًا. يعرض النظام SecurityException إذا لم تتم الإشارة إلى الإذن في ملف بيان التطبيق.

عملية جدولة مهام نقل البيانات التي بدأها المستخدم

لتشغيل مهمة بدأها المستخدم، اتّبِع الخطوات التالية:

  1. إذا كانت هذه هي المرة الأولى التي تُعلن فيها عن واجهة برمجة تطبيقات باستخدام JobScheduler، يُرجى تعريف JobService والأذونات المرتبطة بها في ملف البيان. حدِّد أيضًا فئة فرعية محدّدة من JobService لنقل البيانات:

    <service android:name="com.example.app.CustomTransferService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="false">
            ...
    </service>
    
    class CustomTransferService : JobService() {
      ...
    }
    
  2. يُرجى تعريف إذن "RUN_USER_INITIATED_JOBS" في البيان:

    <manifest ...>
        <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />
        <application ...>
            ...
        </application>
    </manifest>
    
  3. يمكنك استدعاء طريقة setUserInitiated() الجديدة عند إنشاء كائن JobInfo. ننصحك أيضًا بتقديم تقدير لحجم الحمولة من خلال استدعاء setEstimatedNetworkBytes() أثناء إنشاء وظيفتك:

    val networkRequestBuilder = NetworkRequest.Builder()
            .addCapability(NET_CAPABILITY_INTERNET)
            .addCapability(NET_CAPABILITY_NOT_METERED)
            // Add or remove capabilities based on your requirements
            .build()
    
    val jobInfo = JobInfo.Builder()
            // ...
            .setUserInitiated(true)
            .setRequiredNetwork(networkRequestBuilder.build())
            .setEstimatedNetworkBytes(1024 * 1024 * 1024)
            // ...
            .build()
    
  4. حدِّد موعد المهمة قبل بدء عملية النقل، بينما يكون الطلب مرئيًا أو في قائمة الشروط المسموح بها:

    val jobScheduler: JobScheduler =
        context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    jobScheduler.schedule(jobInfo)
    
  5. عند تنفيذ المهمة، عليك التأكّد من طلب الرمز setNotification() على الكائن JobService. تُستخدم هذه القيمة لإعلام المستخدم بأن المهمة قيد التشغيل، في كل من "إدارة المهام" وفي منطقة إشعارات شريط الحالة:

    class CustomTransferService : JobService() {
      override fun onStartJob(params: JobParameters?): Boolean {
          val notification = Notification.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
                  .setContentTitle("My user-initiated data transfer job")
                  .setSmallIcon(android.R.mipmap.myicon)
                  .setContentText("Job is running")
                  .build()
    
          setNotification(params, notification.id, notification,
                  JobService.JOB_END_NOTIFICATION_POLICY_DETACH)
          // Do the job execution.
      }
    }
    
  6. قم بتحديث الإشعار بشكل دوري لإبقاء المستخدم على اطلاع بحالة الوظيفة وتقدمها. إذا لم يكن بإمكانك تحديد حجم النقل قبل تحديد موعد المهمة أو كنت بحاجة إلى تعديل حجم النقل المقدَّر، استخدِم واجهة برمجة التطبيقات الجديدة updateEstimatedNetworkBytes() لتعديل حجم النقل بعد أن يصبح معروفًا.

  7. عند اكتمال التنفيذ، اتصل بـ jobFinished() لإعلام النظام باكتمال المهمة أو إعادة جدولة المهمة.

إمكانية إيقاف مهام نقل البيانات التي بدأها المستخدم

يمكن لكل من المستخدم والنظام إيقاف مهام النقل التي يبدؤها المستخدم.

بواسطة المستخدم، من "إدارة المهام"

يمكن للمستخدم إيقاف مهمة نقل بيانات يبدؤها المستخدم في مدير المهام.

عندما يضغط المستخدم على زر إيقاف، يُجري النظام ما يلي:

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

ولهذه الأسباب، يُنصح بتوفير عناصر تحكّم في الإشعار المنشور للوظيفة للسماح بإيقاف المهمة وإعادة جدولتها بشكل ملائم.

تجدر الإشارة إلى أنّه في الظروف الخاصة، لا يظهر الزر إيقاف بجانب الوظيفة في إدارة المهام، أو لا يتم عرض الوظيفة في إدارة المهام على الإطلاق.

حسب النظام

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

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

وعندما يوقف النظام المهمة (وليس بسبب حالة الذاكرة المنخفضة)، يستدعي النظام المهمة onStopJob()، ويعيد النظام محاولة المهمة في الوقت الذي يعتبره النظام النموذج الأمثل. يُرجى التأكّد من أنّ تطبيقك يمكنه الاحتفاظ بحالة نقل البيانات، حتى في حال عدم استدعاء onStopJob()، وأنّ التطبيق يمكنه استعادة هذه الحالة عند استدعاء "onStartJob()" مرة أخرى.

الشروط المسموح بها لجدولة مهام نقل البيانات التي يبدأها المستخدم

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

في ما يلي استثناءات العبارة السابقة:

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

إذا تمت جدولة المهمة في وقت آخر غير مدرَج في قائمة الشروط المسموح بها، ستفشل المهمة وتعرض رمز الخطأ RESULT_FAILURE.

القيود المسموح بها لمهام نقل البيانات التي يبدأها المستخدم

لدعم المهام التي يتم تنفيذها في المراحل المثلى، يوفّر Android إمكانية تحديد قيود لكل نوع من أنواع المهام. هذه القيود متاحة بالفعل اعتبارًا من Android 13.

ملاحظة: يقارن الجدول التالي فقط القيود التي تختلف بين كل نوع من أنواع المهام. راجِع صفحة المطوِّرين JobScheduler أو قيود العمل للاطّلاع على جميع القيود.

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

في ما يلي القيود المسموح بها في مهام نقل البيانات التي يبدؤها المستخدم:

  • setBackoffCriteria(JobInfo.BACKOFF_POLICY_EXPONENTIAL)
  • setClipData()
  • setEstimatedNetworkBytes()
  • setMinimumNetworkChunkBytes()
  • setPersisted()
  • setNamespace()
  • setRequiredNetwork()
  • setRequiredNetworkType()
  • setRequiresBatteryNotLow()
  • setRequiresCharging()
  • setRequiresStorageNotLow()

الاختبار

تعرض القائمة التالية بعض الخطوات حول كيفية اختبار وظائف تطبيقك يدويًا:

  • للحصول على معرّف الوظيفة، احصل على القيمة التي تم تحديدها في المهمة التي يتم إنشاؤها.
  • لتشغيل مهمة على الفور، أو لإعادة محاولة مهمة متوقفة، قم بتشغيل الأمر التالي في نافذة طرفية:

    adb shell cmd jobscheduler run -f APP_PACKAGE_NAME JOB_ID
    
  • لمحاكاة النظام الذي يفرض إيقاف مهمة (بسبب حالة النظام أو حالات انتهاء الحصة)، شغِّل الأمر التالي في نافذة طرفية:

    adb shell cmd jobscheduler timeout TEST_APP_PACKAGE TEST_JOB_ID