Как отправить одно сообщение в одно с помощью Firebase Messaging

Я пытался прочитать официальные документы и руководства о том, как отправлять сообщения с одного устройства на другое. Я сохранил регистрационный токен обоих устройств в базе данных реального времени, поэтому у меня есть регистрационный токен другого устройства. Я попробовал следующий способ отправить сообщение

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

однако это не работает. Другое устройство не получает никакого сообщения. Я даже не уверен, могу ли я использовать отправку восходящего сообщения для проведения устройства на устройство связь.

PS: Я просто хочу знать, возможен ли обмен сообщениями между устройствами с помощью FCM? Если да, то код, который я использовал, имеет некоторые проблемы? Если да, то каков правильный путь.

обновление:
Мой вопрос состоял в том, чтобы спросить, возможно ли устройство для обмена сообщениями без использования какого-либо отдельного сервера, кроме firebase, или нет, если да, то как, поскольку об этом нет документации. Я не понимаю, что такое осталось объяснить здесь? В любом случае я получил ответ и обновлю его как ответ, как только вопрос будет открыт.

5 ответов


военнослужащих имеет две функции для отправки сообщений на устройства:

  • панель уведомлений в консоли Firebase позволяет отправлять уведомления определенным устройствам, группам пользователей или темам, на которые подписаны пользователи.
  • вызывая Firebase Cloud Messaging API, вы можете отправлять сообщения с любой стратегией таргетинга, которую вы предпочитаете. Вызов API FCM требует доступа к ключу сервера, который никогда не следует предоставлять на клиентских устройствах. Вот почему ты должен всегда запустить такой код на сервере приложений.

на Firebase документация показывает это визуально:

The two ways to send messages to device with Firebase

отправка сообщений с одного устройства непосредственно на другое устройство не поддерживается через Firebase Cloud Messaging.

обновление: я написал сообщение в блоге подробно как отправлять уведомления между устройствами Android с помощью базы данных Firebase, облачных сообщений и Узел.js.

обновление 2: теперь вы также можете использовать облачные функции для Firebase для безопасной отправки сообщений без вращения сервера. См.этот пример использования начать.


предупреждение существует очень важная причина, почему мы нигде не упоминаем об этом подходе. Это предоставляет ключ сервера в APK, который вы включаете все клиентские устройства. Его можно (и, таким образом, будет) взять из там и может привести к злоупотреблению вашим проектом. Я настоятельно рекомендую против такого подхода, за исключением приложений, которые вы только надеваете ваши собственные устройства. – Франк ван Puffelen

хорошо, так что ответ Фрэнка был исправьте это Firebase изначально не поддерживает обмен сообщениями между устройствами. Однако в этом есть одна лазейка. Сервер Firebase не определяет, отправляете ли вы запрос с фактического сервера или делаете это с вашего устройства.

поэтому все, что вам нужно сделать, это отправить Post Request to Firebase сервер обмена сообщениями вместе с ключом сервера. просто имейте это в виду, что ключ-сервер не должен быть на устройстве, но нет другого варианта, если вы требуется обмен сообщениями между устройствами с помощью Firebase Messaging.

Я использую 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 придет с лучшим решением в будущем. Но до тех пор, я думаю, это единственный способ. Другой способ-отправить сообщение темы или групповые сообщения. Но это не входило в круг вопросов.

обновление:
В JSONArray это определяется вот так -

JSONArray regArray = new JSONArray(regIds);

regIds-это строковый массив идентификаторов регистрации, в который вы хотите отправить это сообщение. Имейте в виду, что регистрационные имена всегда должны быть в массиве, даже если вы хотите, чтобы отправить одному получателю.


Я также использовал прямое устройство для обмена сообщениями GCM в моем прототипе. Он работает очень хорошо. Мы не имеем какой-либо сервер. Мы обмениваемся GCM reg id с помощью sms / text, а затем общаемся с помощью 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");
    }

}

Я опаздываю, но выше решения помогли мне записать этот простой ответ, вы можете отправить свое сообщение непосредственно на устройства android из приложения android, вот простая реализация, которую я сделал, и она отлично работает для меня.

  1. компиляция библиотеки Android volley

    compile 'com.android.volley:volley:1.0.0'
    
  2. просто скопируйте эту простую функцию;) и ваша жизнь станет гладкой, как нож в масле. :Д

    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 Для что-то вроде / темы / topicName. Для группы реализация такая же, но вам просто нужно позаботиться о параметрах. проверка документации Firebase, и вы можете передать эти параметры. дайте мне знать, если у вас возникнут какие-либо проблемы.


согласно новой документации, которая была обновлена на October 2, 2018 вы должны отправить запрос post, как показано ниже

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",
    }
}

чтобы получить регистрационный маркер устройства продлить FirebaseMessagingService и заменить onNewToken(String token) Дополнительные сведения см. В doc https://firebase.google.com/docs/cloud-messaging/android/device-group