如何使用Firebase消息发送一对一消息

我一直在尝试阅读关于如何从一个设备发送消息到另一个设备的官方文档和指南。 我已经在实时数据库中保存了这两个设备的注册令牌,因此我有另一个设备的注册令牌。 我已经尝试了以下方式发送消息

RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken()) .setMessageId(incrementIdAndGet()) .addData("message", "Hello") .build(); FirebaseMessaging.getInstance().send(message); 

但是这不起作用。 其他设备不会收到任何消息。 我甚至不确定,如果我可以使用上行消息发送进行设备到设备的通信。

PS:我只想知道是否可以使用FCM进行设备到设备的消息传递? 如果是,那么我使用的代码有一些问题? 如果是,那么正确的方法是什么。

更新:
我的问题是询问设备到设备的消息传递,而不使用除firebase以外的任何单独的服务器可以消息传递是否可能,如果是的话比如何,因为没有关于它的文档。 我不明白这里要解释什么? 无论如何,我得到了答案,并将问题重新打开后,将其作为答案更新。

Firebase有两个function可以将消息发送到设备:

  • Firebase控制台中的“通知”面板允许您将通知发送到特定设备,用户组或用户订阅的主题。
  • 通过调用Firebase Cloud Messaging API,您可以使用您喜欢的任何定位策略发送消息。 调用FCM API需要访问您的服务器密钥,您绝不应在客户端设备上公开这些密钥。 这就是为什么你应该总是在应用服务器上运行这样的代码。

Firebase文档直观地显示了这一点:

使用Firebase将消息发送到设备的两种方法

通过Firebase云消息传递不支持将消息从一台设备直接发送到另一台设备。

更新 :我写了一篇博客文章,详细介绍如何使用Firebase数据库,云消息传递和Node.js在Android设备之间发送通知 。

更新2 :现在,您还可以使用Firebase的云端function安全地发送邮件,而无需启动服务器。 请参阅此示例用例以开始。

警告为什么我们在任何地方都没有提到这个方法有一个非常重要的原因。 这暴露了您放在每个客户端设备上的APK中的服务器密钥。 它可以(并因此)从那里被拿走,并可能导致滥用您的项目。 我强烈build议不要采取这种方法,除了您只能在自己的设备上使用的应用程序。 – Frank van Puffelen

好的,所以弗兰克的回答是正确的, Firebase本身不支持设备到设备的消息。 但是这里有一个漏洞。 Firebase服务器不会识别您是从实际的服务器发送请求还是从您的设备进行此操作。

因此,您所要做的就是向服务器密钥和Firebase的消息服务器发送一个Post Request只要记住服务器密钥不应该在设备上 ,但是如果您想要使用Firebase消息传递设备到设备消息,则没有其他select。

我正在使用OkHTTP而不是调用Rest API的默认方式。 代码是这样的 –

 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(); } 

我希望未来Firebase将会有更好的解决scheme。 但在那之前,我认为这是唯一的方法。 另一种方法是发送话题消息或群组消息。 但这不在问题的范围之内。

更新:
JSONArray是这样定义的 –

 JSONArray regArray = new JSONArray(regIds); 

regIds是一个注册IDstring数组,你想发送这个消息给。 请记住,注册ID必须始终位于数组中,即使您希望将其发送给单个收件人。

我也一直使用直接的设备来设备gcm消息在我的原型。 它工作得很好。 我们没有任何服务器。 我们使用短信/文本交换GCM注册ID,然后使用GCM进行通信。 我在这里把与GCM处理有关的代码放在这里

**************发送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"); } } 

**************接收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"); } } 

我迟到了,但以上的解决scheme已经帮助我写下这个简单的答案,你可以直接从android应用程序发送到android设备的消息,这是我做了简单的实现,它对我很好。

  1. 编译android volley库

     compile 'com.android.volley:volley:1.0.0' 
  2. 只要复制粘贴这个简单的function;)和你的生活将变得光滑,就像黄油刀。 :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); return headers; } }; Volley.newRequestQueue(activity).add(myReq); } 

注意如果要将消息发送到主题,则可以将参数instanceIdToken更改为/ topics / topicName之类的内容 。 对于组的实现是相同的,但你只需要照顾参数。 结帐Firebase文档,您可以传递这些参数。 让我知道你是否面临任何问题。