Firebase Cloud Messaging (FCM) is the go-to solution for implementing push notifications in Android apps. Whether you're building a messaging app, an e-commerce platform, or a social media service, real-time notifications keep users engaged and informed. This tutorial walks you through the entire process of integrating Firebase notifications into your Android project, from initial setup to handling received messages.
Setting Up Firebase in Your Android Project
Before writing any code, you need to connect your Android app to Firebase. Head to the Firebase Console and create a new project or select an existing one. Click the Android icon to register your app, providing your package name (e.g., com.example.myapp). Download the google-services.json file and place it in your app's root directory.
Next, add the Firebase SDK to your project. In your project-level build.gradle file, include the Google services classpath:

| File | Dependency |
|---|---|
| Project-level build.gradle | classpath 'com.google.gms:google-services:4.3.15' |
| App-level build.gradle | implementation 'com.google.firebase:firebase-messaging:23.1.2' |
Apply the Google services plugin at the bottom of your app-level build.gradle file:
apply plugin: 'com.google.gms.google-services'
Don't forget to add the necessary permissions in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Creating a Firebase Messaging Service
To handle incoming notifications, you need to create a service that extends FirebaseMessagingService. This service receives messages and allows you to customize how notifications are displayed. Create a new Java or Kotlin class called MyFirebaseMessagingService:

public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getNotification() != null) {
sendNotification(remoteMessage.getNotification().getTitle(),
remoteMessage.getNotification().getBody());
}
}
}
Register this service in your AndroidManifest.xml within the <application> tag:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Building and Displaying Notifications
The onMessageReceived method is your entry point for processing messages. Here's how to build a proper notification with a channel (required for Android 8.0+):
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, intent,
PendingIntent.FLAG_IMMUTABLE);
String channelId = "fcm_default_channel";
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
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(0, notificationBuilder.build());
}
Handling Data Messages vs Notification Messages
FCM supports two message types that behave differently. Notification messages are automatically displayed by the system when your app is in the background. Data messages are always delivered to onMessageReceived, giving you full control over the user experience.

For maximum flexibility, send data messages from your server or the Firebase Console. Here's a sample payload structure:
- Notification payload: Automatically shows a notification when app is backgrounded
- Data payload: Always triggers
onMessageReceived, allowing custom handling - Combined payload: Shows notification when backgrounded, delivers data when foregrounded
Managing Device Tokens
Each device instance has a unique registration token that FCM uses to route messages. Override onNewToken to handle token refreshes:
@Override
public void onNewToken(String token) {
Log.d("FCM Token", token);
sendTokenToServer(token);
}
Store this token on your backend server to target specific devices. Tokens can change when the app is reinstalled, when the user clears app data, or during periodic refreshes. Always keep your server-side token storage updated.
Testing Your Implementation
Use the Firebase Console to send test notifications. Navigate to Cloud Messaging in your project, click Send your first message, and compose a notification. You can target specific devices using the FCM token or send to topic subscribers.
For automated testing, use the Firebase Admin SDK on your server:
Message message = Message.builder()
.setNotification(Notification.builder()
.setTitle("Test Title")
.setBody("Test Body")
.build())
.setToken(registrationToken)
.build();
FirebaseMessaging.getInstance().send(message);
Best Practices for Production
Implement topic messaging for broad announcements rather than individual device targeting. Use collapse keys to prevent message flooding—only the last message with a given collapse key is delivered when the device comes online. Set appropriate time-to-live values for time-sensitive content, and always handle the case where the user has disabled notifications in system settings.
Monitor delivery metrics through the Firebase Console and integrate with BigQuery for detailed analytics. Test thoroughly across different Android versions and device manufacturers, as custom ROMs often have aggressive battery optimization that can delay or block FCM messages.






















