Implementing Firebase FCM Push Notifications (WebView + Android)
This is something I implemented around summer 2025 for a separate project. Since this was before I migrated my blog, I never wrote it up. Writing it down now before I forget.
This covers how I added push notifications to a WebView-based Android app.
FCM Basics
What Is an FCM Token?
An identifier that Firebase automatically generates per device. It’s not derived from a user ID — it’s specific to the device. It can change when the app is reinstalled or after a certain period.
What You Need
All you need for push notifications is “user ID + FCM token.” An email address is only for personalizing notification content and isn’t required.
Overall Architecture
1. Web side creates user → issues user ID
2. Pass user ID to the app (via JavaScript Interface)
3. App retrieves FCM token
4. Send user ID + FCM token to server → save to DB
Division of Responsibilities
| Role | Handled by |
|---|---|
| User management & authentication | Web side |
| Business logic | Web side |
| Deciding when/who/what to notify | Web side (server) |
| FCM token retrieval | App side |
| Receiving & displaying notifications | App side |
The app is just “a WebView container + notification capability.” The real work happens on the web side.
Server-Side Implementation (PHP)
Saving FCM Tokens
No need to store these in Firebase — your own DB works fine.
CREATE TABLE user_fcm_tokens (
user_id VARCHAR(255),
fcm_token VARCHAR(500),
created_at TIMESTAMP
);
Installing the Library
composer require kreait/firebase-php
Sending Individual Notifications
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
class NotificationService
{
private $messaging;
public function __construct() {
$factory = (new Factory)
->withServiceAccount('/path/to/service-account.json');
$this->messaging = $factory->createMessaging();
}
public function sendToUser($userId, $title, $body) {
$fcmToken = $this->getUserFcmToken($userId);
if (!$fcmToken) {
throw new Exception('FCM token not found');
}
$message = CloudMessage::withTarget('token', $fcmToken)
->withNotification([
'title' => $title,
'body' => $body,
])
->withData([
'userId' => $userId,
'type' => 'message'
]);
return $this->messaging->send($message);
}
}
Broadcast Notifications (Topics)
Send to all users in a single API call.
// Subscribe to topic when registering token
public function registerToken($userId, $fcmToken) {
$this->saveUserToken($userId, $fcmToken);
$this->messaging->subscribeToTopic('all_users', [$fcmToken]);
}
// Send broadcast notification
public function sendToAllUsers($title, $body) {
$message = CloudMessage::withTarget('topic', 'all_users')
->withNotification([
'title' => $title,
'body' => $body
]);
return $this->messaging->send($message);
}
Android Implementation
Firebase Console Setup
- Create a project in the Firebase Console
- Download
google-services.jsonand place it inapp/
Gradle Configuration
// app/build.gradle
dependencies {
implementation 'com.google.firebase:firebase-messaging:23.0.0'
}
MainActivity Implementation
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupWebView()
}
// JavaScript Interface called from the web side
@JavascriptInterface
fun registerUser(userId: String) {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
val fcmToken = task.result
// Send to server
sendToServer(userId, fcmToken)
// Subscribe to topic for broadcast notifications
FirebaseMessaging.getInstance().subscribeToTopic("all_users")
}
}
}
private fun sendToServer(userId: String, fcmToken: String) {
// HTTP POST to server (using OkHttp or similar)
}
private fun setupWebView() {
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(this, "Android")
webView.loadUrl("https://yourwebapp.com")
}
}
Calling from the Web Side
// Notify the app when user registration is complete
if (window.Android) {
window.Android.registerUser(userId);
}
Things to Watch Out For
- Token refresh: FCM tokens are periodically refreshed, so implement a refresh handler
- Native implementation recommended: FCM via WebView has limitations; implementing FCM on the native side is more reliable
- Request permission: You need to request push notification permission from the user
- Topic name format: Must match
/topics/[a-zA-Z0-9-_.~%]+ - Topic limit: Up to 20 million topics per app
Rough Time Estimates
| Task | Time |
|---|---|
| Create project in Firebase Console | 5 min |
| Download google-services.json | 1 min |
| Gradle configuration | 2 min |
| Code implementation | 30 min |
| Total | ~1 hour |
Basic push notifications can be implemented in about an hour.