在搜索文档后,我没有找到任何关于如何在不使用外部服务器的情况下使用 FCM 发送设备到设备消息的信息。
例如,如果我正在创建一个聊天应用程序,我需要向用户发送推送通知以告知未读消息,因为他们不会一直在线,我也无法在后台运行一个持久化服务,该服务将始终连接到实时数据库,因为那将消耗过多资源。
那么,当某个用户“B”向用户“A”发送聊天消息时,我应该如何向用户“A”发送推送通知?是否需要外部服务器来完成这项工作,还是只能使用 Firebase 服务器?
在搜索文档后,我没有找到任何关于如何在不使用外部服务器的情况下使用 FCM 发送设备到设备消息的信息。
例如,如果我正在创建一个聊天应用程序,我需要向用户发送推送通知以告知未读消息,因为他们不会一直在线,我也无法在后台运行一个持久化服务,该服务将始终连接到实时数据库,因为那将消耗过多资源。
那么,当某个用户“B”向用户“A”发送聊天消息时,我应该如何向用户“A”发送推送通知?是否需要外部服务器来完成这项工作,还是只能使用 Firebase 服务器?
更新:现在可以使用 Firebase 云函数作为处理推送通知的服务器。在这里查看他们的文档。
============
根据文档,您必须实现一个服务器来处理设备到设备通信中的推送通知。
在编写使用 Firebase 云消息传递的客户端应用程序之前,您必须拥有一个满足以下标准的应用服务器:
...
您需要决定要使用哪些 FCM 连接服务器协议,以使您的应用服务器与 FCM 连接服务器进行交互。请注意,如果您想要从客户端应用程序使用上行消息,则必须使用 XMPP。有关此更详细的讨论,请参见选择 FCM 连接服务器协议。
如果您只需要从服务器向用户发送基本通知,则可以使用他们的无服务器解决方案 Firebase 通知。
在这里查看 FCM 和 Firebase 通知之间的比较:https://firebase.google.com/support/faq/#messaging-difference
Constants.LEGACY_SERVER_KEY
是一个本地类变量,在 Firebase 项目的 设置->云消息传递->Legacy Server key
中可以找到。在下面的代码片段中,需要传递设备注册令牌,即 regToken
,请参阅此处。public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
private void sendNotification(final String regToken) {
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
try {
OkHttpClient client = new OkHttpClient();
JSONObject json=new JSONObject();
JSONObject dataJson=new JSONObject();
dataJson.put("body","Hi this is sent from device to device");
dataJson.put("title","dummy title");
json.put("notification",dataJson);
json.put("to",regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization","key="+Constants.LEGACY_SERVER_KEY)
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
}catch (Exception e){
//Log.d(TAG,e+"");
}
return null;
}
}.execute();
}
如果您希望向特定主题发送消息,请按照以下方式在JSON中替换 regToken
:
json.put("to","/topics/foo-bar")
不要忘记在AndroidManifest.xml文件中添加INTERNET权限。
重要提示:使用上面的代码意味着您的服务器密钥驻留在客户端应用程序中。这是危险的,因为有人可以挖掘您的应用程序并获取服务器密钥以向您的用户发送恶意通知。
发送推送的代码:
private void sendFCMPush() {
String Legacy_SERVER_KEY = YOUR_Legacy_SERVER_KEY;
String msg = "this is test message,.,,.,.";
String title = "my title";
String token = FCM_RECEIVER_TOKEN;
JSONObject obj = null;
JSONObject objData = null;
JSONObject dataobjData = null;
try {
obj = new JSONObject();
objData = new JSONObject();
objData.put("body", msg);
objData.put("title", title);
objData.put("sound", "default");
objData.put("icon", "icon_name"); // icon_name image must be there in drawable
objData.put("tag", token);
objData.put("priority", "high");
dataobjData = new JSONObject();
dataobjData.put("text", msg);
dataobjData.put("title", title);
obj.put("to", token);
//obj.put("priority", "high");
obj.put("notification", objData);
obj.put("data", dataobjData);
Log.e("!_@rj@_@@_PASS:>", obj.toString());
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, Constants.FCM_PUSH_URL, obj,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.e("!_@@_SUCESS", response + "");
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("!_@@_Errors--", error + "");
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization", "key=" + Legacy_SERVER_KEY);
params.put("Content-Type", "application/json");
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
int socketTimeout = 1000 * 60;// 60 seconds
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
requestQueue.add(jsObjRequest);
}
只需调用 sendFCMPush() 函数即可;
是的,不需要任何服务器也可以实现。您可以在客户端创建设备组,然后在组内交换消息。但是有一些限制:
参考:Firebase文档,请查看“管理Android客户端应用程序上的设备组”部分。
1)订阅相同的主题名称,例如:
2)在应用程序内发送消息
Google Cloud Functions让从设备到设备发送推送消息成为可能,而无需应用服务器。我已经创建了一个云函数,在数据库中添加新消息时触发它。
这是node.js
代码。
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin'); admin.initializeApp();
exports.sendNotification = functions.database.ref('/conversations/{chatLocation}/{messageLocation}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
const toIDUser = original.toID;
const isGroupChat = original.isGroupChat;
if (isGroupChat) {
const tokenss = admin.database().ref(`/users/${toIDUser}/tokens`).once('value').then(function(snapshot) {
// Handle Promise
const tokenOfGroup = snapshot.val()
// get tokens from the database at particular location get values
const valuess = Object.keys(tokenOfGroup).map(k => tokenOfGroup[k]);
//console.log(' ____________ddd((999999ddd_________________ ' + valuess );
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(valuess, payload);
}, function(error) {
console.error(error);
});
return ;
} else {
// get token from the database at particular location
const tokenss = admin.database().ref(`/users/${toIDUser}/credentials`).once('value').then(function(snapshot) {
// Handle Promise
// The Promise was "fulfilled" (it succeeded).
const credentials = snapshot.val()
// console.log('snapshot ......snapshot.val().name****^^^^^^^^^^^^kensPromise****** :- ', credentials.name);
//console.log('snapshot.....****snapshot.val().token****^^^^^^^^^^^^kensPromise****** :- ', credentials.token);
const deviceToken = credentials.token;
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(deviceToken, payload);
}, function(error) {
console.error(error);
});
}
return ;
});
这里是如何在不使用除 Firebase 之外的第二个服务器的情况下获取通知的步骤。因此,我们仅使用 Firebase,而不需要额外的服务器。
在移动应用程序代码中,我们使用 Android 库(如 这里)创建自己的通知功能,而不是使用 Firebase 库(如 这里),也不使用 Firebase Cloud Messaging。以下是一个 Kotlin 示例: private fun notification() { createNotificationChannel() val intent = Intent(this, LoginActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)在 Firebase 数据库中创建“待处理通知”集合。文档应包含用户名称(用于发送通知)和源名称(用户点击通知后应前往的位置)。
在应用程序代码中,实现向“待处理通知”集合添加新记录的选项。例如,如果用户 A 向用户 B 发送消息,则在集合中创建具有用户 B ID(将收到通知的用户)的文档。
在应用程序代码中,设置后台服务(当应用程序对用户不可见时)。就像这里一样。在后台服务中,设置一个监听器以侦听“待处理通知”集合中的更改。当带有用户 ID 的新记录进入集合时,请调用第 1 段所创建的通知函数,并从集合中删除相应的记录。