Nhận tin nhắn trong ứng dụng Android

Các 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 chạy ở nền trước nhận được tin nhắn 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 Loại thông báo.

Xử lý thông báo

Để nhận tin nhắn, hãy dùng một dịch vụ mở rộng FirebaseMessagingService. Dịch vụ của bạn phải ghi đè lệnh gọi lại onMessageReceivedonDeletedMessages.

Khoảng thời gian xử lý thông báo có thể ngắn hơn 20 giây tuỳ thuộc vào độ trễ phát sinh trước khi gọi onMessageReceived, bao gồm cả độ trễ hệ điều hành, thời gian khởi động ứng dụng, luồng chính bị các thao tác khác chặn hoặc các lệnh gọi onMessageReceived trước đó mất quá nhiều thời gian. Sau thời gian đó, các hành vi khác nhau của hệ điều hành như loại bỏ quy trình của Android hoặc giới hạn thực thi trong nền của Android O có thể ảnh hưởng đến khả năng hoàn thành công việc của bạn.

onMessageReceived được cung cấp cho hầu hết các loại thông báo, trừ các ngoại lệ sau:

  • Gửi tin nhắn thông báo khi ứng dụng của bạn chạy trong 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à tải trọng dữ liệu, khi nhận ở 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 được phân phối trong phần bổ sung của ý định Hoạt động của 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.
Để biết thêm thông tin về các loại thông báo, hãy xem bài viết Thông báo và thông báo dữ liệ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 các giá trị mặc định để tuỳ chỉnh giao diện 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 sau vào bên trong thẻ application để đặt biểu tượng và màu sắc tuỳ chỉnh mặc đị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 nội dung 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 nội dung thông báo không đặt màu sắc rõ ràng 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à biểu tượng nào được đặt trong tải trọng thông báo, thì Android sẽ hiển thị biểu tượng ứng dụng kết xuất 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à nhận dữ liệu tin nhắn:

Kotlin+KTX

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.
}

Ghi đè onDeletedMessages

Trong một số trường hợp, FCM có thể không gửi tin nhắn. Trường hợp này xảy ra khi có quá nhiều thông báo (>100) đang chờ xử lý cho ứng dụng của bạn trên một thiết bị cụ thể tại thời điểm kết nối hoặc nếu thiết bị chưa kết nối với FCM trong hơn 1 tháng. Trong những trường hợp này, bạn có thể nhận được 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ẽ đồng bộ hoá toàn bộ với máy chủ ứng dụng của bạn. Nếu bạn không gửi tin nhắn cho ứng dụng trên thiết bị đó trong vòng 4 tuần qua, thì FCM sẽ không gọi onDeletedMessages().

Xử lý tin nhắn thông báo trong ứng dụng chạy ở chế độ nền

Khi ứng dụng của bạn chạy trong nền, Android sẽ chuyển các tin nhắn 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.

Trong đó bao gồm cả các thông báo chứa cả tải trọng thông báo và dữ liệu (cũng như tất cả thông báo 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 được phân phối trong phần bổ sung của ý định Hoạt động của trình chạy.

Để biết thông tin chi tiết về quá trình 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 được gửi và mở trên các thiết bị của 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 tin nhắn FCM ở chế độ khởi động trực tiếp

Các nhà phát triển muốn gửi thông báo FCM đến các ứng dụng ngay cả trước khi thiết bị được mở khoá có thể cho phép ứng dụng Android nhận thông báo khi thiết bị đang ở chế độ khởi động trực tiếp. Ví dụ: bạn có thể muốn người dùng ứng dụng nhận được thông báo chuông báo ngay cả trên thiết bị đang khoá.

Khi xây dựng trường hợp sử dụng này, hãy quan sát 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. Điều đặc biệt quan trọng là bạn phải cân nhắc đến chế độ hiển thị của các thông báo hỗ trợ khởi động trực tiếp; bất kỳ người dùng nào 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

  • Bạn phải thiết lập thiết bị cho chế độ khởi động trực tiếp.
  • Thiết bị phải cài đặt phiên bản Dịch vụ Google Play gần đây (19.0.54 trở lên).
  • Ứng dụng phải sử dụng SDK FCM (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

  1. Trong tệp Gradle cấp ứng dụng, hãy thêm 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'
    
  2. Thông báo tính năng khởi động trực tiếp cho FirebaseMessagingService của ứng dụng bằng cách thêm thuộc tính android: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>
    

Quan trọng là bạn phải đảm bảo FirebaseMessagingService này có thể chạy ở chế độ khởi động trực tiếp. Hãy 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ệ thông tin đăng nhập khi chạy ở chế độ khởi động trực tiếp.
  • Dịch vụ không được tìm cách sử dụng các thành phần, chẳng hạn như Activities, BroadcastReceivers hoặc Services khác chưa được đánh dấu là có khả năng nhận biết khởi động trực tiếp 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ệ dành cho thông tin đăng nhập cũng như không được gọi các thành phần không trực tiếpBootAware khi đang 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ụ sẽ cần phải nhận biết được 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 (bạn có thể đưa các SDK này vào ứng dụng mà không gặp sự cố ở chế độ khởi động trực tiếp), nhưng nhiều API Firebase không hỗ trợ gọi ở chế độ khởi động trực tiếp.
  • Nếu ứng dụng đang dùng một Application tuỳ chỉnh, thì Application cũng cần phải nhận biết được khởi động trực tiếp (không có quyền truy cập vào bộ nhớ được bảo vệ 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 thông báo đến các thiết bị ở chế độ khởi động trực tiếp, vui lòng xem phần Gửi tin nhắn hỗ trợ khởi động trực tiếp.