[英]How to send one to one message using Firebase Messaging
I have been trying to read the official docs and guides about how to send message from one device to another.我一直在尝试阅读有关如何将消息从一台设备发送到另一台设备的官方文档和指南。 I have saved registration token of both devices in the Real Time Database, thus I have the registration token of another device.我已经在实时数据库中保存了两个设备的注册令牌,因此我有另一个设备的注册令牌。 I have tried the following way to send the message我尝试了以下方式发送消息
RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken())
.setMessageId(incrementIdAndGet())
.addData("message", "Hello")
.build();
FirebaseMessaging.getInstance().send(message);
However this is not working.但是,这是行不通的。 The other device doesn't receive any message.另一台设备没有收到任何消息。 I am not even sure, if I can use upstream message sending to conduct device to device communication.我什至不确定是否可以使用上游消息发送来进行设备到设备的通信。
PS: I just want to know if device-to-device messaging is possible using FCM? PS:我只想知道是否可以使用 FCM 进行设备到设备的消息传递? If yes, then is the code I used have some issue?如果是,那么我使用的代码是否有问题? If yes, then what is the correct way.如果是,那么正确的方法是什么。
Update:更新:
My question was to ask whether device to device messaging without using any separate server other than firebase could messaging is possible or not, if yes than how, since there's no documentation about it.我的问题是询问设备到设备的消息传递是否可能在不使用除 firebase 之外的任何单独服务器的情况下进行消息传递,如果是,而不是如何,因为没有关于它的文档。 I do not understand what is left to explain here?我不明白这里还有什么要解释的? Anyways I got the answer and will update it as an answer once the question gets reopened.无论如何,我得到了答案,并会在问题重新打开后将其更新为答案。
Firebase has two features to send messages to devices: Firebase 有两个功能可以向设备发送消息:
The Firebase documentation shows this visually: Firebase 文档直观地显示了这一点:
Sending messages from one device directly to another device is not supported through the Firebase Cloud Messaging client-side SDKs. Firebase 云消息传递客户端 SDK 不支持将消息从一个设备直接发送到另一台设备。
Update : I wrote a blog post detailing how to send notifications between Android devices using Firebase Database, Cloud Messaging and Node.js .更新:我写了一篇博文,详细介绍了如何使用 Firebase 数据库、云消息传递和 Node.js 在 Android 设备之间发送通知。
Update 2 : You can now also use Cloud Functions for Firebase to send messages securely, without spinning up a server.更新 2 :您现在还可以使用Cloud Functions for Firebase安全地发送消息,而无需启动服务器。 See this sample use-case to get started.请参阅此示例用例以开始使用。 If you don't want to use Cloud Functions, you can run the same logic on any trusted environment you already have, such as your development machine, or a server you control.如果您不想使用 Cloud Functions,您可以在您已有的任何可信环境上运行相同的逻辑,例如您的开发机器或您控制的服务器。
Warning There is a very important reason why we don't mention this approach anywhere.警告我们没有在任何地方提及这种方法有一个非常重要的原因。 This exposes your server key in the APK that you put on every client device.这会在您放置在每个客户端设备上的 APK 中公开您的服务器密钥。 It can (and thus will) be taken from there and may lead to abuse of your project.它可以(因此将)从那里被拿走,并可能导致您的项目被滥用。 I highly recommend against taking this approach, except for apps that you only put on your own devices.我强烈建议不要采用这种方法,除了你只放在自己设备上的应用程序。 – Frank van Puffelen ——弗兰克·范·普菲伦
Ok, so the answer by Frank was correct that Firebase
does not natively support device to device messaging.好的,所以弗兰克的回答是正确的, Firebase
本身并不支持设备到设备的消息传递。 However there's one loophole in that.但是,其中有一个漏洞。 The Firebase server doesn't identify whether you have send the request from an actual server or are you doing it from your device. Firebase 服务器无法识别您是从实际服务器发送请求还是从您的设备发送请求。
So all you have to do is send a Post Request
to Firebase
's messaging server along with the Server Key.因此,您所要做的就是向Firebase
的消息服务器发送一个Post Request
以及服务器密钥。 Just keep this in mind that the server key is not supposed to be on the device , but there's no other option if you want device-to-device messaging using Firebase Messaging.请记住,服务器密钥不应该在设备上,但如果您希望使用 Firebase 消息传递设备到设备的消息,则没有其他选择。
I am using OkHTTP instead of default way of calling the Rest API.我使用 OkHTTP 而不是调用 Rest API 的默认方式。 The code is something like this -代码是这样的 -
public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
OkHttpClient mClient = new OkHttpClient();
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(TAG, "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(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
}
}
}.execute();
}
String postToFCM(String bodyString) throws IOException {
RequestBody body = RequestBody.create(JSON, bodyString);
Request request = new Request.Builder()
.url(FCM_MESSAGE_URL)
.post(body)
.addHeader("Authorization", "key=" + SERVER_KEY)
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
}
I hope Firebase will come with a better solution in future.我希望 Firebase 将来会提供更好的解决方案。 But till then, I think this is the only way.但在那之前,我认为这是唯一的方法。 The other way would be to send topic message or group messaging.另一种方式是发送主题消息或群组消息。 But that was not in the scope of the question.但这不在问题的范围内。
Update:更新:
The JSONArray is defined like this - JSONArray 的定义如下 -
JSONArray regArray = new JSONArray(regIds);
regIds is a String array of registration ids, you want to send this message to. regIds 是一个注册 id 的字符串数组,您想将此消息发送到。 Keep in mind that the registration ids must always be in an array, even if you want it to send to a single recipient.请记住,注册 ID 必须始终位于数组中,即使您希望将其发送给单个收件人。
I have also been using direct device to device gcm messaging in my prototype.我也一直在我的原型中使用直接设备到设备 gcm 消息传递。 It has been working very well.它一直运作良好。 We dont have any server.我们没有任何服务器。 We exchange GCM reg id using sms/text and then communicate using GCM after that.我们使用短信/文本交换 GCM reg id,然后使用 GCM 进行通信。 I am putting here code related to GCM handling我在这里放了与 GCM 处理相关的代码
**************Sending GCM Message************* **************发送 GCM 消息**************
//Sends gcm message Asynchronously
public class GCM_Sender extends IntentService{
final String API_KEY = "****************************************";
//Empty constructor
public GCM_Sender() {
super("GCM_Sender");
}
//Processes gcm send messages
@Override
protected void onHandleIntent(Intent intent) {
Log.d("Action Service", "GCM_Sender Service Started");
//Get message from intent
String msg = intent.getStringExtra("msg");
msg = "\"" + msg + "\"";
try{
String ControllerRegistrationId = null;
//Check registration id in db
if(RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().size() > 0 ) {
String controllerRegIdArray[] = RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().get(1);
if(controllerRegIdArray.length>0)
ControllerRegistrationId = controllerRegIdArray[controllerRegIdArray.length-1];
if(!ControllerRegistrationId.equalsIgnoreCase("NULL")){
// 1. URL
URL url = new URL("https://android.googleapis.com/gcm/send");
// 2. Open connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// 3. Specify POST method
urlConnection.setRequestMethod("POST");
// 4. Set the headers
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Authorization", "key=" + API_KEY);
urlConnection.setDoOutput(true);
// 5. Add JSON data into POST request body
JSONObject obj = new JSONObject("{\"time_to_live\": 0,\"delay_while_idle\": true,\"data\":{\"message\":" + msg + "},\"registration_ids\":[" + ControllerRegistrationId + "]}");
// 6. Get connection output stream
OutputStreamWriter out = new OutputStreamWriter(urlConnection.getOutputStream());
out.write(obj.toString());
out.close();
// 6. Get the response
int responseCode = urlConnection.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null){
response.append(inputLine);
}
in.close();
Log.d("GCM getResponseCode:", new Integer(responseCode).toString());
}else{
Log.d("GCM_Sender:","Field REGISTRATION_TABLE is null");
}
}else {
Log.d("GCM_Sender:","There is no Registration ID in DB ,please sync devices");
}
} catch (Exception e) {
e.printStackTrace();
//MessageSender.getInstance().sendMessage(msg, Commands.SMS_MESSAGE);
}
}
//Called when service is no longer alive
@Override
public void onDestroy() {
super.onDestroy();
//Do a log that GCM_Sender service has been destroyed
Log.d("Action Service", "GCM_Sender Service Destroyed");
}
}
**************Receiving GCM Message************* **************接收 GCM 消息**************
public class GCM_Receiver extends WakefulBroadcastReceiver {
public static final String RETRY_ACTION ="com.google.android.c2dm.intent.RETRY";
public static final String REGISTRATION ="com.google.android.c2dm.intent.REGISTRATION";
public SharedPreferences preferences;
//Processes Gcm message .
@Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(),
GCMNotificationIntentService.class.getName());
//Start GCMNotificationIntentService to handle gcm message asynchronously
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
/*//Check if DatabaseService is running .
if(!DatabaseService.isServiceRunning) {
Intent dbService = new Intent(context,DatabaseService.class);
context.startService(dbService);
}*/
//Check if action is RETRY_ACTION ,if it is then do gcm registration again .
if(intent.getAction().equals(RETRY_ACTION)) {
String registrationId = intent.getStringExtra("registration_id");
if(TextUtils.isEmpty(registrationId)){
DeviceRegistrar.getInstance().register(context);
}else {
//Save registration id to prefs .
preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("BLACKBOX_REG_ID",registrationId);
editor.commit();
}
} else if (intent.getAction().equals(REGISTRATION)) {
}
}
}
//Processes gcm messages asynchronously .
public class GCMNotificationIntentService extends IntentService{
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
String gcmData;
private final String TAG = "GCMNotificationIntentService";
//Constructor with super().
public GCMNotificationIntentService() {
super("GcmIntentService");
}
//Called when startService() is called by its Client .
//Processes gcm messages .
@Override
protected void onHandleIntent(Intent intent) {
Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Started");
Bundle extras = intent.getExtras();
//Get instance of GoogleCloudMessaging .
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
//Get gcm message type .
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
.equals(messageType)) {
sendNotification("Deleted messages on server: "
+ extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
.equals(messageType)) {
Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
gcmData = extras.getString("message");
Intent actionService = new Intent(getApplicationContext(),Action.class);
actionService.putExtra("data", gcmData);
//start Action service .
startService(actionService);
//Show push notification .
sendNotification("Action: " + gcmData);
//Process received gcmData.
Log.d(TAG,"Received Gcm Message from Controller : " + extras.getString("message"));
}
}
GCM_Receiver.completeWakefulIntent(intent);
}
//Shows notification on device notification bar .
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, BlackboxStarter.class);
//Clicking on GCM notification add new layer of app.
notificationIntent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this).setSmallIcon(R.drawable.gcm_cloud)
.setContentTitle("Notification from Controller")
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
//Play default notification
try {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
}
//Called when service is no longer be available .
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Destroyed");
}
}
I am late but above solutions has helped me to write down this simple answer, you can send your message directly to android devices from android application, here is the simple implementation I have done and it works great for me.我迟到了,但上面的解决方案帮助我写下了这个简单的答案,您可以从 android 应用程序直接将您的消息发送到 android 设备,这是我所做的简单实现,它对我很有用。
compile android volley library编译android volley库
compile 'com.android.volley:volley:1.0.0'
Just copy paste this simple function ;) and your life will become smooth just like knife in butter.只需复制粘贴这个简单的功能;),您的生活就会变得像黄油中的刀一样顺利。 :D :D
public static void sendPushToSingleInstance(final Context activity, final HashMap dataValue /*your data from the activity*/, final String instanceIdToken /*firebase instance token you will find in documentation that how to get this*/ ) { final String url = "https://fcm.googleapis.com/fcm/send"; StringRequest myReq = new StringRequest(Request.Method.POST,url, new Response.Listener<String>() { @Override public void onResponse(String response) { Toast.makeText(activity, "Bingo Success", Toast.LENGTH_SHORT).show(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(activity, "Oops error", Toast.LENGTH_SHORT).show(); } }) { @Override public byte[] getBody() throws com.android.volley.AuthFailureError { Map<String, Object> rawParameters = new Hashtable(); rawParameters.put("data", new JSONObject(dataValue)); rawParameters.put("to", instanceIdToken); return new JSONObject(rawParameters).toString().getBytes(); }; public String getBodyContentType() { return "application/json; charset=utf-8"; } @Override public Map<String, String> getHeaders() throws AuthFailureError { HashMap<String, String> headers = new HashMap<String, String>(); headers.put("Authorization", "key="+YOUR_LEGACY_SERVER_KEY_FROM_FIREBASE_CONSOLE); headers.put("Content-Type","application/json"); return headers; } }; Volley.newRequestQueue(activity).add(myReq); }
Note If you want to send message to topics so you can change parameter instanceIdToken to something like /topics/topicName .注意如果您想向主题发送消息,则可以将参数instanceIdToken更改为/topics/topicName 之类的内容。 For groups implementation is the same but you just need to take care of parameters.对于组的实现是相同的,但您只需要处理参数。 checkout Firebase documentation and you can pass those parameters. 查看 Firebase 文档,您可以传递这些参数。 let me know if you face any issue.如果您遇到任何问题,请告诉我。
According to the new documentation which was updated on October 2, 2018
you must send post request as below根据October 2, 2018
更新的新文档,您必须发送以下帖子请求
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA //Server key
{
"to": "sent device's registration token",
"data": {
"hello": "message from someone",
}
}
To get device's registration token extend FirebaseMessagingService
and override onNewToken(String token)
For more info refer to doc https://firebase.google.com/docs/cloud-messaging/android/device-group要获取设备的注册令牌,请扩展FirebaseMessagingService
并覆盖onNewToken(String token)
有关更多信息,请参阅文档https://firebase.google.com/docs/cloud-messaging/android/device-group
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.