如何实现Android设备接收Firebase实时数据库新消息的通知与后台提醒
嘿,我来帮你搞定Firebase实时数据库的后台消息通知问题!你已经写了NotificationCompat.Builder的开头部分,接下来咱们把整个流程补全,确保就算APP没运行,用户也能收到带声音的通知。
首先得说清楚:直接在客户端后台监听数据库变化在Android高版本上不太靠谱——因为Android 8.0+对后台服务有严格限制,APP进程被杀后监听会停止。所以最稳定的方案是用Firebase Cloud Functions + FCM(Firebase Cloud Messaging),当数据库有新消息时,通过云端自动触发推送通知,不管APP状态如何都能收到。
方案一:Firebase Cloud Functions + FCM(推荐,稳定可靠)
这个方案的逻辑是:当实时数据库里新增一条消息时,Cloud Functions自动触发,获取接收者的FCM令牌,然后发送通知到目标设备。
1. 配置Cloud Functions
首先在本地初始化Firebase Functions项目(确保你已经安装了Firebase CLI):
firebase init functions
然后打开functions/index.js,编写监听数据库的触发函数:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 监听messages节点下的新消息创建事件 exports.sendNewMessageNotification = functions.database.ref('/messages/{messageId}') .onCreate((snapshot, context) => { // 获取新消息的内容 const messageData = snapshot.val(); const recipientId = messageData.recipientId; // 假设你的消息结构里存了接收者的用户ID const senderName = messageData.senderName; const messageText = messageData.text; // 从数据库里获取接收者的FCM令牌(需要提前把用户的FCM令牌存在/users/{userId}/fcmToken路径下) return admin.database().ref(`/users/${recipientId}/fcmToken`).once('value').then(tokenSnapshot => { const fcmToken = tokenSnapshot.val(); if (!fcmToken) { console.log("找不到接收者的FCM令牌,跳过推送"); return null; } // 构建FCM通知内容 const payload = { notification: { title: `${senderName}发来新消息`, body: messageText, sound: 'default' // 启用系统默认通知声音 }, data: { // 可选:传递额外数据,比如消息ID,方便点击通知跳转到对应聊天界面 messageId: context.params.messageId } }; // 发送通知到目标设备 return admin.messaging().sendToDevice(fcmToken, payload).then(response => { console.log("通知发送成功"); return response; }).catch(error => { console.error("发送通知失败:", error); }); }); });
写完后部署到Firebase:
firebase deploy --only functions
2. 客户端配置FCM
在Android项目里添加FCM依赖(用最新版本就行):
implementation 'com.google.firebase:firebase-messaging:24.0.0'
然后创建一个MyFirebaseMessagingService类,继承FirebaseMessagingService,用来处理收到的通知:
public class MyFirebaseMessagingService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); // 收到FCM消息后,构建本地通知显示给用户 sendNotification(remoteMessage); } private void sendNotification(RemoteMessage remoteMessage) { // 提取通知标题和内容 String title = remoteMessage.getNotification().getTitle(); String body = remoteMessage.getNotification().getBody(); // 提取额外数据(比如消息ID) String messageId = remoteMessage.getData().get("messageId"); // 构建点击通知后打开的Intent(这里假设你的聊天界面是ChatActivity) Intent intent = new Intent(this, ChatActivity.class); intent.putExtra("messageId", messageId); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 注意:Android 12+需要添加FLAG_IMMUTABLE或FLAG_MUTABLE PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE ); // 创建通知渠道(Android 8.0+必须) String channelId = "chat_notification_channel"; NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_launcher) // 替换成你的通知小图标 .setContentTitle(title) .setContentText(body) .setAutoCancel(true) // 点击通知后自动消失 .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) // 设置通知声音 .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Android 8.0+创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( channelId, "聊天消息通知", NotificationManager.IMPORTANCE_DEFAULT ); channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); notificationManager.createNotificationChannel(channel); } // 显示通知 notificationManager.notify(0, notificationBuilder.build()); } @Override public void onNewToken(String token) { super.onNewToken(token); // 把新的FCM令牌上传到数据库,方便Cloud Functions获取 String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid(); FirebaseDatabase.getInstance().getReference("users") .child(currentUserId) .child("fcmToken") .setValue(token); } }
最后在AndroidManifest.xml里注册这个服务:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
方案二:客户端后台监听数据库(局限性大,不推荐)
如果你不想用Cloud Functions,也可以在客户端用前台服务监听数据库,但要注意:Android 8.0+对后台服务限制很严,APP在后台一段时间后会被系统回收,监听会停止,只有当APP在前台或者作为前台服务运行时才稳定。
1. 创建前台服务
public class MessageListenerService extends Service { private DatabaseReference messagesRef; private ChildEventListener messageListener; @Override public void onCreate() { super.onCreate(); // 初始化数据库监听 messagesRef = FirebaseDatabase.getInstance().getReference("messages"); messageListener = new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { Message message = snapshot.getValue(Message.class); if (message != null && !message.getSenderId().equals(FirebaseAuth.getInstance().getCurrentUser().getUid())) { // 收到新消息,发送通知 sendNotification(message.getSenderName(), message.getText()); } } // 其他回调方法可以空实现 @Override public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {} @Override public void onChildRemoved(@NonNull DataSnapshot snapshot) {} @Override public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {} @Override public void onCancelled(@NonNull DatabaseError error) {} }; messagesRef.addChildEventListener(messageListener); // 启动前台服务(Android 8.0+必须) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "message_listener_channel"; NotificationChannel channel = new NotificationChannel( channelId, "消息监听服务", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle("正在监听新消息") .setContentText("后台运行中") .build(); startForeground(1, notification); } } private void sendNotification(String senderName, String messageText) { // 这里的通知构建逻辑和方案一的sendNotification方法完全一致,直接复制过去就行 Intent intent = new Intent(this, ChatActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE ); String channelId = "chat_notification_channel"; NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle(senderName + "发来新消息") .setContentText(messageText) .setAutoCancel(true) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( channelId, "聊天消息通知", NotificationManager.IMPORTANCE_DEFAULT ); channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); notificationManager.createNotificationChannel(channel); } notificationManager.notify(0, builder.build()); } @Override public void onDestroy() { super.onDestroy(); // 移除监听,避免内存泄漏 if (messageListener != null) { messagesRef.removeEventListener(messageListener); } } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
2. 注册服务并启动
在AndroidManifest.xml里注册:
<service android:name=".MessageListenerService" android:foregroundServiceType="dataSync" />
然后在APP启动或者用户登录后启动服务:
Intent serviceIntent = new Intent(this, MessageListenerService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); }
最后总结
强烈推荐用方案一,因为FCM是谷歌官方的推送服务,能保证在APP未运行、甚至设备重启后都能收到通知;而Cloud Functions自动触发,不需要客户端一直保持监听,稳定性更高。
内容的提问来源于stack exchange,提问作者Raj Lama

