Thông báo liên tục được chia sẻ

Kể từ API Android cấp 26, bạn cần phải có 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 yêu cầu quá mức về 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ý thông báo một cách cẩn thận để thông báo được chia sẻ trên mọi dịch vụ, thì sẽ có nhiều thông báo liên tục không loại bỏ đượ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 SDK (chẳng hạn như Navigation SDK) – chạy các dịch vụ trên nền trước độc lập với ứng dụng. Các SDK này có thể có các thông báo độc lập và ổn định riêng, khiến việc hợp nhất các SDK trở nên khó khăn. Để giải quyết những vấn đề này, SDK Điều hướng phiên bản 1.11 đã ra mắt một API đơn giản để giúp quản lý các thông báo liên tục trên ứng dụng, bao gồm cả trong SDK.

Hợp nhất các 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 Android và lớp thông báo liên tục. Chức năng 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ả dịch vụ trên nền trước bằng trình quản lý.


NavigationAPI chứa các phương thức tĩnh để khởi chạy và nhận singleton ForegroundServiceManager. Bạn chỉ có thể khởi tạo 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 tạo (initForegroundServiceManagerMessageAndIntent() hoặc initForegroundServiceManagerProvider()), bạn nên bao quanh lệnh gọi đó bằng khối try/catch trong trường hợp đường dẫn đó được nhập lại. Để ngăn các vấn đề không tương thích, Navigation SDK 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ả tham chiếu đến ForegroundServiceManager và gọi clearForegroundServiceManager() trước mỗi lần gọi tiếp theo. Trong phiên bản 2.0 của SDK điều hướng, một trường hợp ngoại lệ đã kiểm tra sẽ được thêm vào API cho mục đích này.

Bốn tham số của initForegroundServiceManagerMessageAndIntent()application, notificationId, defaultMessageresumeIntent. Nếu 3 tham số cuối cùng có giá trị rỗng, thì thông báo đó là thông báo SDK điều hướng 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 đằng sau thông báo này. Tham số notificationId chỉ định mã thông báo cần 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ể đặt chính sách này một cách rõ ràng để giải quyết 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 rỗng thì các lượt nhấp vào thông báo sẽ bị bỏ qua.

3 tham số của initForegroundServiceManagerProvider()application, notificationIdnotificationProvider. Nếu 2 thông số cuối cùng là giá trị rỗng, thì thông báo đó là thông báo SDK điều hướng chuẩn. Tham số notificationId chỉ định mã thông báo cần 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ể đặt chính sách này một cách rõ ràng để giải quyết 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. Nếu bạn đặt notificationProvider, thì trình cung cấp sẽ luôn chịu trách nhiệm tạo thông báo cần hiển thị.

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

ForegroundServiceManager có 3 phương thức đơn giản. Hai nhiệm vụ đầu tiên dành cho việc di chuyển một dịch vụ vào và ra khỏi nền trước và thường được gọi từ trong dịch vụ đã được 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 được chia sẻ. Phương thức cuối cùng, updateNotification(), sẽ gắn cờ trình quản lý mà thông báo đã thay đổi và phải được kết xuất lại.

Nếu muốn kiểm soát hoàn toàn nội dung của thông báo liên tục được chia sẻ, thì API mới sẽ cung cấp giao diện NotificationContentProvider để xác định trình cung cấp thông báo 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ể tuỳ ý 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 một phương thức dễ dàng để gọi updateNotification() mà không cần truy cập vào ForegroundServiceManager. Phương thức trợ giúp này có thể hữu ích nếu bạn sử dụng một bản sao của trình cung cấp thông báo để nhận tin nhắn thông báo mới. Trong trường hợp đó, bạn có thể gọi trực tiếp phương thức nội bộ này để hiển thị nội dung 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 để 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ụ khác trên nền trước của ứng dụng
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 về 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 các phương thức startForeground()stopForeground() của trình quản lý dịch vụ trên nền trước.
Ẩn thông báo liên tục của các dịch vụ khác trên nền trước của ứng dụng, nhưng đặt văn bản mặc định hiển thị khi không đi theo chỉ dẫn
Tình huống dễ thứ hai 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 về SDK điều hướng, trừ phi hệ thống đang không đi theo chỉ dẫn. Khi hệ thống không đi theo chỉ dẫn, chuỗi đã 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". Lệnh gọi này cũng có thể được dùng để đặ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
Tình huống cuối cùng là xác định và tạo nhà cung cấp thông báo, đồng thời truyền thông báo đó đến ForegroundServiceManager thông qua initForegroundServiceManagerProvider(). Tuỳ chọn này cho phép bạn kiểm soát hoàn toàn nội dung hiển thị trong thông báo, nhưng cũng sẽ ngắt kết nối thông tin thông báo của SDK Điều hướng khỏi thông báo, từ đó xoá các lời nhắc hữu ích cho từng chặng hiển thị trong thông báo. Google chưa 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ề 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 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 "";
   }
 }
}

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 trường hợp ngoại lệ từ các lệnh gọi đến initForegroundServiceManagerMessageAndIntent() hoặc initForegroundServiceManagerProvider() trong trường hợp lộ trình mã được nhập nhiều lần. Trong SDK điều hướng phiên bản 2.0, việc gọi phương thức này nhiều lần sẽ trả về một ngoại lệ đã đánh dấu thay vì một ngoại lệ trong thời gian chạy.
  • Có thể Google vẫn còn việc cần làm để tạo 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 một nhà cung cấp thông báo, bạn có thể kiểm soát hành vi quan trọng theo mức độ ưu tiên.
  • Google chưa 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.