รวมการแจ้งเตือนในอุปกรณ์เคลื่อนที่

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

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

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

คอมโพเนนต์

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


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

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

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

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

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

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

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

ส่วนนี้จะอธิบายรายละเอียดสถานการณ์การใช้งานสำหรับการใช้การแจ้งเตือนแบบถาวรที่แชร์

ซ่อนการแจ้งเตือนแบบถาวรของบริการที่ทำงานอยู่เบื้องหน้าของแอปอื่นๆ
สถานการณ์ที่ง่ายที่สุดคือการคงลักษณะการทำงานปัจจุบันไว้ และใช้การแจ้งเตือนแบบถาวรสำหรับการแสดงผลข้อมูล Navigation SDK เท่านั้น บริการอื่นๆ สามารถซ่อนอยู่หลังการแจ้งเตือนนี้ได้โดยใช้เมธอด startForeground() และ stopForeground() ของ Foreground Service Manager
ซ่อนการแจ้งเตือนแบบถาวรของบริการที่ทำงานอยู่เบื้องหน้าของแอปอื่นๆ แต่ตั้งค่าข้อความเริ่มต้นที่แสดงเมื่อไม่ได้นำทาง
สถานการณ์ที่ง่ายที่สุดเป็นอันดับ 2 คือการคงลักษณะการทำงานปัจจุบันไว้ และใช้การแจ้งเตือนแบบถาวรสำหรับการแสดงผลข้อมูล Navigation SDK เท่านั้น ยกเว้นเมื่อระบบไม่ได้นำทาง เมื่อระบบไม่ได้นำทาง สตริงที่ระบุใน initForegroundServiceManagerMessageAndIntent() จะแสดงแทนสตริง Navigation SDK เริ่มต้นที่พูดถึง "Google Maps" นอกจากนี้ คุณยังใช้การเรียกนี้เพื่อตั้งค่า Intent ของการกลับมาทำงานอีกครั้งซึ่งจะเริ่มทำงานเมื่อมีการคลิกการแจ้งเตือนได้ด้วย
ควบคุมการแสดงผลของการแจ้งเตือนแบบถาวรได้อย่างเต็มที่
สถานการณ์สุดท้ายต้องมีการกำหนดและสร้างผู้ให้บริการการแจ้งเตือน แล้วส่งไปยัง ForegroundServiceManager โดยใช้ initForegroundServiceManagerProvider() ตัวเลือกนี้ช่วยให้คุณควบคุมสิ่งที่แสดงในการแจ้งเตือนได้อย่างเต็มที่ แต่ก็ยกเลิกการเชื่อมต่อข้อมูลการแจ้งเตือนของ Navigation SDK จากการแจ้งเตือนด้วย ซึ่งจะนำข้อความแจ้งแบบเลี้ยวต่อเลี้ยวที่เป็นประโยชน์ซึ่งแสดงในการแจ้งเตือนออก Google ไม่มีวิธีง่ายๆ ในการดึงข้อมูลนี้และแทรกลงในการแจ้งเตือน

ตัวอย่างผู้ให้บริการการแจ้งเตือน

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้างและแสดงผลการแจ้งเตือนโดยใช้ Content Provider ของการแจ้งเตือนแบบง่าย

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() ในกรณีที่มีการเข้าถึงเส้นทางโค้ดมากกว่า 1 ครั้ง ใน Navigation SDK v2.0 การเรียกใช้เมธอดนี้หลายครั้งจะแสดงข้อยกเว้นที่ตรวจสอบแล้วแทนข้อยกเว้นรันไทม์
  • Google อาจยังต้องดำเนินการเพื่อให้ได้การจัดรูปแบบที่สอดคล้องกันตลอดอายุการใช้งานของการแจ้งเตือนที่ตรงกับการจัดรูปแบบส่วนหัว
  • เมื่อกำหนดผู้ให้บริการการแจ้งเตือน คุณจะควบคุมลักษณะการทำงานของ Heads-up ได้ด้วยลำดับความสำคัญ
  • Google ไม่มีวิธีง่ายๆ ในการดึงข้อมูลแบบเลี้ยวต่อเลี้ยวที่ผู้ให้บริการการแจ้งเตือนอาจแทรกลงในการแจ้งเตือน