[Học Android] Hướng dẫn gửi thông báo với Firebase Cloud Messaging trong Android – Phần 2
![[Học Android] Hướng dẫn gửi thông báo với Firebase Cloud Messaging trong Android – Phần 2](https://webnulled.net/wp-content/uploads/2022/02/android-huong-dan-gui-thong-bao-voi-firebase-cloud-messaging-trong-android-phan-2_6202b1c5965c9.png)
Ở phần trước, chúng ta đã cùng tìm hiểu về Firebase Cloud Messaging, cách gửi và nhận thông báo dạng Notification Messages thông qua một ứng dụng đơn giản. Trong phần 2 này, chúng ta sẽ cùng tìm hiểu về loại FCM message còn lại là Data messages nhé.
Table of Contents
1. Tìm hiểu về Data messages
Như ở bài trước mình đã trình bày thì Data messages là một loại thông báo của Firebase Cloud Messaging. Data messages được sử dụng khi bạn muốn trực tiếp xử lý thông báo ở ứng dụng client mà không cần quan tâm ứng dụng đang ở trạng thái Foreground hay Background. Bởi vì đối với Data messages thì ứng dụng client (android) sẽ xử lý thông qua phương thức onMessageReceived() bất kể ứng dụng đang ở trạng thái Foreground hay Background.
Cấu trúc của một thông báo dạng Data messages:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"name" : "Peter Paker",
"mission" : "Kick ass Thanos!",
"status" : "Open"
}
}
}
Trong đó, data sẽ là nơi chứa dữ liệu mà bạn muốn gửi. Các key dữ liệu name, mission, status ở trên là do mình tự định nghĩa, bạn cũng có thể tự định nghĩa các cặp key – value riêng cho ứng dụng của bạn tại đây.
2. Tạo ứng dụng đơn giản gửi và nhận thông báo dạng Data messages
Bây giờ mình sẽ tạo một hệ thống đơn giản để gửi và nhận Data messages với Firebase như sau:
-
Một trang web bằng php đơn giản để gửi các thông báo, trong thông báo đó sẽ có chứa một Mission gì đó (ví dụ như “Kick ass Thanos” chẳng hạn).
-
Một ứng dụng bằng android để nhận các thông báo này, sau đó hiển thị ra popup với các action để người dùng có thể chấp nhận hoặc từ chối Mission đó.
2.1. Tạo ứng dụng android nhận thông báo
Bước 1: Tạo ứng dụng android và kết nối với Firebase
Bước này các bạn làm tương tự như ở bài trước mình đã trình bày.
Bước 2: Xử lý nhận thông báo và hiển thị thông báo dưới dạng popup nổi trên màn hình:
Bạn cần tạo một service có tên là MyFirebaseService và extends từ FirebaseMessagingService như sau:
public class MyFirebaseService extends FirebaseMessagingService {
private static final String TAG = "FirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
sendNotification(remoteMessage.getData());
}
@Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
sendRegistrationToServer(token);
}
private void sendRegistrationToServer(String token) {
// TODO: Implement this method to save device token to Database.
}
// create Intent follow acction
private Intent createIntent(String actionName, int notificationId, String mission) {
Intent intent = new Intent(this, MissionActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(actionName);
intent.putExtra("NOTIFICATION_ID", notificationId);
intent.putExtra("MISSION", mission);
return intent;
}
private void sendNotification(Map data) {
try {
String name = (String) data.get("name");
String mission = (String) data.get("mission");
int status = Integer.parseInt((String) data.get("status"));
int notificationId = new Random().nextInt();
String title = "Hi " + name + "! You have a new Mission!";
String message = "Mission: " + mission;
Intent acceptIntent = createIntent(MissionActivity.ACCEPT_ACTION, notificationId, mission);
Intent rejectIntent = createIntent(MissionActivity.REJECT_ACTION, notificationId, mission);
Intent intent = createIntent(MissionActivity.SHOW_ACTION, notificationId, mission);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
String channelId = getString(R.string.project_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background))
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_ALL)
.setPriority(NotificationManager.IMPORTANCE_HIGH)
.addAction(new NotificationCompat.Action(
android.R.drawable.sym_call_missed,
"Reject",
PendingIntent.getActivity(this, 0, rejectIntent, PendingIntent.FLAG_CANCEL_CURRENT)))
.addAction(new NotificationCompat.Action(
android.R.drawable.sym_call_outgoing,
"Accept",
PendingIntent.getActivity(this, 0, acceptIntent, PendingIntent.FLAG_CANCEL_CURRENT)));
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(notificationId, notificationBuilder.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Trong đó:
-
onMessageReceived(): đây là phương thức xử lý nhận thông báo từ Firebase gửi về. Như mình đã nói ở trên (cũng như ở bài trước) là thông báo dạng Data messages luôn được phương thức onMessageReceived() xử lý dù ứng dụng ở trạng thái Foreground hay Background. Vì vậy mình sẽ xử lý nhận thông báo và hiển thị lên popup nổi ở đây.
-
onNewToken() và sendRegistrationToServer(): Khi một thiết bị cài đặt ứng dụng thì nó sẽ sinh ra một device_token trong method onNewToken() và đăng kí nó với Firebase. Firebase sẽ dựa vào các token này để gửi thông báo đến các thiết bị. Bạn cần lưu ý rằng token này rất quan trọng nếu bạn muốn gửi thông báo từ Web -> App. Bạn nên lưu token này vào Database của bạn để sử dụng cho việc gửi thông báo từ Web.
-
sendNotification(): đây là phương thức xử lý hiển thị thông báo nổi dạng popup trên màn hình thiết bị. Lưu ý: nếu bạn gửi thông báo đến thiết bị thành công nhưng nó chỉ hiển thị ở thanh trạng thái mà không hiển thị popup nổi trên màn hình thì bạn cần vào setting của thiết bị và cấp quyền hiển thị thông báo cho ứng dụng.
-
createIntent(): phương thức này xử lý tạo các intent tương ứng với các action mà bạn thực hiện với popup thông báo (click button Accept, Reject hoặc click thẳng vào thông báo).
Tiếp theo bạn cần đăng ký service này trong AndroidManifest.xml:
Bước 3: Tạo một activity xử lý hiển thị thông tin tương ứng với các action của popup thông báo
MissionActivity.java
public class MissionActivity extends AppCompatActivity {
public static final String ACCEPT_ACTION = "Accept";
public static final String REJECT_ACTION = "Reject";
public static final String SHOW_ACTION = "Show";
private Intent intent;
private TextView tvMissionContent;
private TextView tvMissionStatus;
private Button btnAccept;
private Button btnReject;
private LinearLayout layoutAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mission);
mappingWidgets();
hideNotification();
process();
}
private void mappingWidgets() {
intent = getIntent();
tvMissionContent = findViewById(R.id.tvMissionContent);
tvMissionStatus = findViewById(R.id.tvMissionStatus);
btnAccept = findViewById(R.id.btnAccept);
btnReject = findViewById(R.id.btnReject);
tvMissionContent.setText(intent.getStringExtra("MISSION"));
layoutAction = findViewById(R.id.layoutAction);
layoutAction.setVisibility(LinearLayout.INVISIBLE);
btnAccept.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
layoutAction.setVisibility(LinearLayout.INVISIBLE);
acceptMission();
}
});
btnReject.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
layoutAction.setVisibility(LinearLayout.INVISIBLE);
rejectMission();
}
});
}
private void process() {
String action = intent.getAction();
if (action == null) {
return;
}
switch (action) {
case ACCEPT_ACTION:
acceptMission();
break;
case SHOW_ACTION:
showMission();
break;
case REJECT_ACTION:
rejectMission();
break;
default:
finish();
break;
}
}
private void hideNotification() {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(getIntent().getIntExtra("NOTIFICATION_ID", -1));
}
private void acceptMission()
{
tvMissionStatus.setText("Accepted");
tvMissionStatus.setTextColor(Color.GREEN);
Toast.makeText(this, "You have been accepted this mission!", Toast.LENGTH_LONG).show();
}
private void showMission()
{
layoutAction.setVisibility(LinearLayout.VISIBLE);
tvMissionStatus.setText("Waiting");
tvMissionStatus.setTextColor(Color.CYAN);
}
private void rejectMission()
{
tvMissionStatus.setText("Rejected");
tvMissionStatus.setTextColor(Color.RED);
Toast.makeText(this, "You have been rejected this mission!", Toast.LENGTH_LONG).show();
}
}
Tạo layout activity_mission.xml
Tạo 2 drawable cho button Accept và Reject
- border_radius_green_button.xml
- border_radius_red_button.xml
2.2. Tạo một web (php) đơn giản để gửi thông báo
Bước 1: Bạn cần tạo một trang view với một form đơn giản gồm có:
-
2 trường là Name và Mission
-
1 button Send Mission để submit form.
Bước 2: Xử lý gửi thông báo dạng Data messages đến Firebase:
function sendNotification($devicesToken, $data) {
$messages = [
'registration_ids' => $devicesToken,
'data' => $data,
];
$headers = [
'Authorization: key=' . 'YOUR_FIRE_BASE_SERVER_KEY',
'Content-Type: application/json',
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($messages));
$result = curl_exec($ch);
curl_close($ch);
if ($result === FALSE) {
throw new Exception('FCM Send Error: ' . curl_error($ch), 500);
}
}
Các bạn dùng function trên để gửi thông báo đến Firebase, trong đó:
-
YOUR_FIRE_BASE_SERVER_KEY: Firebase server key ứng dụng của bạn. Bạn cần vào Firebase Console -> chọn project -> Project setting -> Cloud Messaging để lấy server key này.
-
$devicesToken : đây là một array chứa danh sách các device_token của các thiết bị mà bạn muốn gửi thông báo đến. Token này sinh ra từ method onNewToken() mà mình đã nói ở trên. Nếu ở trên bạn đã lưu các token nào vào Database thì ở đây bạn chỉ cần lấy chúng ra thôi. $devicesToken sẽ có dạng như sau:
$devicesToken = [
'device_token_1',
'device_token_2',
'device_token_3',
....
];
```'
- **$data**: đây là một array chứa dữ liệu (dạng key - value) của thông báo mà bạn muốn gửi đi. Ở đây mình sẽ lấy từ form và nó có sẽ dạng như sau:
```php
$data = [
'name' => 'Peter Paker',
'mission' => 'Kick ass Thanos!',
'status' => 0,
];
2.3. Demo
Ứng dụng ở trạng thái Foreground:
Ứng dụng ở trạng thái Background:
Bạn có thể thấy rõ, dù ứng dụng đang ở trạng thái Foreground hay Background thì popup thông báo nổi cũng đều giống nhau, điều này chứng tỏ cả hai trạng thái khi nhận thông báo đều qua phương thức onMessageReceived() xử lý. Đây cũng chính là đặc điểm của loại thông báo Data messages trong Firebase Cloud Messaging, khác với thông báo dạng Notification messages mình đã giới thiệu với các bạn ở bài viết trước.
3. Kết luận
Qua hai bài viết, mình đã giới thiệu cho các bạn về Firebase Cloud Messaging nói chung và 2 loại thông báo của nó là Notification messages và Data messages nói riêng. Chắc các bạn đã hiểu cách thức gửi và xử lý nhận từng loại thông báo thông qua các ứng dụng đơn giản mình đã trình bày. Vì vậy, tùy vào mục đích sử dụng cũng như ứng dụng của bạn mà lựa chọn loại thông báo thích hợp nhé. Ngoài ra, các bạn cũng có thể sử dụng kết hợp cả 2 loại thông báo này trong ứng dụng của mình.
Hy vọng bài viết của mình sẽ giúp ích cho các bạn trong các dự án sau này.
Cảm ơn các bạn đã đọc bài viết.
Chào thân ái và quyết thắng!
Xem lại Phần 1 tại đây
Theo viblo.asia