DEV Community

myougaTheAxo
myougaTheAxo

Posted on

FCM Push Notifications: Firebase Cloud Messaging Android Guide

FCM Push Notifications: Firebase Cloud Messaging Android Guide

Firebase Cloud Messaging (FCM) is Google's service for pushing notifications to Android, iOS, and web apps. This guide covers the core patterns for reliable push notifications in Android.

FirebaseMessagingService Setup

The entry point for handling FCM tokens and messages is FirebaseMessagingService:

class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onNewToken(token: String) {
        super.onNewToken(token)
        // Called when FCM generates a new registration token
        // Send this token to your backend to enable targeted pushes
        sendTokenToServer(token)
        Log.d("FCM", "New token: $token")
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        // Called when a message arrives
        // Handle data payload and notification payload
        handlePushMessage(remoteMessage)
    }
}
Enter fullscreen mode Exit fullscreen mode

Handling onNewToken

When FCM generates a token (initial startup, token refresh), onNewToken() is called. Store and send this token to your backend:

private fun sendTokenToServer(token: String) {
    // POST to your server's /register-fcm-token endpoint
    val request = Request.Builder()
        .url("https://yourapi.com/users/token")
        .post(RequestBody.create(
            json_body))
        .build()
    client.newCall(request).execute()
}
Enter fullscreen mode Exit fullscreen mode

Handling onMessageReceived

When a message arrives, onMessageReceived() is called. Messages contain:

  • Notification payload: Displays auto-generated system notification
  • Data payload: Custom key-value pairs for app-level handling
private fun handlePushMessage(remoteMessage: RemoteMessage) {
    // Data payload (custom app logic)
    val data = remoteMessage.data
    if (data.isNotEmpty()) {
        val userId = data["user_id"]
        val action = data["action"]
        Log.d("FCM", "Action: $action for user: $userId")
    }

    // Notification payload (system notification)
    remoteMessage.notification?.let {
        val title = it.title ?: "Notification"
        val body = it.body ?: ""
        showNotification(title, body)
    }
}
Enter fullscreen mode Exit fullscreen mode

NotificationCompat.Builder

For custom notifications, use NotificationCompat.Builder:

import androidx.core.app.NotificationCompat

private fun showNotification(title: String, body: String) {
    val notification = NotificationCompat.Builder(this, "fcm_channel")
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle(title)
        .setContentText(body)
        .setAutoCancel(true)
        .setContentIntent(getPendingIntent())
        .build()

    NotificationManagerCompat.from(this).notify(1, notification)
}
Enter fullscreen mode Exit fullscreen mode

NotificationChannel (Android 8+)

Android 8+ requires NotificationChannel before posting notifications:

import androidx.core.app.NotificationCompat

fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            "fcm_channel",
            "FCM Notifications",
            NotificationManager.IMPORTANCE_HIGH
        ).apply {
            description = "Firebase Cloud Messaging notifications"
            enableLights(true)
            enableVibration(true)
        }
        val manager = getSystemService(NotificationManager::class.java)
        manager?.createNotificationChannel(channel)
    }
}
Enter fullscreen mode Exit fullscreen mode

Call createNotificationChannel() in your Application class or Activity onCreate:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }
}
Enter fullscreen mode Exit fullscreen mode

Topic Subscription

Subscribe to a topic for broadcast messages to multiple devices:

FirebaseMessaging.getInstance().subscribeToTopic("news")
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d("FCM", "Subscribed to 'news' topic")
        }
    }
Enter fullscreen mode Exit fullscreen mode

Then send from your backend to /topics/news endpoint.

POST_NOTIFICATIONS Permission (Android 13+)

Android 13+ requires POST_NOTIFICATIONS permission. Add to AndroidManifest:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Enter fullscreen mode Exit fullscreen mode

Request at runtime:

import androidx.core.content.ContextCompat
import androidx.core.app.ActivityCompat

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(
        this, Manifest.permission.POST_NOTIFICATIONS
    ) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
            this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 100
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Data Payload Handling

Data payloads are app-controlled. Handle them in onMessageReceived():

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)

    val action = remoteMessage.data["action"]
    val deeplink = remoteMessage.data["deeplink"]

    when (action) {
        "update_profile" -> {
            // Navigate to profile page
            navigateToDeeplink(deeplink)
        }
        "new_message" -> {
            // Show badge or custom notification
            updateUnreadCount()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Learn More:

8 Android App Templates → https://myougatheax.gumroad.com

Top comments (0)