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

نظام التشغيل 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