Tổng hợp thông báo trên thiết bị di động

Kể từ Android API cấp 26, bạn phải sử dụng thông báo liên tục cho các dịch vụ trên nền trước. Yêu cầu này nhằm ngăn bạn ẩn các dịch vụ có thể gây ra nhu cầu quá mức đối với tài nguyên hệ thống, đặc biệt là pin. Yêu cầu này tạo ra một vấn đề tiềm ẩn: Nếu một ứng dụng có nhiều dịch vụ trên nền trước không quản lý cẩn thận thông báo để thông báo đó được chia sẻ trên tất cả các dịch vụ, thì có thể có nhiều thông báo liên tục không đóng được, dẫn đến tình trạng lộn xộn không mong muốn trong danh sách thông báo đang hoạt động.

Vấn đề này trở nên khó khăn hơn khi bạn sử dụng các SDK như SDK Điều hướng, chạy các dịch vụ trên nền trước độc lập với ứng dụng có thông báo liên tục độc lập riêng, khiến các dịch vụ này khó hợp nhất. Để giải quyết những vấn đề này, Navigation SDK phiên bản 1.11 đã giới thiệu một API đơn giản để giúp quản lý thông báo liên tục trên ứng dụng, bao gồm cả trong SDK.

Hợp nhất thông báo liên tục

Thành phần

Trình quản lý dịch vụ trên nền trước cung cấp một trình bao bọc xung quanh lớp dịch vụ trên nền trước của Android và lớp thông báo liên tục. Hàm chính của trình bao bọc này là thực thi việc sử dụng lại Mã thông báo để thông báo được chia sẻ trên tất cả các dịch vụ trên nền trước bằng cách sử dụng trình quản lý.


SDK Điều hướng chứa các phương thức tĩnh để khởi tạo và lấy singleton ForegroundServiceManager. Bạn chỉ có thể khởi chạy singleton này một lần trong suốt thời gian hoạt động của SDK Điều hướng. Do đó, nếu sử dụng một trong các lệnh gọi khởi chạy (initForegroundServiceManagerMessageAndIntent() hoặc initForegroundServiceManagerProvider()), bạn nên bao quanh lệnh gọi đó bằng một khối try-catch trong trường hợp đường dẫn đó được nhập lại. SDK Điều hướng sẽ gửi một ngoại lệ thời gian chạy nếu bạn gọi một trong hai phương thức nhiều lần, trừ phi trước tiên bạn xoá tất cả tệp đối chiếu đến ForegroundServiceManager và gọi clearForegroundServiceManager() trước mỗi lệnh gọi tiếp theo.

Bốn tham số của initForegroundServiceManagerMessageAndIntent()application, notificationId, defaultMessageresumeIntent. Nếu ba tham số cuối cùng là rỗng, thì thông báo đó là thông báo SDK điều hướng tiêu chuẩn. Bạn vẫn có thể ẩn các dịch vụ trên nền trước khác trong ứng dụng sau thông báo này. Tham số notificationId chỉ định mã thông báo sẽ được sử dụng cho thông báo. Nếu giá trị này là rỗng, thì hệ thống sẽ sử dụng một giá trị tuỳ ý. Bạn có thể thiết lập rõ ràng để giải quyết các xung đột với các thông báo khác, chẳng hạn như các thông báo từ một SDK khác. defaultMessage là một chuỗi hiển thị khi hệ thống không điều hướng. resumeIntent là một ý định được kích hoạt khi người dùng nhấp vào thông báo. Nếu resumeIntent là giá trị rỗng, thì các lượt nhấp vào thông báo sẽ bị bỏ qua.

Ba tham số của initForegroundServiceManagerProvider()application, notificationIdnotificationProvider. Nếu hai tham số cuối cùng là rỗng, thì thông báo đó là thông báo SDK điều hướng tiêu chuẩn. Tham số notificationId chỉ định mã thông báo sẽ được sử dụng cho thông báo. Nếu giá trị này là rỗng, thì một giá trị tuỳ ý sẽ được sử dụng. Bạn có thể thiết lập rõ ràng để giải quyết xung đột với các thông báo khác, chẳng hạn như thông báo từ một SDK khác. Nếu bạn đặt notificationProvider, thì nhà cung cấp luôn chịu trách nhiệm tạo thông báo để hiển thị.

Phương thức getForegroundServiceManager() của Navigation SDK trả về trình quản lý dịch vụ trên nền trước singleton. Nếu bạn chưa tạo một tệp, thì thao tác này tương đương với việc gọi initForegroundServiceManagerMessageAndIntent() với các tham số rỗng cho notificationId, defaultMessageresumeIntent.

ForegroundServiceManager có ba phương thức đơn giản. Hai phương thức đầu tiên dùng để di chuyển một dịch vụ vào và ra khỏi nền trước, đồng thời thường được gọi từ trong dịch vụ đã tạo. Việc sử dụng các phương thức này đảm bảo rằng các dịch vụ được liên kết với thông báo liên tục dùng chung. Phương thức cuối cùng, updateNotification(), gắn cờ cho trình quản lý rằng thông báo đã thay đổi và cần được hiển thị lại.

Nếu bạn cần toàn quyền kiểm soát thông báo liên tục được chia sẻ, thì API sẽ cung cấp giao diện NotificationContentProvider để xác định trình cung cấp thông báo, trong đó chứa một phương thức duy nhất để nhận thông báo có nội dung hiện tại. Thư viện này cũng cung cấp một lớp cơ sở mà bạn có thể sử dụng để xác định trình cung cấp. Một trong những mục đích chính của lớp cơ sở là cung cấp cách gọi updateNotification() mà không cần truy cập vào ForegroundServiceManager. Nếu sử dụng một thực thể của nhà cung cấp thông báo để nhận thông báo mới, bạn có thể gọi trực tiếp phương thức nội bộ này để hiển thị thông báo trong thông báo.

Tình huống sử dụng

Phần này trình bày chi tiết các trường hợp sử dụng thông báo liên tục được chia sẻ.

Ẩn thông báo liên tục của các dịch vụ trên nền trước của ứng dụng khác
Trường hợp dễ nhất là duy trì hành vi hiện tại và chỉ sử dụng thông báo liên tục để hiển thị thông tin SDK điều hướng. Các dịch vụ khác có thể ẩn sau thông báo này bằng cách sử dụng trình quản lý dịch vụ trên nền trước startForeground() và các phương thức stopForeground().
Ẩn thông báo liên tục của các dịch vụ trên nền trước của ứng dụng khác, nhưng đặt văn bản mặc định hiển thị khi không điều hướng
Tình huống dễ dàng thứ hai là giữ nguyên hành vi hiện tại và chỉ sử dụng thông báo liên tục để hiển thị thông tin SDK điều hướng, ngoại trừ khi hệ thống không điều hướng. Khi hệ thống không điều hướng, chuỗi được cung cấp cho initForegroundServiceManagerMessageAndIntent() sẽ hiển thị thay vì chuỗi SDK điều hướng mặc định đề cập đến "Google Maps". Bạn cũng có thể sử dụng lệnh gọi này để đặt ý định tiếp tục sẽ kích hoạt khi người dùng nhấp vào thông báo.
Kiểm soát toàn bộ quá trình hiển thị thông báo liên tục
Trong trường hợp cuối cùng, bạn cần xác định và tạo trình cung cấp thông báo rồi truyền trình cung cấp đó đến ForegroundServiceManager bằng initForegroundServiceManagerProvider(). Tuỳ chọn này cho phép bạn kiểm soát toàn bộ nội dung hiển thị trong thông báo, nhưng cũng ngắt kết nối thông tin thông báo của SDK chỉ đường khỏi thông báo, do đó xoá các lời nhắc từng chặng hữu ích xuất hiện trong thông báo. Google không cung cấp một phương thức đơn giản để truy xuất thông tin này và chèn thông tin đó vào thông báo.

Ví dụ về trình cung cấp thông báo

Ví dụ về mã sau đây minh hoạ cách tạo và trả về thông báo bằng cách sử dụng một trình cung cấp nội dung thông báo đơn giản.

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 "";
   }
 }
}

Sau khi tạo NotificationContentProviderImpl, bạn sẽ kết nối SDK điều hướng với lớp này bằng mã sau:

ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
mNotification = new NotificationContentProviderImpl(getApplication());
NavigationApi.clearForegroundServiceManager();
NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);

Những điểm cần lưu ý và kế hoạch trong tương lai

  • Hãy nhớ gọi initForegroundServiceManagerMessageAndIntent() hoặc initForegroundServiceManagerProvider() sớm để xác định rõ trường hợp sử dụng dự kiến. Bạn phải gọi phương thức này trước khi tạo một Trình điều hướng mới.
  • Hãy nhớ phát hiện các ngoại lệ từ các lệnh gọi đến initForegroundServiceManagerMessageAndIntent() hoặc initForegroundServiceManagerProvider() trong trường hợp bạn nhập lộ trình mã nhiều lần. Trong Navigation SDK v2.0, việc gọi phương thức này nhiều lần sẽ gửi ra một ngoại lệ đã kiểm tra thay vì ngoại lệ thời gian chạy.
  • Google vẫn có thể phải làm việc để có được kiểu nhất quán trong suốt thời gian hoạt động của thông báo khớp với kiểu tiêu đề.
  • Khi xác định trình cung cấp thông báo, bạn có thể kiểm soát hành vi thông báo quan trọng bằng mức độ ưu tiên.
  • Google không cung cấp một phương thức đơn giản để truy xuất thông tin từng chặng mà nhà cung cấp thông báo có thể chèn vào thông báo.