如何使用 Firebase 数据库和 Firebase 通知为聊天应用构建通知系统

How to structure a notification system for a chat app using Firebase Database and Firebase Notification

我正在 Android.

中使用 Firebase 数据库开发聊天应用程序

我已经完成了核心(聊天和用户列表活动),但我还没有完成通知系统。

我想要的是,当一个用户被添加到对话(单个或群组)并且另一个用户向该对话写了一条新消息时,第一个用户必须收到一个通知,如果点击打开对话 activity.

我最大的难题是如何构建在后台运行的服务以接收推送通知或收听我需要查看的 firebase 数据库节点以了解是否有任何消息。

我想到了两种不同的方法:

方法 1) 使用 firebase 通知

使用这种方法,当发送方发送一条消息时,我 只需将发送方客户端的数据通知发送到对话中的所有其他客户端,这样接收方将决定显示通知(如果聊天 activity 未打开)并处理点击操作。

通过这种方法,我想我会节省 CPU 消耗,然后是电池。

我不明白的是如何注册用户以接收组通知(主题通知)因为据我了解,我必须让该客户订阅该主题,但是如果应用程序在后台或关闭,它如何知道一个新的组,其用户在里面,已经创建等等它必须订阅主题? 对于两个用户的对话场景,这不是问题,因为我可以将通知直接发送给目标用户,而不需要他订阅任何主题。

方法2)用后台服务监听一个firebase数据库数据节点

使用这种方法,我只需要创建一个 可启动服务来侦听数据库的特定节点 (使用 ValueEventListener)并在数据显示新 message/conversation来了。 一个要监听的节点的例子,可以是关于看不见的消息的数据如下:

conversation_user_unseen_messages
    $conversationId1
        $user1: 3
    $conversationId2
        $user2: 1

然后,如果数据显示新 messages/conversations,android 应用程序客户端将决定显示系统通知。

我认为使用这种方法会更多的能源消耗,因为它必须不断检查数据库上是否有任何新消息。

最后考虑

我发现了一个非常有用的 guide 由神秘的 Frank van Puffelen 写的,它解释了如何设置我需要的系统,使用额外的服务器端组件(node.js 服务器) . 我的最后一个问题是:我需要设置服务器吗? 是比由客户端处理所有事情(例如使用 http 请求)更好的解决方案吗?

您认为最好的解决方案是什么? 非常感谢。

编辑

我还在想办法,但这里有一些考虑。

我必须请求并使用 InstanceID

Instance ID provides a unique ID per instance of your apps.

所以我必须在用户连接时请求一个 InstanceID 并且 InstanceId 可用。

然后不要使用主题

Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, target messages to tokens, not topics.

topic messagin guide that instead suggests to target message to tokens 中所述。

为此,我必须在我的用户数据库参考中收集用户令牌:

users: {
    $userId1: {
        name: "John",
        email: "john@gmail.com",
        token: "Ax8HiP3Edf7....",
    }
}

然后当我的应用程序客户端发送新消息时,它还必须向参与聊天的所有用户发送通知,我已经可以用我当前的数据库结构来做这件事。

如何处理和收集请求? 我实现了一个 node.js 服务器应用程序,它连接到 Firebase 数据库并侦听应用程序发出的通知请求,然后通过 http 调用将通知请求发送到每个目标应用程序。

我什么时候必须注册用户令牌? 当应用程序客户端首次启动或 InstanceID 过期时 (onTokenRefresh)。

这样做正确吗?

编辑 2

我在 FCM 实现中发现了一个大漏洞。似乎我无法处理发送给 iOs 不在前台的应用程序的所有通知。

Data notification documentation

中找到

On iOS, FCM stores the message and delivers it only when the app is in the foreground and has established a FCM connection. On Android, a client app receives a data message in onMessageReceived() and can handle the key-value pairs accordingly.

我需要在应用程序处于后台时捕获数据通知,我特别需要它,因为我想更新应用程序图标上的徽章计数器,让用户知道他有多少条未读消息。

我现在正在尝试 OneSignal 解决方案即使在后台也可以接收通知,它是免费的并且与 GCM 交互。我很遗憾不能留在 Google 但如果我不能使用 FCM 更新徽章计数我必须看看另一边。

如有任何考虑,我们将不胜感激。

方法 1 是您应该使用的方法。 Frank's guide使用的是第一种方式,所以你需要搭建一个服务器

Is it a better solution than handling everything by the clients (using for example http requests)?

是的。如果您在客户端(应用程序)中发送通知,您的 API 密钥将被暴露(通过网络嗅探或逆向工程),您肯定希望避免这种情况。

how to subscribe a user to a new group topic if the app is closed or in the background?

看起来你必须在服务器上创建关系映射,通过调用 https://iid.googleapis.com/iid/v1/IID_TOKEN/rel/topics/TOPIC_NAME 并将 Authorization: key=YOUR_API_KEY 作为 header,完整的描述是 here. Follow this guide 来获得实例 ID 令牌。

希望我的回答能解答您的疑问。干杯:)

现在您可以使用 firebase 函数简单地实现此目的...

这是我的

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.sendFollowerNotification = 
functions.database.ref('/whatever/{groupUID}/{userUID}/{todoUID}')
.onWrite(async (change, context) => {

  const useruuid = context.params.userUID;
  const thisdata = change.after.val();
  console.log('sendto:'+useruuid);

  var ref = admin.database().ref(`userdatas/${useruuid}/phonetkn`);

return ref.once("value", function(snapshot){
     const payload = {
          notification: {

            image: "default",
            sound:"default",
            vibrate:"true" ,

              title: 'New Mission !',
              body: thisdata.titre ,
              color: '#22c064',
              icon: "notification_icon"
          }
     };
     admin.messaging().sendToDevice(snapshot.val(), payload)

}, function (errorObject) {
    console.log("The read failed: " + errorObject.code);
});


});

//