איחוד התראות בנייד

החל מרמת Android API 26, נדרש שימוש בהתראות קבועות בשירותים שפועלים בחזית. הדרישה הזו נועדה למנוע מכם להסתיר שירותים שעשויים להפעיל לחץ יתר על משאבי המערכת, כולל הסוללה בפרט. הדרישה הזו יוצרת בעיה פוטנציאלית: אם באפליקציה עם כמה שירותי חזית לא מנהלים בקפידה את ההתראה כך שתשותף בין כל השירותים, יכול להיות שיופיעו כמה התראות עיקשות שלא ניתן לסגור, וכתוצאה מכך תהיה תחושה של עומס לא רצוי ברשימת ההתראות הפעילה.

הבעיה הזו נעשית קשה יותר כשמשתמשים ב-SDKs כמו Navigation SDK, שמפעילים שירותי חזית שאינם תלויים באפליקציה, ויש להם התראות קבועות משלהם, ולכן קשה לאחד אותם. כדי לטפל בבעיות האלה, הוספנו ל-Navigation SDK בגרסה 1.11 ממשק API פשוט שעוזר לנהל התראות קבועות באפליקציה, כולל ב-SDK.

איחוד התראות קבועות

רכיבים

מנהל השירותים בחזית מספק מעטפת סביב סוג השירות בחזית של Android וסוג ההתראות הקבועות. הפונקציה העיקרית של המעטפת הזו היא לאכוף שימוש חוזר במזהה ההתראה, כדי שההתראה תשותף בין כל שירותי החזית באמצעות המנהל.


‏Navigation SDK מכיל שיטות סטטיות להפעלה ולקבלה של ה-singleton‏ ForegroundServiceManager. אפשר לאתחל את ה-Singleton הזה רק פעם אחת במהלך החיים של Navigation SDK. לכן, אם משתמשים באחת מהקריאות להפעלה (initForegroundServiceManagerMessageAndIntent() או initForegroundServiceManagerProvider()), צריך להקיף אותה בבלוק try-catch למקרה שהנתיב הזה ייכנס שוב. אם תנסו להפעיל כל אחת מהשיטות יותר מפעם אחת, מערכת Navigation SDK תשליך חריגה בסביבת זמן הריצה, אלא אם תמחקו קודם את כל ההפניות ל-ForegroundServiceManager ותפעילו את clearForegroundServiceManager() לפני כל קריאה נוספת.

ארבעת הפרמטרים של initForegroundServiceManagerMessageAndIntent() הם application,‏ notificationId,‏ defaultMessage ו-resumeIntent. אם שלושת הפרמטרים האחרונים הם null, ההתראה תהיה ההתראה הרגילה של Navigation SDK. עדיין אפשר להסתיר שירותים אחרים באפליקציה שפועלים בחזית מאחורי ההתראה הזו. הפרמטר notificationId מציין את מזהה ההתראה שצריך להשתמש בו בהתראה. אם הערך הוא null, המערכת משתמשת בערך שרירותי. אפשר להגדיר אותו באופן מפורש כדי לעקוף התנגשויות עם התראות אחרות, כמו התראות מ-SDK אחר. הערך defaultMessage הוא מחרוזת שמוצגת כשהמערכת לא מנווטת. resumeIntent הוא כוונה (intent) שמופעל כשלוחצים על ההתראה. אם הערך של resumeIntent הוא null, המערכת תתעלם מהקליקים על ההתראה.

שלושת הפרמטרים של initForegroundServiceManagerProvider() הם application,‏ notificationId ו-notificationProvider. אם שני הפרמטרים האחרונים הם null, ההתראה היא ההתראה הרגילה של Navigation SDK. הפרמטר notificationId מציין את מזהה ההתראה שצריך להשתמש בו בהתראה. אם הערך הוא null, המערכת משתמשת בערך שרירותי. אפשר להגדיר אותו באופן מפורש כדי לעקוף התנגשויות עם התראות אחרות, כמו התראות מ-SDK אחר. אם הערך של notificationProvider מוגדר, הספק תמיד אחראי ליצירת ההתראה שצריך להציג.

השיטה getForegroundServiceManager() ב-Navigation SDK מחזירה את ה-singleton של מנהל השירותים בחזית. אם עדיין לא יצרתם קוד, זה שווה ערך לקריאה ל-initForegroundServiceManagerMessageAndIntent() עם פרמטרים null עבור notificationId,‏ defaultMessage ו-resumeIntent.

ל-ForegroundServiceManager יש שלוש שיטות פשוטות. שתי הפונקציות הראשונות משמשות להעברת שירות לחזית וליציאה ממנה, והן נקראות בדרך כלל מתוך השירות שנוצר. השימוש בשיטות האלה מבטיח שהשירותים משויכים להתרעה הקבועה המשותפת. השיטה האחרונה, updateNotification(), מסמנת למנהל שהתראה השתנתה וצריכה להיכלל שוב ברינדור.

אם אתם צריכים שליטה מלאה בהתראה הקבועה המשותפת, ה-API מספק ממשק 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 v2.0, קריאה לשיטה הזו כמה פעמים גורמת להשלכת חריגה מאומתת במקום חריגה בסביבת זמן הריצה.
  • יכול להיות ש-Google עדיין צריכה לעבוד על סגנון עקבי לאורך כל חיי ההתראה, שיהיה תואם לסגנון הכותרת.
  • כשמגדירים ספק התראות, אפשר לקבוע את רמת העדיפות כדי לקבוע את אופן ההצגה של ההתראות.
  • Google לא מספקת דרך פשוטה לאחזור מידע על מסלול מפורט שספק ההתראות עשוי להוסיף להתרעה.