web-dev-qa-db-fra.com

Comment envoyer un message à un autre à l'aide de la messagerie Firebase

J'ai essayé de lire la documentation et les guides officiels pour savoir comment envoyer un message d'un appareil à un autre. J'ai sauvegardé le jeton d'enregistrement des deux appareils dans la base de données en temps réel. J'ai donc le jeton d'enregistrement d'un autre appareil. J'ai essayé la méthode suivante pour envoyer le message

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

Cependant cela ne fonctionne pas. L'autre appareil ne reçoit aucun message. Je ne sais même pas si je peux utiliser l'envoi de messages en amont pour établir une communication entre périphériques. 

PS: Je veux juste savoir si la messagerie entre périphériques est possible avec FCM? Si oui, le code que j'ai utilisé a-t-il un problème? Si oui, alors quelle est la bonne manière.

Mettre à jour:
Ma question était de savoir si la messagerie de périphérique à périphérique sans utiliser de serveur distinct de celui de Firebase pouvait ou non être un service de messagerie, dans l’affirmative, car il n’existe aucune documentation à ce sujet. Je ne comprends pas ce qui reste à expliquer ici? Quoi qu'il en soit, j'ai la réponse et la mettrai à jour une fois la question rouverte.

9
noob

Warning Il y a une raison très importante pour laquelle nous ne mentionnons pas cette approche nulle part. Cela expose votre clé de serveur dans l'APK que vous mettez sur chaque périphérique client. Il peut (et donc sera) pris de là et peut conduire à un abus de votre projet. Je recommande fortement contre cette approche, sauf pour les applications que vous ne mettez que sur vos propres appareils. - Frank van Puffelen

Ok, la réponse de Frank était donc correcte, à savoir que Firebase ne prend pas nativement en charge la messagerie de périphérique à périphérique. Cependant, il y a une faille dans cela. Le serveur Firebase n'indique pas si vous avez envoyé la demande depuis un serveur réel ou si vous le faites depuis votre périphérique. 

Donc, tout ce que vous avez à faire est d’envoyer un Post Request au serveur de messagerie de Firebase avec la clé de serveur. Gardez simplement cela à l'esprit que la clé de serveur n'est pas supposée être sur le périphérique, mais il n'y a pas d'autre option si vous souhaitez une messagerie entre périphériques à l'aide de la messagerie Firebase. 

J'utilise OkHTTP au lieu de la méthode par défaut d'appeler l'API Rest. Le code est quelque chose comme ça - 

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

J'espère que Firebase apportera une meilleure solution à l'avenir. Mais jusque-là, je pense que c'est le seul moyen. L'autre façon serait d'envoyer un message de sujet ou un message de groupe. Mais ce n'était pas dans le champ de la question. 

Mettre à jour:
Le JSONArray est défini comme ceci - 

JSONArray regArray = new JSONArray(regIds);

regIds est un tableau String d'identifiants d'enregistrement auxquels vous souhaitez envoyer ce message. N'oubliez pas que les identifiants d'enregistrement doivent toujours figurer dans un tableau, même si vous souhaitez que celui-ci soit envoyé à un seul destinataire.

12
noob

Firebase a deux fonctionnalités pour envoyer des messages aux périphériques:

  • le panneau Notifications de votre console Firebase vous permet d'envoyer des notifications à des périphériques, groupes d'utilisateurs ou sujets spécifiques auxquels les utilisateurs se sont abonnés.
  • en appelant l'API Firebase Cloud Messaging, vous pouvez envoyer des messages avec la stratégie de ciblage de votre choix. L'appel de l'API FCM nécessite un accès à votre clé de serveur, que vous ne devez jamais exposer sur les périphériques clients. C'est pourquoi vous devriez toujours exécuter ce code sur un serveur d'applications.

La Documentation Firebase montre ceci visuellement:

 The two ways to send messages to device with Firebase

L'envoi de messages directement d'un périphérique à un autre n'est pas pris en charge par Firebase Cloud Messaging.

Mise à jour: j'ai écrit un article de blog détaillant comment envoyer des notifications entre appareils Android à l'aide de la base de données Firebase, de la messagerie en nuage et de Node.js .

Update 2: Vous pouvez désormais également utiliser Fonctions de cloud pour Firebase pour envoyer des messages en toute sécurité, sans faire tourner le serveur. Voir cet exemple de cas d'utilisation pour commencer.

14
Frank van Puffelen

J'ai également utilisé la messagerie d'un périphérique à l'autre directement dans mon prototype. Cela fonctionne très bien. Nous n'avons pas de serveur. Nous échangeons l’identifiant GCM avec sms/text, puis nous communiquons avec GCM par la suite. Je mets ici le code relatif à la gestion de GCM

************** Envoi du message de 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");
    }
}

************** Réception du message de 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");
    }

}
3
vsrivas

Selon la nouvelle documentation mise à jour le October 2, 2018, vous devez envoyer une demande de publication comme ci-dessous.

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

Pour obtenir le jeton d'enregistrement de périphérique, étendez FirebaseMessagingService et remplacez onNewToken(String token) Pour plus d'informations, consultez doc https://firebase.google.com/docs/cloud-messaging/Android/device-group

0
Bek