I this post, will learn about firebase cloud messaging in android. I will show you a full demonstration with sample app. Let see how we setup firebase cloud messaging in our app
Overview
- Firebase Cloud Messaging is a cross-platform messaging solution that lets you reliably deliver messages free of cost.
- It just allows you to notify a client that a new email, data or other data is available to sync.
- You can send notification messages that are displayed to the user.
- Notification messaging is act as instant messaging. A message can transfer a payload of up to 4KB to the client.
- You can send a message to a single device, group of the device, or to devices subscribed to topic.
Implementation step of firebase cloud messaging in your app
- Create an android project
- Create a firebase project on firebase console
- Connect your app to firebase
- Setup your android app to use the cloud messaging libraries
- Receive the code when the app is background and foreground.
Setup your android app to use the cloud messaging libraries
Navigate your project, In project-level build.gradle file make sure to include Google maven.
build.gradle (project-level)
// add these line classpath 'com.google.gms:google-services:4.2.0'
app/build.gradle
// Add these line implementation 'com.google.firebase:firebase-messaging:20.1.0' implementation 'android.arch.work:work-runtime:1.0.1'
Add this line on top of app/build.gradle
apply plugin: 'com.google.gms.google-services'
Add internet uses permission
<uses-permission android:name="android.permission.INTERNET" />
Set Notification custom icon as default in manifest
<!-- Add these line --> <!-- [START] --> <!-- Set custom default icon --> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_notification" /> <!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message.--> <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" /> <!-- Set fcm default channel--> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id" /> <!-- [END] -->
Let’s create a service class named is CloudMessagingService
In the src folder create a service class named CloudMessagingService which extends FirebaseMessagingService. We have to override two methods which are onNewToken() and onMessageReceived().
Let’s override onNewToken methods.
In this methods make own server request here using your HTTP client
@Override public void onNewToken(String token) { super.onNewToken(token); Log.d(TAG, "Refreshed token: " + token); // make a own server request here using your http client }
Now override onMessageReceived method
Let’s override onMessageReceived method, in this method we received RemoteMessgage instance that contains all message details.
Message Type
Firebase cloud messing allow you to send two types of messages.
- Notification Messages
- It displays the notification. It’s managed by firebase SDK, FCM automatically to the end-user device on behalf of the client app.
- Notification messages contain a predefined set of user-visible keys.
- Data Messages
- Data messages contain only your user defines custom key-value pair.
- Handled by the app itself. It contains key-value pairs, based on this you can do something(such as make some event, store some data, sync some data ).
The maximum payload of both messages type is 4 KB, except when sending messaging from firebase console, which enforces a 1024 character limit.
Check Notification Messages
Check remote message contains a notification payload or not.
// if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); }
Handle DATA messages
Check remote message contains a data payload. If data payload contains more than 10 seconds job then we should run log run job using WorkManager. If the task is within 10 min then we should handle it.
// if remote message contains a data payload. if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); Map<String, String> data = remoteMessage.getData(); String jobType = data.get("type"); /* Check the message contains data If needs to be processed by long running job so check if data needs to be processed by long running job */ if (jobType.equalsIgnoreCase(JobType.LONG.name())) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleLongRunningJob(); } else { // Handle message within 10 seconds handleNow(data); } }
Create and show notification containing the received FCM message.
private void sendNotification(String title, String messageBody) { Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT); String channelId = getString(R.string.default_notification_channel_id); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Since android Oreo notification channel is needed. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Channel human readable title NotificationChannel channel = new NotificationChannel(channelId, "Cloud Messaging Service", NotificationManager.IMPORTANCE_DEFAULT); notificationManager.createNotificationChannel(channel); } notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); }
Finally, your CloudMessagingService class looks like this.
package com.firebasecloudmessaging; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.util.Log; import androidx.core.app.NotificationCompat; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import java.util.Map; public class CloudMessagingService extends FirebaseMessagingService { private static final String TAG = "CloudMessagingService"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); // log the getting message from firebase Log.d(TAG, "From: " + remoteMessage.getFrom()); // if remote message contains a data payload. if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); Map<String, String> data = remoteMessage.getData(); String jobType = data.get("type"); /* Check the message contains data If needs to be processed by long running job so check if data needs to be processed by long running job */ if (jobType.equalsIgnoreCase(JobType.LONG.name())) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleLongRunningJob(); } else { // Handle message within 10 seconds handleNow(data); } } // if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } } @Override public void onNewToken(String token) { super.onNewToken(token); Log.d(TAG, "Refreshed token: " + token); sendRegistrationToServer(token); } /** * Persist token on third-party servers using your Retrofit APIs client. * Modify this method to associate the user's FCM InstanceID token with any server-side account * * @param token The new token. */ private void sendRegistrationToServer(String token) { // make a own server request here using your http client } private void handleNow(Map<String, String> data) { if (data.containsKey("title") && data.containsKey("message")) { sendNotification(data.get("title"), data.get("message")); } } /** * Schedule async work using WorkManager mostly these are one type job. */ private void scheduleLongRunningJob() { OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(DataSyncWorker.class) .build(); WorkManager.getInstance().beginWith(work).enqueue(); } /** * Create and show notification containing the received FCM message. * * @param messageBody FCM message body received. */ private void sendNotification(String title, String messageBody) { Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT); String channelId = getString(R.string.default_notification_channel_id); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Since android Oreo notification channel is needed. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Channel human readable title NotificationChannel channel = new NotificationChannel(channelId, "Cloud Messaging Service", NotificationManager.IMPORTANCE_DEFAULT); notificationManager.createNotificationChannel(channel); } notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); } }
Modify main activity layout file
This file I’m adding a button for logging firebase token.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <Button android:id="@+id/logTokenButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log Token" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Meanwhile, open the MainActivity, and manage followings things
- Handle possible data accompanying notification message
- Log and show the Get new Instance ID token
package com.firebasecloudmessaging; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.InstanceIdResult; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // If a notification message is tapped, any data accompanying the notification // message is available in the intent extras. // Handle possible data accompanying notification message. if (getIntent().getExtras() != null) { for (String key : getIntent().getExtras().keySet()) { Object value = getIntent().getExtras().get(key); Log.d(TAG, "Key: " + key + " Value: " + value); // navigate the app based on param } } // Log and show the Get new Instance ID token Button loginButton = findViewById(R.id.logTokenButton); loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener( new OnCompleteListener<InstanceIdResult>() { @Override public void onComplete(@NonNull Task<InstanceIdResult> task) { if (!task.isSuccessful()) { Log.e(TAG, "getInstanceId failed", task.getException()); return; } // Get new Instance ID token String token = task.getResult().getToken(); Log.d(TAG, token); Toast.makeText(MainActivity.this, token, Toast.LENGTH_SHORT).show(); } }); } }); } }
Conclusion
All done, now run the app and see your app is run and up. you able to see the token on toast and logcat as well. Now send the messaging from android another device. Let’s see your notification is visible on the screen.
In this post, we learned how to implement Firebase Cloud Messaging Android our application. I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.
Happy coding 🙂
Requisition
Please read all Firebase articles in single place.
1 Comment
Hello!
I try to follow the example and doesn’t find this class DataSyncWorker.
private void scheduleLongRunningJob() {
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(DataSyncWorker.class)
.build();
WorkManager.getInstance().beginWith(work).enqueue();
}