החל מרמת Android API 26, נדרשות התראות קבועות לשירותים שפועלים בחזית. הדרישה הזו נועדה למנוע מכם להסתיר שירותים שעשויים להעמיס על משאבי המערכת, כולל הסוללה. הדרישה הזו יוצרת בעיה פוטנציאלית: אם אפליקציה עם כמה שירותים שפועלים בחזית לא מנהלת את ההתראה בקפידה כך שהיא משותפת לכל השירותים, יכולות להיות כמה התראות קבועות שלא ניתן לסגור, מה שמוביל לעומס לא רצוי ברשימה הפעילה של ההתראות.
הבעיה הזו מורכבת יותר כשמשתמשים בערכות SDK כמו Navigation SDK, שמריצות שירותים שפועלים בחזית באופן עצמאי מהאפליקציה, ויש להן התראות קבועות משלהן, כך שקשה לאחד אותן.
כדי לטפל בבעיות האלה, בגרסה 1.11 של Navigation SDK הוספנו API פשוט שיעזור לכם לנהל התראות קבועות באפליקציה, כולל ב-SDK.
רכיבים
מנהל השירותים שפועלים בחזית מספק עטיפה סביב מחלקת השירותים שפועלים בחזית ב-Android ומחלקת ההתראות הקבועות. הפונקציה העיקרית של העטיפה הזו היא לאכוף שימוש חוזר במזהה ההתראה, כדי שההתראה תשותף בין כל שירותי החזית באמצעות מנהל ההתראות.
Navigation SDK מכיל שיטות סטטיות לאתחול ולקבלת ה-singleton ForegroundServiceManager
. אפשר לאתחל את הסינגלטון הזה רק פעם אחת במהלך הפעילות של Navigation SDK. לכן, אם משתמשים באחת מהקריאות לאתחול (initForegroundServiceManagerMessageAndIntent()
או initForegroundServiceManagerProvider()
), צריך להקיף אותה בבלוק try-catch למקרה שהנתיב הזה יוזן מחדש. Navigation SDK
מפעיל חריגה בזמן ריצה אם קוראים לאחת מהשיטות יותר מפעם אחת, אלא אם קודם מוחקים את כל ההפניות אל ForegroundServiceManager
וקוראים ל-clearForegroundServiceManager()
לפני כל קריאה עוקבת.
ארבעת הפרמטרים של initForegroundServiceManagerMessageAndIntent()
הם
application
, notificationId
, defaultMessage
ו-resumeIntent
. אם שלושת הפרמטרים האחרונים הם null, ההתראה היא התראה רגילה של Navigation SDK. עדיין אפשר להסתיר מאחורי ההתראה הזו שירותים אחרים של האפליקציה שפועלים בחזית. הפרמטר notificationId
מציין את מזהה ההתראה שבו צריך להשתמש עבור ההתראה. אם הערך הוא null, נעשה שימוש בערך שרירותי. אפשר להגדיר אותו באופן מפורש כדי למנוע התנגשויות עם התראות אחרות, כמו התראות מ-SDK אחר. defaultMessage
היא מחרוזת שמוצגת כשהמערכת לא מנווטת. resumeIntent
היא כוונה שמופעלת כשלוחצים על ההתראה. אם הערך של resumeIntent
הוא null, המערכת מתעלמת מקליקים על ההתראה.
שלושת הפרמטרים של initForegroundServiceManagerProvider()
הם
application
, notificationId
ו-notificationProvider
. אם שני הפרמטרים האחרונים הם null, ההתראה היא התראה רגילה של Navigation SDK. הפרמטר notificationId
מציין את מזהה ההתראה שבו צריך להשתמש עבור ההתראה. אם הערך הוא null, המערכת משתמשת בערך שרירותי. אפשר להגדיר אותו באופן מפורש כדי למנוע התנגשויות עם התראות אחרות, כמו התראות מ-SDK אחר. אם הערך של notificationProvider
מוגדר, הספק תמיד אחראי ליצירת ההתראה שתוצג.
השיטה getForegroundServiceManager()
של Navigation SDK מחזירה את הסינגלטון של מנהל שירות הרקע. אם עדיין לא יצרתם כזה, אז זה שווה ערך לקריאה ל-initForegroundServiceManagerMessageAndIntent()
עם פרמטרים ריקים עבור notificationId
, defaultMessage
ו-resumeIntent
.
ל-ForegroundServiceManager
יש שלוש שיטות פשוטות. שתי השיטות הראשונות משמשות להעברת שירות אל החזית וממנה, ובדרך כלל מפעילים אותן מתוך השירות שנוצר. השימוש בשיטות האלה מבטיח שהשירותים ישויכו להתראה המשותפת המתמשכת. השיטה האחרונה, updateNotification()
, מסמנת למנהל שההתראה השתנתה וצריך לעבד אותה מחדש.
אם אתם צריכים שליטה מלאה בהתראה המשותפת הקבועה, ה-API מספק ממשק NotificationContentProvider
להגדרת ספק התראות, שמכיל method יחיד לקבלת התראה עם התוכן הנוכחי. הוא מספק גם מחלקת בסיס, שאפשר להשתמש בה כדי להגדיר את הספק. אחת המטרות העיקריות של מחלקת הבסיס היא לספק דרך לקרוא ל-updateNotification()
בלי צורך לגשת ל-ForegroundServiceManager
. אם אתם משתמשים במופע של ספק ההתראות כדי לקבל הודעות התראה חדשות, אתם יכולים לקרוא לשיטה הפנימית הזו ישירות כדי להציג את ההודעה בהתראה.
תרחישי שימוש
בקטע הזה מפורטים תרחישי השימוש בהתראות משותפות ומתמשכות.
- הסתרת התראות קבועות של שירותים אחרים ברקע של אפליקציות
- התרחיש הכי פשוט הוא לשמור על ההתנהגות הנוכחית ולהשתמש בהתראה הקבועה רק כדי להציג מידע מ-Navigation SDK. שירותים אחרים יכולים להסתתר מאחורי ההתראה הזו באמצעות השיטות
startForeground()
ו-stopForeground()
של מנהל השירותים שפועלים בחזית. - הסתרת התראות קבועות של שירותים אחרים בחזית האפליקציה, אבל הגדרת טקסט שמוצג כברירת מחדל כשלא מנווטים
- התרחיש השני הכי פשוט הוא לשמור על ההתנהגות הנוכחית, ולהשתמש בהתראה הקבועה רק כדי להציג מידע מ-Navigation SDK, אלא אם המערכת לא מנווטת. כשהמערכת לא מנווטת, המחרוזת שמועברת אל
initForegroundServiceManagerMessageAndIntent()
מוצגת במקום מחרוזת ברירת המחדל של Navigation SDK שכוללת את המילים Google Maps. אפשר גם להשתמש בקריאה הזו כדי להגדיר את כוונת ההמשך שמופעלת כשלוחצים על ההתראה. - שליטה מלאה בעיבוד של ההתראה הקבועה
- בתרחיש האחרון צריך להגדיר וליצור ספק התראות
ולהעביר אותו אל
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, קריאה לשיטה הזו מספר פעמים תגרום להשלכת חריג מסוג checked ולא חריג מסוג runtime. - יכול להיות ש-Google עדיין תצטרך לבצע פעולות כדי להשיג סגנון עקבי לאורך כל משך החיים של ההתראה, שיתאים לסגנון הכותרת.
- כשמגדירים ספק התראות, אפשר לשלוט בהתנהגות של ההתראות הקופצות באמצעות העדיפות.
- Google לא מספקת אמצעי פשוט לשליפת מידע מפורט על הנחיות נסיעה, שספק התראות עשוי להוסיף להתראה.