Firebase

Firebase Cloud Messaging Android

Pinterest LinkedIn Tumblr

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

  1. Jairo Junior Rangel Redondo Reply

    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();
    }

Write A Comment