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

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

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

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

المكونات

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


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

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

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

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

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

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

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

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

إخفاء الإشعارات المستمرة للخدمات الأخرى التي تعمل في المقدّمة في التطبيق
أسهل سيناريو هو الحفاظ على السلوك الحالي واستخدام الإشعار المستمر لعرض معلومات حزمة Navigation SDK فقط. يمكن إخفاء الخدمات الأخرى خلف هذا الإشعار باستخدام الطريقتَين startForeground() وstopForeground() في مدير الخدمات التي تعمل في المقدّمة.
إخفاء الإشعارات المستمرة للخدمات الأخرى التي تعمل في المقدّمة في التطبيق، ولكن ضبط النص التلقائي الذي يظهر عندما لا يكون النظام في وضع التنقّل
السيناريو الثاني الأسهل هو الحفاظ على السلوك الحالي واستخدام الإشعار المستمر لعرض معلومات حزمة Navigation SDK فقط، باستثناء الحالات التي لا يكون فيها النظام في وضع التنقّل. عندما لا يكون النظام في وضع التنقّل، يتم عرض السلسلة المقدَّمة إلى initForegroundServiceManagerMessageAndIntent() بدلاً من السلسلة التلقائية لحزمة Navigation 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، يمكنك ربط حزمة Navigation SDK بها باستخدام الرمز البرمجي التالي:

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

التحذيرات والخطط المستقبلية

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