如何使用FCM发送设备到设备通知,而不使用XMPP或任何其他脚本?

22

有没有办法通过FCM从一个Android设备向连接到Firebase数据库的另一个设备发送上行通知消息?

我知道XMPP服务器可以接收上行消息并将通知发送到其他设备。要接收使用上行API发送的消息,我需要实现XMPP服务器,但还有其他方法吗?


6
你的回答采用在应用中嵌入服务器密钥的方法是危险的,不建议将其用于任何实际发送到应用程序/应用商店的内容。 - Frank van Puffelen
4
使用Firebase Cloud Messaging发送设备到设备通知的正确方式需要使用应用程序服务器。这并不像听起来那么困难,所以我在Firebase博客文章使用Firebase数据库和云消息发送Android设备之间的通知中对此进行了记录。 - Frank van Puffelen
至少我们可以单独使用Firebase,但是我们可以避免除Firebase之外的第二个服务器。https://dev59.com/0VoU5IYBdhLWcg3wc2xW#67974553 - Liker777
3个回答

16
目前不能直接从一个设备发送通知消息到另一个设备。
(至少在不引入巨大安全漏洞的情况下是不可能的:下面有更多细节。)

完整详情:
  1. 向用户设备发送消息是一项非常严肃的操作!
    根据有效负载,消息可能导致垃圾邮件、网络钓鱼、执行内部方法。
  2. 您希望此操作仅由受信任的实体允许,这就是为什么FCM发送API要求在身份验证标头中使用SERVER-API-KEY。
  3. 将SERVER-API-KEY添加到您的应用程序代码中(或以某种其他方式将其传达给应用程序)是不安全的。因为apk可以被提取、反编译、检查、在模拟器上执行、在调试下执行等等。
目前最佳实现方法:是在两个设备之间加入某种类型的服务器:
[DeviceA] -- please send message to B -->  [SERVER] -- fcmSendAPI --> [DeviceB]

服务器可以简单到一个PHP页面,也可以是一个更复杂的XMPP实现。

在Node.js中的一个示例可以在这里找到:
使用Firebase数据库和云消息传递在设备之间发送通知


请提供任何书面文字,您的答案在此定义。由于现今的Gradle规则,这是不可能提取、反编译、检查、在模拟器上执行或在调试下执行PK的。 - Vishal Patoliya ツ
现在还有一个使用Firebase云函数的示例,可以充当安全发送消息所需的受信任环境。点击此处查看示例 - Frank van Puffelen

3

最终,在尝试自己维护可靠的服务器脚本 2 个月后,我突然发现了 OneSignal。它完全免费,在 iOS、Android、WP 和浏览器上支持设备到设备的推送消息。

希望不会因为宣传垃圾邮件而受到惩罚,但这目前是完全“无后端”的唯一(而且最简单)方式

此外,这是完全安全的方法。除非知道特殊的 OS 用户 ID,否则没有人可以发送推送,您可以将其存储在 Firebase 数据库中并受到规则保护。

更新:它不是 Firebase 的替代品。它只有推送服务,没有其他功能。

更新2:Firebase 现在拥有 Functions,并且其使用示例已经发送 FCM。您现在不需要任何其他服务器或服务。在官方示例中阅读更多信息:https://github.com/firebase/functions-samples


@FrankvanPuffelen在问题中提到的服务器密钥安全怎么样? - Tord Larsen
2
@ErikHellberg 这个密钥是您提供给OneSignal的,因此客户端应用程序根本不包含它。 - Dmytro Rostopira
Firebase和OneSignal的价格比较如何?我的意思是运行Firebase servlet推送FCM到设备与OneSignal相比。OneSignal是否可以访问Firebase数据库以收集数据? - Tord Larsen
@Erik,请查看OneSignal网站,它是免费的,我将其作为Firebase的补充使用。 - Dmytro Rostopira

2

经过多次尝试,最终我找到了一个解决方案,它完美地运作。

步骤1:包含两个库。

compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.google.firebase:firebase-messaging:9.2.0'
步骤2:在您的MainActivity或想要发送通知的位置。
OkHttpClient mClient = new OkHttpClient();

String refreshedToken = "";//add your user refresh tokens who are logged in with firebase.

JSONArray jsonArray = new JSONArray();
jsonArray.put(refreshedToken);

第三步:创建一个异步任务,向所有设备发送通知。
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {

        new AsyncTask<String, String, String>() {
            @Override
            protected String doInBackground(String... params) {
                try {
                    JSONObject root = new JSONObject();
                    JSONObject notification = new JSONObject();
                    notification.put("body", body);
                    notification.put("title", title);
                    notification.put("icon", icon);

                    JSONObject data = new JSONObject();
                    data.put("message", message);
                    root.put("notification", notification);
                    root.put("data", data);
                    root.put("registration_ids", recipients);

                    String result = postToFCM(root.toString());
                    Log.d("Main Activity", "Result: " + result);
                    return result;
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(String result) {
                try {
                    JSONObject resultJson = new JSONObject(result);
                    int success, failure;
                    success = resultJson.getInt("success");
                    failure = resultJson.getInt("failure");
                    Toast.makeText(MainActivity.this, "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
                } catch (JSONException e) {
                    e.printStackTrace();
                    Toast.makeText(MainActivity.this, "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
                }
            }
        }.execute();
    }

String postToFCM(String bodyString) throws IOException {



   public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
      final MediaType JSON
                = MediaType.parse("application/json; charset=utf-8");

        RequestBody body = RequestBody.create(JSON, bodyString);
        Request request = new Request.Builder()
                .url(Url.FCM_MESSAGE_URL)
                .post(body)
                .addHeader("Authorization", "key=" + "your server key")
                .build();
        Response response = mClient.newCall(request).execute();
        return response.body().string();
    }

步骤 4:在您的按钮的 onclick 中调用


    btnSend.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            sendMessage(jsonArray,"Hello","How r u","Http:\\google.com","My Name is Vishal");
        }
    });

15
这里是Firebase开发人员。这将把您的FCM服务器密钥放入APK中,恶意用户可以找到并使用它代表您发送消息。这是一个非常糟糕的想法,我强烈建议不要这样做。正如FCM工程师在这个答案中所说:发送设备到设备的消息需要在服务器上运行受信任的进程来完成。 - Frank van Puffelen
1
非常感谢@FrankvanPuffelen提醒我服务器密钥的问题,这是我的疏忽! - Vishal Patoliya ツ
1
不,那本质上仍然存在同样的问题。我刚刚在这里回答了一个问题:http://stackoverflow.com/questions/41853694/firebase-messaging-without-server-xmpp-server-proposal/41853937#41853937 - Frank van Puffelen
@VishalPatoliyaツ,当用户点击通知时,是否可以打开特定的活动?例如,目前它会打开主要活动,但我想打开另一个活动。同时,我能否传递一些意图?谢谢。 - Adam
2
我一直在努力尝试着,看了很多教程,但是我无法弄清楚如何将代码组合起来。 - Adam
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接