Android跨应用通知推送:基于Firebase向指定员工推送方案咨询
Hey there! Let's break down how to implement this targeted push notification feature step by step—since you already have the core setup (two apps connected to Firebase, report submission, and a way to match on-duty employees), this should fit right in.
Core Tech Stack to Use
- Firebase Cloud Messaging (FCM): Handles sending and receiving push notifications between your apps.
- Firebase Cloud Functions: Triggers automatically when a new report is added to Realtime Database, fetches the right employee's FCM token, and sends the notification.
- Firebase Realtime Database: Stores report data, employee schedules, and FCM tokens mapped to employee IDs.
Step 1: Store FCM Tokens for Employees
First, your employee app needs to save each employee's FCM token to the Realtime Database, linked to their unique employee ID. This lets your Cloud Function know exactly which device to send the notification to.
Android Code (Employee App)
Add this code to your employee app's main activity or a dedicated service to retrieve and save the FCM token:
import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.database.FirebaseDatabase // Call this when the employee logs in or when the FCM token updates fun saveFcmToken(employeeId: String) { FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { return@addOnCompleteListener } // Get new FCM registration token val token = task.result // Save token to Realtime Database at /fcmTokens/{employeeId} FirebaseDatabase.getInstance().getReference("fcmTokens/$employeeId") .setValue(token) .addOnSuccessListener { // Token saved successfully } .addOnFailureListener { // Handle error } } } // Also listen for token updates (FCM tokens can change over time) class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) // Get the currently logged-in employee's ID (you should have this stored locally) val currentEmployeeId = "EMP123" // Replace with actual employee ID retrieval logic saveFcmToken(currentEmployeeId) } }
Don't forget to declare the service in your AndroidManifest.xml:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
Step 2: Write a Cloud Function to Trigger Notifications
Next, create a Cloud Function that listens for new reports added to /reports in Realtime Database. When a new report is added, it will:
- Fetch the report details.
- Use your existing method to get the on-duty employee's ID.
- Retrieve that employee's FCM token from
/fcmTokens. - Send a push notification via FCM.
Cloud Function Code (Node.js)
First, make sure you have the Firebase CLI set up, then create a new function:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.sendReportNotification = functions.database.ref("/reports/{reportId}") .onCreate(async (snapshot, context) => { // 1. Get the new report data const reportData = snapshot.val(); const reportTitle = reportData.title; const reportDescription = reportData.description; const timestamp = reportData.timestamp; // 2. Get the on-duty employee ID using your existing method // Replace this with your actual logic to match the on-duty employee const onDutyEmployeeId = await getOnDutyEmployeeId(timestamp); if (!onDutyEmployeeId) { functions.logger.log("No on-duty employee found for this report"); return null; } // 3. Fetch the employee's FCM token from the database const fcmTokenSnapshot = await admin.database() .ref(`/fcmTokens/${onDutyEmployeeId}`) .once("value"); const fcmToken = fcmTokenSnapshot.val(); if (!fcmToken) { functions.logger.log(`No FCM token found for employee ${onDutyEmployeeId}`); return null; } // 4. Build and send the notification const payload = { notification: { title: "New Report Submitted", body: `Title: ${reportTitle}\nDescription: ${reportDescription}`, clickAction: "OPEN_REPORT_DETAILS" // Optional: For deep linking }, data: { reportId: context.params.reportId, reportTitle: reportTitle // Add any other data you need for the employee app }, token: fcmToken }; return admin.messaging().send(payload) .then((response) => { functions.logger.log("Notification sent successfully:", response); return response; }) .catch((error) => { functions.logger.log("Error sending notification:", error); return error; }); }); // Replace this with your existing method to get the on-duty employee ID async function getOnDutyEmployeeId(timestamp) { // Example logic: Query your /schedules node to find the employee on duty at the timestamp const schedulesSnapshot = await admin.database() .ref("/schedules") .orderByChild("startTime") .endAt(timestamp) .orderByChild("endTime") .startAt(timestamp) .limitToFirst(1) .once("value"); if (schedulesSnapshot.exists()) { const schedule = schedulesSnapshot.val(); // Assuming schedules are stored as { employeeId: { startTime: ..., endTime: ... } } return Object.keys(schedule)[0]; } return null; }
Deploy this function using the Firebase CLI:
firebase deploy --only functions
Step 3: Handle Notifications in the Employee App
Finally, update your MyFirebaseMessagingService to handle incoming notifications and display them, plus handle deep linking if needed:
import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat class MyFirebaseMessagingService : FirebaseMessagingService() { private val CHANNEL_ID = "REPORT_NOTIFICATIONS" override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // Create notification channel (required for Android O and above) createNotificationChannel() // Get notification data val title = remoteMessage.notification?.title ?: "New Report" val body = remoteMessage.notification?.body ?: "A new report has been submitted" val reportId = remoteMessage.data["reportId"] // Create intent to open report details activity val intent = Intent(this, ReportDetailsActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra("REPORT_ID", reportId) } val pendingIntent: PendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) // Build the notification val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) // Replace with your app's icon .setContentTitle(title) .setContentText(body) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) // Show the notification val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(0, notificationBuilder.build()) } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = "Report Notifications" val descriptionText = "Alerts for new submitted reports" val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText } val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } }
Key Notes to Remember
- Token Freshness: FCM tokens can expire or change, so always update the database when
onNewTokenis called in the employee app. - Security: Make sure your Realtime Database rules restrict access to
/fcmTokensso only authenticated employees can write their own tokens. Example rule:{ "rules": { "fcmTokens": { "$employeeId": { ".write": "$employeeId === auth.uid", ".read": "auth != null && auth.token.admin === true" // Allow Cloud Functions (admin) to read } } } } - Error Handling: The Cloud Function includes basic error handling, but you can expand it to handle cases where no token exists or the employee is not found (e.g., log to Firebase Crashlytics or send a fallback notification to a supervisor).
内容的提问来源于stack exchange,提问作者J. Adam

