Thông báo của Firebase hoạt động khác nhau tuỳ thuộc vào trạng thái nền trước/nền sau của ứng dụng nhận. Nếu muốn các ứng dụng trên nền trước nhận được thông báo hoặc thông báo dữ liệu, bạn cần viết mã để xử lý lệnh gọi lại onMessageReceived
.
Để biết nội dung giải thích về sự khác biệt giữa thông báo và thông báo dữ liệu, hãy xem phần Các loại thông báo.
Xử lý thông báo
Để nhận thông báo, hãy sử dụng một dịch vụ mở rộng
FirebaseMessagingService
.
Dịch vụ của bạn nên ghi đè các lệnh gọi lại onMessageReceived
và onDeletedMessages
.
onMessageReceived
được cung cấp cho hầu hết các loại thông báo, ngoại trừ những loại sau:
-
Thông báo được gửi khi ứng dụng của bạn chạy ở chế độ nền. Trong trường hợp này, thông báo sẽ được gửi đến khay hệ thống của thiết bị. Theo mặc định, khi người dùng nhấn vào một thông báo, trình chạy ứng dụng sẽ mở ra.
-
Tin nhắn có cả tải trọng thông báo và dữ liệu, khi nhận được ở chế độ nền. Trong trường hợp này, thông báo sẽ được gửi đến khay hệ thống của thiết bị và tải trọng dữ liệu sẽ được gửi trong phần bổ sung của ý định trong Hoạt động trình chạy.
Tóm lại:
Trạng thái ứng dụng | Thông báo | Dữ liệu | Cả hai |
---|---|---|---|
Màu nổi | onMessageReceived |
onMessageReceived |
onMessageReceived |
Thông tin khái quát | Khay hệ thống | onMessageReceived |
Thông báo: khay hệ thống Dữ liệu: trong phần bổ sung của ý định. |
Lệnh gọi lại onMessageReceived
được cung cấp thời gian chờ cho phép bạn chỉ cần đăng một thông báo nhưng các bộ hẹn giờ không được thiết kế để cho phép ứng dụng truy cập vào mạng hoặc thực hiện công việc bổ sung. Do đó, nếu ứng dụng của bạn làm bất cứ việc gì phức tạp hơn, bạn cần làm thêm việc để đảm bảo ứng dụng có thể hoàn thành công việc của mình.
Nếu dự kiến ứng dụng của bạn có thể cần gần 10 giây để xử lý một thông báo, thì bạn nên lên lịch cho một công việc WorkManager hoặc làm theo hướng dẫn về WakeLock bên dưới. Trong một số trường hợp, khoảng thời gian xử lý một thông báo có thể ngắn hơn 10 giây, tuỳ thuộc vào độ trễ phát sinh trước khi gọi onMessageReceived
, bao gồm cả độ trễ của hệ điều hành, thời gian khởi động ứng dụng, luồng chính bị chặn bởi các thao tác khác hoặc các lệnh gọi onMessageReceived
trước đó mất quá nhiều thời gian. Sau khi hết thời gian hẹn giờ đó, ứng dụng của bạn có thể phải chịu quy trình tắt hoặc giới hạn thực thi trong nền. Xin lưu ý rằng độ trễ đối với các giao dịch mạng và quá trình khởi động ứng dụng có thể đáng kể. Vì vậy, khi nghi ngờ, hãy lên kế hoạch để quá trình xử lý thông báo của bạn chạy trong thời gian dài nếu có bất kỳ phần phụ thuộc không đồng bộ nào, chẳng hạn như yêu cầu truy cập mạng hoặc tải dữ liệu chuyên sâu.
Chỉnh sửa tệp kê khai ứng dụng
Để sử dụng FirebaseMessagingService
, bạn cần thêm nội dung sau vào tệp kê khai ứng dụng:
<service android:name=".java.MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
Ngoài ra, bạn nên đặt giá trị mặc định để tuỳ chỉnh giao diện của thông báo. Bạn có thể chỉ định một biểu tượng mặc định tuỳ chỉnh và một màu mặc định tuỳ chỉnh được áp dụng bất cứ khi nào các giá trị tương đương không được đặt trong tải trọng thông báo.
Thêm các dòng này vào bên trong thẻ application
để đặt biểu tượng mặc định tuỳ chỉnh và màu tuỳ chỉnh:
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages. See README(https://goo.gl/l4GJaQ) for more. --> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_stat_ic_notification" /> <!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. See README(https://goo.gl/6BKBk7) for more. --> <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
Android hiển thị biểu tượng mặc định tuỳ chỉnh cho
- Tất cả thông báo được gửi từ Trình soạn thông báo.
- Mọi thông báo không đặt biểu tượng một cách rõ ràng trong tải trọng thông báo.
Android sử dụng màu mặc định tuỳ chỉnh cho
- Tất cả thông báo được gửi từ Trình soạn thông báo.
- Mọi thông báo không đặt rõ ràng màu trong tải trọng thông báo.
Nếu bạn không đặt biểu tượng mặc định tuỳ chỉnh và không đặt biểu tượng trong tải trọng thông báo, thì Android sẽ hiển thị biểu tượng ứng dụng được hiển thị bằng màu trắng.
Ghi đè onMessageReceived
Bằng cách ghi đè phương thức FirebaseMessagingService.onMessageReceived
, bạn có thể thực hiện các thao tác dựa trên đối tượng RemoteMessage đã nhận và lấy dữ liệu thông báo:
Kotlin
override fun onMessageReceived(remoteMessage: RemoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: ${remoteMessage.from}") // Check if message contains a data payload. if (remoteMessage.data.isNotEmpty()) { Log.d(TAG, "Message data payload: ${remoteMessage.data}") // Check if data needs to be processed by long running job if (needsToBeScheduled()) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob() } else { // Handle message within 10 seconds handleNow() } } // Check if message contains a notification payload. remoteMessage.notification?.let { Log.d(TAG, "Message Notification Body: ${it.body}") } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
Java
@Override public void onMessageReceived(RemoteMessage remoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: " + remoteMessage.getFrom()); // Check if message contains a data payload. if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); if (/* Check if data needs to be processed by long running job */ true) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob(); } else { // Handle message within 10 seconds handleNow(); } } // Check if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
Duy trì trạng thái bật của thiết bị trong khi xử lý thông báo FCM
Nếu cần giữ thiết bị ở trạng thái thức trong khi xử lý một thông báo FCM, thì ứng dụng của bạn sẽ cần giữ một WakeLock trong thời gian này hoặc cần tạo một tác vụ WorkManager. WakeLock hoạt động hiệu quả đối với các hoạt động xử lý ngắn có thể vượt quá thời gian chờ mặc định onMessageReceived
. Đối với các quy trình công việc mở rộng, chẳng hạn như gửi nhiều RPC nối tiếp đến máy chủ, việc sử dụng một công việc WorkManager sẽ phù hợp hơn so với WakeLock. Trong phần này, chúng ta sẽ tập trung vào cách sử dụng WakeLock. WakeLock ngăn thiết bị chuyển sang chế độ ngủ trong khi ứng dụng của bạn đang chạy. Điều này có thể dẫn đến việc tăng mức sử dụng pin. Vì vậy, bạn chỉ nên sử dụng WakeLock trong trường hợp ứng dụng của bạn không được tạm dừng trong khi xử lý thông báo, chẳng hạn như:
- Thông báo cho người dùng có giới hạn thời gian.
- Tương tác với một thứ gì đó bên ngoài thiết bị mà không nên bị gián đoạn (chẳng hạn như truyền dữ liệu qua mạng hoặc giao tiếp với một thiết bị khác, như đồng hồ đã ghép nối).
Trước tiên, bạn cần đảm bảo rằng ứng dụng của mình yêu cầu quyền WakeLock (SDK FCM bao gồm quyền này theo mặc định, vì vậy, thường thì bạn không cần thêm gì cả).
<uses-permission android:name="android.permission.WAKE_LOCK" />
Sau đó, ứng dụng của bạn sẽ cần lấy một WakeLock khi bắt đầu lệnh gọi lại FirebaseMessagingService.onMessageReceived()
và giải phóng WakeLock đó khi kết thúc lệnh gọi lại.
FirebaseMessagingService
tuỳ chỉnh của ứng dụng:
@Override public void onMessageReceived(final RemoteMessage message) { // If this is a message that is time sensitive or shouldn't be interrupted WakeLock wakeLock = getSystemService(PowerManager.class).newWakeLock(PARTIAL_WAKE_LOCK, "myApp:messageReceived"); try { wakeLock.acquire(TIMEOUT_MS); // handle message ... finally { wakeLock.release(); } }
Ghi đè onDeletedMessages
Trong một số trường hợp, FCM có thể không gửi được tin nhắn. Điều này xảy ra khi có quá nhiều thông báo (>100) đang chờ ứng dụng của bạn trên một thiết bị cụ thể tại thời điểm thiết bị đó kết nối hoặc nếu thiết bị chưa kết nối với FCM trong hơn một tháng. Trong những trường hợp này, bạn có thể nhận được một lệnh gọi lại đến FirebaseMessagingService.onDeletedMessages()
. Khi phiên bản ứng dụng nhận được lệnh gọi lại này, phiên bản đó sẽ thực hiện quá trình đồng bộ hoá đầy đủ với máy chủ ứng dụng của bạn. Nếu bạn chưa gửi tin nhắn cho ứng dụng trên thiết bị đó trong vòng 4 tuần qua, FCM sẽ không gọi cho onDeletedMessages()
.
Xử lý thông báo trong ứng dụng ở chế độ nền
Khi ứng dụng của bạn ở chế độ nền, Android sẽ chuyển các thông báo đến khay hệ thống. Theo mặc định, khi người dùng nhấn vào thông báo, trình chạy ứng dụng sẽ mở ra.
Điều này bao gồm cả những thông báo chứa cả thông báo và tải trọng dữ liệu (cũng như tất cả thông báo được gửi từ Bảng điều khiển thông báo). Trong những trường hợp này, thông báo sẽ được gửi đến khay hệ thống của thiết bị và tải trọng dữ liệu sẽ được gửi trong các phần bổ sung của ý định trong Hoạt động trình chạy.
Để biết thông tin chi tiết về việc gửi tin nhắn đến ứng dụng của bạn, hãy xem trang tổng quan báo cáo FCM. Trang tổng quan này ghi lại số lượng tin nhắn đã gửi và mở trên các thiết bị Apple và Android, cùng với dữ liệu về "lượt hiển thị" (thông báo mà người dùng nhìn thấy) cho các ứng dụng Android.
Nhận thông báo FCM ở chế độ khởi động trực tiếp
Nhà phát triển muốn gửi tin nhắn FCM đến các ứng dụng ngay cả trước khi thiết bị được mở khoá có thể cho phép một ứng dụng Android nhận tin nhắn khi thiết bị ở chế độ khởi động trực tiếp. Ví dụ: bạn có thể muốn người dùng ứng dụng của mình nhận được thông báo báo thức ngay cả trên thiết bị ở trạng thái khoá.
Khi xây dựng trường hợp sử dụng này, hãy tuân thủ các phương pháp hay nhất và hạn chế chung đối với chế độ khởi động trực tiếp. Bạn cần đặc biệt cân nhắc khả năng hiển thị của các thông báo có bật tính năng khởi động trực tiếp; mọi người dùng có quyền truy cập vào thiết bị đều có thể xem các thông báo này mà không cần nhập thông tin đăng nhập của người dùng.
Điều kiện tiên quyết
- Thiết bị phải được thiết lập ở chế độ khởi động trực tiếp.
- Thiết bị phải cài đặt phiên bản gần đây của Dịch vụ Google Play (19.0.54 trở lên).
- Ứng dụng phải sử dụng FCM SDK (
com.google.firebase:firebase-messaging
) để nhận thông báo FCM.
Bật tính năng xử lý thông báo ở chế độ khởi động trực tiếp trong ứng dụng của bạn
Trong tệp Gradle ở cấp ứng dụng, hãy thêm một phần phụ thuộc vào thư viện hỗ trợ khởi động trực tiếp FCM:
implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0'
Cho phép
FirebaseMessagingService
của ứng dụng nhận biết chế độ khởi động trực tiếp bằng cách thêm thuộc tínhandroid:directBootAware="true"
vào tệp kê khai ứng dụng:<service android:name=".java.MyFirebaseMessagingService" android:exported="false" android:directBootAware="true"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
Bạn cần đảm bảo rằng FirebaseMessagingService
này có thể chạy ở chế độ khởi động trực tiếp. Kiểm tra các yêu cầu sau:
- Dịch vụ không được truy cập vào bộ nhớ được bảo vệ bằng thông tin đăng nhập khi chạy ở chế độ khởi động trực tiếp.
- Dịch vụ không được cố gắng sử dụng các thành phần, chẳng hạn như
Activities
,BroadcastReceivers
hoặcServices
khác không được đánh dấu là nhận biết quá trình khởi động trực tiếp trong khi chạy ở chế độ khởi động trực tiếp. - Mọi thư viện mà dịch vụ sử dụng cũng không được truy cập vào bộ nhớ được bảo vệ bằng thông tin đăng nhập cũng như không được gọi các thành phần non-directBootAware trong khi chạy ở chế độ khởi động trực tiếp. Điều này có nghĩa là mọi thư viện mà ứng dụng sử dụng được gọi từ dịch vụ đều cần nhận biết quá trình khởi động trực tiếp, hoặc ứng dụng sẽ cần kiểm tra xem ứng dụng có đang chạy ở chế độ khởi động trực tiếp hay không và không gọi các thư viện đó ở chế độ đó. Ví dụ: Firebase SDK hoạt động với tính năng khởi động trực tiếp (có thể được đưa vào một ứng dụng mà không làm ứng dụng đó gặp sự cố ở chế độ khởi động trực tiếp), nhưng nhiều API Firebase không hỗ trợ việc được gọi ở chế độ khởi động trực tiếp.
- Nếu ứng dụng đang sử dụng một
Application
tuỳ chỉnh, thìApplication
cũng cần nhận biết chế độ khởi động trực tiếp (không có quyền truy cập vào bộ nhớ được bảo vệ bằng thông tin đăng nhập ở chế độ khởi động trực tiếp).
Để biết hướng dẫn về cách gửi tin nhắn đến các thiết bị ở chế độ khởi động trực tiếp, hãy xem phần Gửi tin nhắn có bật chế độ khởi động trực tiếp.