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

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

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

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

المكوّنات

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


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

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

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

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

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

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

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

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

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

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

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