简体   繁体   English

如何使用 Firebase 消息传递一对一消息

[英]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 Notifications panel in your Firebase Console allows you to send notifications to specific devices, groups of users, or topics that users subscribed to. Firebase 控制台中的通知面板允许您向特定设备、用户组或用户订阅的主题发送通知。
  • by calling Firebase Cloud Messaging API, you can send messages with whatever targeting strategy you prefer.通过调用 Firebase Cloud Messaging API,您可以使用您喜欢的任何定位策略发送消息。 Calling the FCM API requires access to your Server key, which you should never expose on client devices.调用 FCM API 需要访问您的服务器密钥,您不应在客户端设备上公开该密钥。 That's why you should always run such code on an app server.这就是为什么您应该始终在应用服务器上运行此类代码的原因。

The Firebase documentation shows this visually: Firebase 文档直观地显示了这一点:

使用 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 设备,这是我所做的简单实现,它对我很有用。

  1. compile android volley library编译android volley库

    compile 'com.android.volley:volley:1.0.0'
  2. 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.

相关问题 在一个功能上发送多个firebase云消息 - Send multiple firebase cloud messaging on one functions 如何在不托管服务器的情况下不使用 FCM 发送一对一的上游消息? - How to send one-to-one upstream messaging using not not using FCM without hosting a server? 如何向FCM(Firebase云消息传递)令牌的特定用户发送消息? - How to send a message to a specific user of an FCM (Firebase Cloud Messaging) token? 如何使用smack API 4.1.0 android发送一对一消息 - How to send one to one message using smack API 4.1.0 android 如何使用Firebase Cloud Messaging根据Firebase数据发送通知 - How to send notifications depending on the Firebase data using Firebase Cloud Messaging 在Firebase中可以将一个项目用于多个应用程序的优缺点 - Pros and cons of using one project for multiple apps in Firebase Could Messaging 如何使用Firebase云消息传递向所有注册用户发送通知 - how to send notification to all registration user using firebase cloud messaging 如何使用 Firebase 云消息发送组通知? - How to send the Group Notifications using Firebase Cloud messaging? 如何仅将 Firebase 消息传递设置为我应用的一种风格? - How to set Firebase Messaging only to one flavour of my app? 如何使用 Firebase 云消息在前台显示多个通知 - how show more than one notification in foreground with Firebase Cloud Messaging
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM