You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:45:55