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)
}
}
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()
}
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)
}
}
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)
}
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)
}
}
Call createNotificationChannel() in your Application class or Activity onCreate:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
}
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")
}
}
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" />
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
)
}
}
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()
}
}
}
Learn More:
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)