การแจ้งเตือนถาวรที่ใช้ร่วมกัน

ตั้งแต่ Android API ระดับ 26 เป็นต้นไป จะต้องมีการแจ้งเตือนอย่างต่อเนื่องสำหรับบริการที่ทำงานอยู่เบื้องหน้า ข้อกำหนดนี้มีไว้เพื่อป้องกันไม่ให้คุณซ่อนบริการที่อาจใช้ทรัพยากรระบบมากเกินไป ซึ่งรวมถึงแบตเตอรี่ ข้อกำหนดนี้อาจก่อให้เกิดปัญหาขึ้นได้ หากแอปที่มีบริการที่ทำงานอยู่เบื้องหน้าหลายแอปไม่ได้จัดการการแจ้งเตือนดังกล่าวอย่างรอบคอบและแชร์กับบริการทั้งหมด จะมีการแจ้งเตือนที่ปิดไม่ได้ซ้ำๆ หลายครั้ง ทำให้รายการการแจ้งเตือนที่ใช้งานอยู่ดูรกรุงรัง

ปัญหานี้จะยากขึ้นเมื่อคุณใช้ SDK เช่น Navigation SDK ซึ่งเรียกใช้บริการที่ทำงานอยู่เบื้องหน้าโดยไม่ขึ้นอยู่กับแอป ซึ่งอาจมีการแจ้งเตือนถาวรที่เป็นอิสระของตัวเอง ทำให้รวมได้ยาก ในการแก้ไขปัญหาเหล่านี้ Navigation SDK เวอร์ชัน 1.11 ได้เปิดตัว API แบบง่ายเพื่อช่วยจัดการการแจ้งเตือนถาวรในแอป รวมถึงภายใน SDK

รวมการแจ้งเตือนถาวร

คอมโพเนนต์

ตัวจัดการบริการที่ทำงานอยู่เบื้องหน้ามี Wrapper สำหรับคลาสบริการที่ทำงานอยู่เบื้องหน้าของ Android และคลาสการแจ้งเตือนถาวร หน้าที่หลักของ Wrapper นี้คือการบังคับใช้รหัสการแจ้งเตือนซ้ำเพื่อให้แชร์การแจ้งเตือนในบริการที่ทำงานอยู่เบื้องหน้าทั้งหมดที่ใช้ตัวจัดการ


NavigationAPI มีวิธีแบบคงที่สำหรับการเริ่มต้นและรับซิงเกิล ForegroundServiceManager ซิงเกิลตันนี้จะเริ่มต้นได้เพียงครั้งเดียวตลอดอายุการใช้งานของ Navigation SDK ดังนั้น หากใช้การเรียกเริ่มต้นอันใดอันหนึ่ง (initForegroundServiceManagerMessageAndIntent() หรือ initForegroundServiceManagerProvider()) คุณก็ควรล้อมรอบด้วยบล็อก ลอง/จับ ในกรณีที่มีการป้อนเส้นทางนั้นอีกครั้ง เพื่อป้องกันปัญหาความไม่เข้ากันนี้ Navigation SDK จะส่งข้อยกเว้นรันไทม์หากคุณเรียกใช้เมธอดใดเมธอดหนึ่งมากกว่า 1 ครั้ง เว้นแต่คุณจะล้างการอ้างอิงทั้งหมดไปยัง ForegroundServiceManager ก่อนและเรียก clearForegroundServiceManager() ก่อนการเรียกครั้งต่อไปแต่ละครั้ง ใน Navigation SDK เวอร์ชัน 2.0 จะมีการเพิ่มข้อยกเว้นที่เลือกลงใน API สำหรับวัตถุประสงค์นี้

พารามิเตอร์ 4 ตัวของ initForegroundServiceManagerMessageAndIntent() คือ application, notificationId, defaultMessage และ resumeIntent หากพารามิเตอร์ 3 รายการสุดท้ายเป็น Null การแจ้งเตือนจะเป็นการแจ้งเตือน Navigation SDK แบบมาตรฐาน คุณยังสามารถซ่อนบริการที่ทำงานอยู่เบื้องหน้าอื่นๆ ในแอปที่อยู่เบื้องหลังการแจ้งเตือนนี้ได้ พารามิเตอร์ notificationId ระบุรหัสการแจ้งเตือนที่ควรใช้สำหรับการแจ้งเตือน หากเป็นค่าว่าง ระบบจะใช้ค่าที่กำหนดเอง คุณสามารถตั้งค่าแบบเจาะจงเพื่อไม่ให้มีข้อขัดแย้งกับการแจ้งเตือนอื่นๆ เช่น การแจ้งเตือนจาก SDK อื่น defaultMessage เป็นสตริงที่แสดงเมื่อระบบไม่ได้นำทาง resumeIntent คือ Intent ที่เริ่มทำงานเมื่อมีการคลิกการแจ้งเตือน หาก resumeIntent เป็นค่าว่าง ระบบจะไม่สนใจการคลิกที่การแจ้งเตือนนั้น

พารามิเตอร์ 3 ตัวของ initForegroundServiceManagerProvider() คือ application, notificationId และ notificationProvider หากพารามิเตอร์ 2 รายการสุดท้ายเป็น Null การแจ้งเตือนจะเป็นการแจ้งเตือน Navigation SDK แบบมาตรฐาน พารามิเตอร์ notificationId ระบุรหัสการแจ้งเตือนที่ควรใช้สำหรับการแจ้งเตือน หากเป็นค่าว่าง ระบบจะใช้ค่าที่กำหนดเอง คุณสามารถตั้งค่าแบบเจาะจงเพื่อไม่ให้มีข้อขัดแย้งกับการแจ้งเตือนอื่นๆ เช่น การแจ้งเตือนจาก SDK อื่น หากตั้งค่า notificationProvider ไว้ ผู้ให้บริการจะเป็นผู้รับผิดชอบในการสร้างการแจ้งเตือนเพื่อให้แสดงผลเสมอ

เมธอด getForegroundServiceManager() ของ Navigation SDK จะแสดงผล Singleton ของ ระบบจัดการบริการที่ทำงานอยู่เบื้องหน้า หากยังไม่ได้สร้าง พร็อพเพอร์ตี้จะเทียบเท่ากับการเรียกใช้ initForegroundServiceManagerMessageAndIntent() ด้วยพารามิเตอร์ Null สำหรับ notificationId, defaultMessage และ resumeIntent

ForegroundServiceManager มี 3 วิธีการง่ายๆ 2 ประเภทแรกมีไว้สำหรับการย้ายบริการเข้าและออกจากเบื้องหน้า และมักเรียกใช้จากภายในบริการที่สร้างขึ้น การใช้วิธีการเหล่านี้ช่วยให้มั่นใจว่าบริการเชื่อมโยงกับการแจ้งเตือนถาวรที่แชร์ไว้ วิธีสุดท้ายคือ updateNotification() จะแจ้งผู้จัดการว่ามีการเปลี่ยนแปลงการแจ้งเตือนแล้ว และควรแสดงผลอีกครั้ง

หากต้องการควบคุมเนื้อหาของการแจ้งเตือนถาวรที่แชร์อย่างสมบูรณ์ API ใหม่จะให้อินเทอร์เฟซ NotificationContentProvider สำหรับกำหนดผู้ให้บริการการแจ้งเตือน ซึ่งมีวิธีรับการแจ้งเตือนเกี่ยวกับเนื้อหาปัจจุบันด้วยวิธีเดียว นอกจากนี้ยังมีคลาสพื้นฐาน ซึ่งคุณสามารถเลือกใช้เพื่อช่วยกำหนดผู้ให้บริการ หนึ่งในวัตถุประสงค์หลักของคลาสพื้นฐานคือเครื่องมือนี้ช่วยให้เรียกใช้ updateNotification() ได้อย่างง่ายดายโดยไม่ต้องเข้าถึง ForegroundServiceManager วิธีตัวช่วยนี้จะเป็นประโยชน์หากคุณใช้อินสแตนซ์ของผู้ให้บริการการแจ้งเตือนเพื่อรับข้อความแจ้งเตือนใหม่ ซึ่งในกรณีนี้คุณสามารถใช้เมธอดภายในนี้โดยตรงเพื่อแสดงข้อความในการแจ้งเตือน

สถานการณ์การใช้งาน

ส่วนนี้จะอธิบายถึงสถานการณ์การใช้งานต่างๆ ในการใช้การแจ้งเตือนถาวรที่แชร์

ซ่อนการแจ้งเตือนถาวรของบริการที่ทำงานอยู่เบื้องหน้าอื่นๆ ของแอป
สถานการณ์ที่ง่ายที่สุดคือการรักษาลักษณะการทำงานปัจจุบันไว้ และใช้เฉพาะการแจ้งเตือนแบบถาวรสำหรับแสดงผลข้อมูล Navigation SDK บริการอื่นๆ จะซ่อนอยู่หลังการแจ้งเตือนนี้ได้โดยใช้เมธอด startForeground() และ stopForeground() ของตัวจัดการบริการที่ทำงานอยู่เบื้องหน้า
ซ่อนการแจ้งเตือนตลอดเวลาของบริการที่ทำงานอยู่เบื้องหน้าอื่นๆ ของแอป แต่ตั้งค่าข้อความเริ่มต้นที่แสดงเมื่อไม่มีการนำทาง
สถานการณ์ที่ 2 ที่ง่ายที่สุดคือการรักษาลักษณะการทำงานปัจจุบันไว้และใช้การแจ้งเตือนแบบถาวรสำหรับการแสดงผลข้อมูล Navigation SDK เท่านั้น ยกเว้นในกรณีที่ระบบไม่ได้นำทาง เมื่อระบบไม่ได้นำทาง สตริงที่ระบุให้กับ initForegroundServiceManagerMessageAndIntent() จะแสดงแทนสตริง SDK การนำทางเริ่มต้นที่กล่าวถึง "Google Maps" และยังสามารถใช้การเรียกใช้นี้เพื่อตั้งค่า Intent การกลับมาทำงานอีกครั้งที่จะเริ่มทำงานเมื่อมีการคลิกการแจ้งเตือนได้อีกด้วย
ควบคุมการแสดงผลของการแจ้งเตือนถาวรได้อย่างเต็มที่
สถานการณ์สุดท้ายจะต้องกำหนดและสร้างผู้ให้บริการการแจ้งเตือน แล้วส่งไปยัง 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 "";
   }
 }
}

ข้อควรระวังและแผนการในอนาคต

  • โปรดเรียกใช้ initForegroundServiceManagerMessageAndIntent() หรือ initForegroundServiceManagerProvider() ตั้งแต่เนิ่นๆ เพื่อกำหนดสถานการณ์การใช้งานที่คาดไว้เป็นอย่างดี คุณต้องเรียกใช้เมธอดนี้ก่อนที่จะสร้าง Navigator ใหม่
  • อย่าลืมหาข้อยกเว้นจากการเรียกใช้ไปยัง initForegroundServiceManagerMessageAndIntent() หรือ initForegroundServiceManagerProvider() ในกรณีที่มีการป้อนเส้นทางโค้ดมากกว่า 1 ครั้ง ใน Navigation SDK v2.0 การเรียกใช้เมธอดนี้หลายครั้งจะทำให้เกิดข้อยกเว้นที่ทำเครื่องหมายแล้วแทนที่จะเป็นข้อยกเว้นรันไทม์
  • Google ยังอาจมีงานที่ต้องทำอีกเพื่อจัดรูปแบบให้สอดคล้องกันตลอดอายุของการแจ้งเตือนที่ตรงกับการจัดรูปแบบส่วนหัว
  • เมื่อกำหนดผู้ให้บริการการแจ้งเตือน คุณจะควบคุมพฤติกรรมการแจ้งเตือนด้วยลำดับความสำคัญได้
  • Google ไม่ได้มอบวิธีง่ายๆ ในการดึงข้อมูลแบบเลี้ยวต่อเลี้ยวซึ่งผู้ให้บริการการแจ้งเตือนอาจแทรกไว้ในการแจ้งเตือน