Kể từ API Android cấp 26, các dịch vụ trên nền trước phải có thông báo liên tụ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 về tài nguyên hệ thống, đặc biệt là pin. Yêu cầu này có thể gây ra một vấ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 tất cả các dịch vụ, thì có thể có nhiều thông báo liên tục không thể đóng, 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ư Navigation SDK. SDK này 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 của riêng chúng, khiến việc hợp nhất trở nên khó khăn.
Để 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ý các thông báo liên tục trên ứng dụng, kể cả trong SDK.
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. 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ả các dịch vụ trên nền trước bằng cách sử dụng trình quản lý.
Navigation SDK chứa các phương thức tĩnh để khởi chạy và nhận đối tượng ForegroundServiceManager
đơn nhất. Bạn chỉ có thể khởi chạy singleton này một lần trong vòng đời của Navigation SDK. Do đó, nếu dùng một trong các lệnh gọi khởi tạo (initForegroundServiceManagerMessageAndIntent()
hoặc initForegroundServiceManagerProvider()
), thì bạn nên đặt lệnh gọi đó trong một khối try-catch trong trường hợp đường dẫn đó được nhập lại. 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 này nhiều lần, trừ phi trước tiên bạn xoá tất cả các tệp tham chiếu đến ForegroundServiceManager
và gọi clearForegroundServiceManager()
trước mỗi lần gọi tiếp theo.
Bốn tham số của initForegroundServiceManagerMessageAndIntent()
là application
, notificationId
, defaultMessage
và resumeIntent
. Nếu 3 tham số cuối cùng là rỗng, thì thông báo đó là thông báo tiêu chuẩn của Navigation SDK. Bạn vẫn có thể ẩn các dịch vụ khác ở nền trước trong ứng dụng đằng sau thông báo này. Tham số notificationId
chỉ định mã nhận dạng thông báo sẽ được dùng cho thông báo. Nếu giá trị này là rỗng, thì một giá trị tuỳ ý sẽ được dùng. Bạn có thể đặt rõ ràng để giải quyết các xung đột với những thông báo khác, chẳng hạn như thông báo từ một SDK khác. defaultMessage
là một chuỗi xuất hiện 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.
initForegroundServiceManagerProvider()
có 3 tham số là application
, notificationId
và notificationProvider
. Nếu 2 tham số cuối cùng là giá trị rỗng, thì thông báo sẽ là thông báo tiêu chuẩn của Navigation SDK. Tham số notificationId
chỉ định mã thông báo sẽ được dùng cho thông báo. Nếu mã này là rỗng, thì một giá trị tuỳ ý sẽ được dùng. Bạn có thể đặt rõ ràng để giải quyết các xung đột với những thông báo khác, chẳng hạn như thông báo từ một SDK khác. Nếu notificationProvider
được đặt, thì nhà cung cấp luôn chịu trách nhiệm tạo thông báo sẽ được hiển thị.
Phương thức getForegroundServiceManager()
của Navigation SDK trả về singleton trình quản lý dịch vụ trên nền trước. Nếu bạn chưa tạo một đối tượng nào, thì đối tượng đó tương đương với việc gọi initForegroundServiceManagerMessageAndIntent()
bằng các tham số rỗng cho notificationId
, defaultMessage
và resumeIntent
.
ForegroundServiceManager
có 3 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, 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 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 kết xuất lại.
Nếu bạn cần kiểm soát hoàn toàn thông báo liên tục dùng chung, thì API sẽ cung cấp một giao diện NotificationContentProvider
để xác định một trình cung cấp thông báo. Giao diện này 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 để giúp xác định 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 phiên bản của trì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.
Các trường hợp sử dụng
Phần này trình bày chi tiết các trường hợp sử dụng khi dùng thông báo liên tục dùng chung.
- Ẩ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 Navigation SDK. 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()
và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ụ trên nền trước của ứng dụng khác, nhưng đặt văn bản mặc định xuất hiện khi không điều hướng
- Trường hợp dễ nhất 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 Navigation SDK, trừ phi hệ thống không điều hướng. Khi hệ thống không đi theo chỉ dẫn, chuỗi được cung cấp cho
initForegroundServiceManagerMessageAndIntent()
sẽ xuất hiện thay vì chuỗi mặc định của Navigation SDK đề cập đến "Google Maps". Bạn cũng có thể 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 hoàn toàn quá trình hiển thị thông báo liên tục
- Trường hợp cuối cùng yêu cầu xác định và tạo một trình cung cấp thông báo, đồng thời truyền trình cung cấp đó đến
ForegroundServiceManager
bằng cách sử dụnginitForegroundServiceManagerProvider()
. Lựa chọn này giúp bạn kiểm soát hoàn toàn nội dung được 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 Navigation SDK khỏi thông báo, do đó xoá các lời nhắc hữu ích từng chặng xuất hiện trong thông báo. Google không cung cấp phương tiện đơn giản để truy xuất thông tin này và chèn thông tin đó vào thông báo.
Nhà cung cấp thông báo mẫu
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 Navigation SDK với NotificationContentProviderImpl
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ặcinitForegroundServiceManagerProvider()
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 Navigator mới. - Hãy nhớ nắm bắt các ngoại lệ từ các lệnh gọi đến
initForegroundServiceManagerMessageAndIntent()
hoặcinitForegroundServiceManagerProvider()
trong trường hợp đường dẫn mã được nhập nhiều lần. Trong Navigation SDK phiên bản 2.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ì một ngoại lệ thời gian chạy. - Google vẫn có thể cần phải làm việc để có được kiểu dáng nhất quán trong suốt thời gian tồn tại của thông báo khớp với kiểu dáng tiêu đề.
- Khi xác định một trình cung cấp thông báo, bạn có thể kiểm soát hành vi của thông báo quan trọng bằng mức độ ưu tiên.
- Google không cung cấp phương tiện đơn giản để truy xuất thông tin chỉ đường từng chặng mà nhà cung cấp thông báo có thể chèn vào thông báo.