دمج إشعارات الجوّال

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

تصبح هذه المشكلة أكثر تعقيدًا عند استخدام حِزم تطوير البرامج (SDK) مثل Navigation SDK التي تشغّل خدمات تعمل في المقدّمة بشكل مستقل عن التطبيق، وتتضمّن إشعارات ثابتة مستقلة خاصة بها، ما يجعل دمجها أمرًا صعبًا. لمعالجة هذه المشاكل، قدّمت حزمة Navigation SDK الإصدار 1.11 واجهة برمجة تطبيقات بسيطة للمساعدة في إدارة الإشعارات المستمرة في جميع أنحاء التطبيق، بما في ذلك داخل حزمة SDK.

دمج الإشعارات الدائمة

المكونات

يوفّر مدير الخدمات التي تعمل في المقدّمة برنامج تضمين لفئة الخدمات التي تعمل في المقدّمة في Android وفئة الإشعارات الدائمة. تتمثّل الوظيفة الرئيسية لهذا البرنامج المغلّف في فرض إعادة استخدام رقم تعريف الإشعار لكي تتم مشاركة الإشعار بين جميع الخدمات التي تعمل في المقدّمة والتي تستخدم المدير.


تحتوي حزمة تطوير البرامج (SDK) الخاصة بخدمة Navigation على طرق ثابتة لتهيئة والحصول على ForegroundServiceManager singleton. لا يمكن تهيئة هذا العنصر إلا مرة واحدة خلال مدة استخدام حزمة تطوير البرامج (SDK) الخاصة بخدمة Navigation. نتيجةً لذلك، إذا كنت تستخدم إحدى عمليات الاستدعاء الأولية (initForegroundServiceManagerMessageAndIntent() أو initForegroundServiceManagerProvider())، عليك تضمينها في كتلة try-catch في حال إعادة إدخال المسار. يُطلق Navigation SDK استثناء وقت التشغيل إذا استدعيت أيًا من الطريقتين أكثر من مرة ما لم تمحُ أولاً جميع المراجع إلى ForegroundServiceManager وتستدعي clearForegroundServiceManager() قبل كل استدعاء لاحق.

المَعلمات الأربع الخاصة بـ initForegroundServiceManagerMessageAndIntent() هي application وnotificationId وdefaultMessage وresumeIntent. إذا كانت المَعلمات الثلاث الأخيرة فارغة، يكون الإشعار هو إشعار Navigation SDK العادي. سيظل بإمكانك إخفاء الخدمات الأخرى التي تعمل في المقدّمة في التطبيق خلف هذا الإشعار. تحدّد المَعلمة notificationId رقم تعريف الإشعار الذي يجب استخدامه للإشعار. إذا كانت القيمة فارغة، سيتم استخدام قيمة عشوائية. يمكنك ضبطه بشكل صريح لتجنُّب التعارضات مع الإشعارات الأخرى، مثل الإشعارات الواردة من حزمة تطوير برامج أخرى. ‫defaultMessage هي سلسلة يتم عرضها عندما لا يكون النظام في وضع التنقّل. ‫resumeIntent هو كائن intent يتم تنشيطه عند النقر على الإشعار. إذا كانت قيمة resumeIntent هي null، سيتم تجاهل النقرات على الإشعار.

المَعلمات الثلاث الخاصة بـ initForegroundServiceManagerProvider() هي application وnotificationId وnotificationProvider. إذا كانت المَعلمتان الأخيرتان فارغتين، يكون الإشعار هو الإشعار التلقائي لحزمة تطوير البرامج (SDK) الخاصة بخدمة Navigation. تحدّد المَعلمة notificationId معرّف الإشعار الذي يجب استخدامه للإشعار. إذا كانت القيمة فارغة، سيتم استخدام قيمة عشوائية. يمكنك ضبطه بشكل صريح لتجنُّب التعارضات مع الإشعارات الأخرى، مثل الإشعارات الواردة من حزمة تطوير برامج (SDK) أخرى. في حال ضبط notificationProvider، يكون مقدّم الخدمة مسؤولاً دائمًا عن إنشاء الإشعار الذي سيتم عرضه.

تعرض طريقة getForegroundServiceManager() في حزمة تطوير البرامج للتنقّل عنصرًا واحدًا من مدير الخدمة التي تعمل في المقدّمة. إذا لم يسبق لك إنشاء رمز مميّز، سيكون ذلك مكافئًا لاستدعاء initForegroundServiceManagerMessageAndIntent() مع مَعلمات فارغة لكل من notificationId وdefaultMessage وresumeIntent.

يحتوي ForegroundServiceManager على ثلاث طرق بسيطة. يُستخدم الخياران الأولان لنقل خدمة إلى المقدّمة أو الخلفية، ويتم عادةً استدعاؤهما من داخل الخدمة التي تم إنشاؤها. يضمن استخدام هذه الطرق ربط الخدمات بالإشعار الدائم المشترَك. تحدّد الطريقة الأخيرة، updateNotification()، للمدير أنّ الإشعار قد تغيّر، ويجب إعادة عرضه.

إذا كنت بحاجة إلى التحكّم الكامل في الإشعار الدائم المشترَك، توفّر واجهة برمجة التطبيقات واجهة NotificationContentProvider لتحديد موفّر الإشعارات، والذي يحتوي على طريقة واحدة للحصول على إشعار يتضمّن المحتوى الحالي. وتوفّر أيضًا فئة أساسية يمكنك استخدامها اختياريًا للمساعدة في تحديد مقدّم الخدمة. أحد الأغراض الرئيسية لفئة الأساس هو توفير طريقة لاستدعاء updateNotification() بدون الحاجة إلى الوصول إلى ForegroundServiceManager. إذا كنت تستخدم مثيلاً لمقدّم الإشعارات لتلقّي رسائل إشعارات جديدة، يمكنك استدعاء هذه الطريقة الداخلية مباشرةً لعرض الرسالة في الإشعار.

سيناريوهات الاستخدام

يوضّح هذا القسم سيناريوهات الاستخدام الخاصة باستخدام الإشعارات الدائمة المشترَكة.

إخفاء الإشعارات الدائمة الخاصة بالخدمات التي تعمل في المقدّمة في التطبيقات الأخرى
أسهل سيناريو هو الحفاظ على السلوك الحالي واستخدام الإشعار الدائم فقط لعرض معلومات Navigation SDK. يمكن أن تختفي الخدمات الأخرى خلف هذا الإشعار باستخدام طريقتَي مدير الخدمة التي تعمل في المقدّمة startForeground() وstopForeground().
إخفاء الإشعارات الدائمة الخاصة بالخدمات الأخرى التي تعمل في المقدّمة، ولكن ضبط النص التلقائي الذي يظهر عند عدم التنقّل
السيناريو الثاني الأسهل هو الحفاظ على السلوك الحالي، واستخدام الإشعار الدائم فقط لعرض معلومات Navigation SDK، إلا عندما لا يكون النظام في وضع التنقّل. عندما لا يكون النظام في وضع التنقّل، يتم عرض السلسلة المقدَّمة إلى initForegroundServiceManagerMessageAndIntent() بدلاً من سلسلة حزمة تطوير البرامج (SDK) التلقائية التي تشير إلى "خرائط Google". يمكنك أيضًا استخدام هذا الاستدعاء لضبط هدف الاستئناف الذي يتم تنشيطه عند النقر على الإشعار.
التحكّم الكامل في عرض الإشعار الدائم
يتطلّب السيناريو الأخير تحديد موفّر إشعارات وإنشاؤه وتمريره إلى ForegroundServiceManager باستخدام initForegroundServiceManagerProvider(). يمنحك هذا الخيار تحكّمًا كاملاً في المحتوى المعروض في الإشعار، ولكنّه يؤدي أيضًا إلى فصل معلومات إشعار Navigation SDK عن الإشعار، وبالتالي إزالة الطلبات المفيدة بالتوجيه خطوة بخطوة التي تظهر في الإشعار. لا توفّر Google طريقة سهلة لاسترداد هذه المعلومات وإدراجها في الإشعار.

مثال على مقدّم خدمة الإشعارات

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

public class NotificationContentProviderImpl
   extends NotificationContentProviderBase
   implements NotificationContentProvider {
 private String channelId;
 private Context context;
 private String message;

 /** Constructor */
 public NotificationContentProviderImpl(Application application) {
   super(application);
   message = "-- uninitialized --";
   channelId = null;
   this.context = application;
 }

 /**
  * Sets message to display in the notification. Calls updateNotification
  * to display the message immediately.
  *
  * @param msg The message to display in the notification.
  */
 public void setMessage(String msg) {
   message = msg;
   updateNotification();
 }

 /**
  * Returns the notification as it should be rendered.
  */
 @Override
 public Notification getNotification() {
   Notification notification;

   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     Spanned styledText = Html.fromHtml(message, FROM_HTML_MODE_LEGACY);
     String channelId = getChannelId(context);
     notification =
         new Notification.Builder(context, channelId)
             .setContentTitle("Notifications Demo")
             .setStyle(new Notification.BigTextStyle()
                 .bigText(styledText))
             .setSmallIcon(R.drawable.ic_navigation_white_24dp)
             .setTicker("ticker text")
             .build();
   } else {
     notification = new Notification.Builder(context)
         .setContentTitle("Notification Demo")
         .setContentText("testing non-O text")
         .build();
   }

   return notification;
 }

 // Helper to set up a channel ID.
 private String getChannelId(Context context) {
   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     if (channelId == null) {
       NotificationManager notificationManager =
           (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
       NotificationChannel channel = new NotificationChannel(
           "default", "navigation", NotificationManager.IMPORTANCE_DEFAULT);
       channel.setDescription("For navigation persistent notification.");
       notificationManager.createNotificationChannel(channel);
       channelId = channel.getId();
     }
     return channelId;
   } else {
     return "";
   }
 }
}

بعد إنشاء NotificationContentProviderImpl، عليك ربط حزمة تطوير البرامج (SDK) الخاصة بخدمة Navigation API بها باستخدام الرمز التالي:

ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
mNotification = new NotificationContentProviderImpl(getApplication());
NavigationApi.clearForegroundServiceManager();
NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);

تنبيهات وخطط مستقبلية

  • احرص على استدعاء initForegroundServiceManagerMessageAndIntent() أو initForegroundServiceManagerProvider() في وقت مبكر حتى يتم تحديد سيناريو الاستخدام المتوقّع بشكل جيد. يجب استدعاء هذه الطريقة قبل إنشاء عنصر Navigator جديد.
  • احرص على رصد الاستثناءات من المكالمات إلى initForegroundServiceManagerMessageAndIntent() أو initForegroundServiceManagerProvider() في حال تم إدخال مسار الرمز البرمجي أكثر من مرة. في الإصدار 2.0 من حزمة تطوير البرامج (SDK) الخاصة بخدمة Navigation، يؤدي استدعاء هذه الطريقة عدة مرات إلى ظهور استثناء تم التحقّق منه بدلاً من استثناء وقت التشغيل.
  • قد تحتاج Google إلى إجراء بعض التعديلات للحصول على تصميم متّسق على مدار فترة عرض الإشعار يتطابق مع تصميم العنوان.
  • عند تحديد موفّر إشعارات، يمكنك التحكّم في سلوك الإشعارات المنبثقة باستخدام الأولوية.
  • لا توفّر Google وسيلة بسيطة لاسترداد المعلومات الخاصة بالتوجيه خطوة بخطوة التي قد يدرجها موفّر الإشعارات في الإشعار.