Saltar al contenido

Cómo enviar un mensaje uno a uno usando Firebase Messaging

Solución:

Firebase tiene dos funciones para enviar mensajes a dispositivos:

  • el panel de Notificaciones en tu Firebase Console te permite enviar notificaciones a dispositivos específicos, grupos de usuarios o temas a los que los usuarios se suscribieron.
  • al llamar a la API de Firebase Cloud Messaging, puede enviar mensajes con la estrategia de orientación que prefiera. Llamar a la API de FCM requiere acceso a su servidor key, que nunca debe exponer en dispositivos cliente. Es por eso que siempre debe ejecutar dicho código en un servidor de aplicaciones.

La documentación de Firebase muestra esto visualmente:

Las dos formas de enviar mensajes al dispositivo con Firebase

El envío de mensajes desde un dispositivo directamente a otro dispositivo no es compatible con Firebase Cloud Messaging.

Actualizar: Escribí una publicación de blog que detalla cómo enviar notificaciones entre dispositivos Android usando Firebase Database, Cloud Messaging y Node.js.

Actualización 2: Ahora también puede usar Cloud Functions para Firebase para enviar mensajes de forma segura, sin activar un servidor. Vea este caso de uso de muestra para comenzar.

Advertencia Hay una razón muy importante por la que no mencionamos este enfoque en ninguna parte. Esto expone su servidor key en el APK que colocas en cada dispositivo cliente. Puede (y, por lo tanto, se tomará) de allí y puede dar lugar a un abuso de su proyecto. Recomiendo encarecidamente no adoptar este enfoque, a excepción de las aplicaciones que solo coloca en sus propios dispositivos. – Frank van Puffelen

Ok, entonces la respuesta de Frank fue correcta que Firebase no es compatible de forma nativa con la mensajería de dispositivo a dispositivo. Sin embargo, hay una laguna en eso. El servidor de Firebase no identifica si ha enviado la solicitud desde un servidor real o lo está haciendo desde su dispositivo.

Así que todo lo que tienes que hacer es enviar un Post Request para Firebaseservidor de mensajería junto con la clave del servidor. Solo tenga esto en cuenta que el servidor key no se supone que esté en el dispositivo, pero no hay otra opción si desea la mensajería de dispositivo a dispositivo mediante Firebase Messaging.

Estoy usando OkHTTP en lugar de la forma predeterminada de llamar a la API Rest. El código es algo como esto:

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

Espero que Firebase venga con una mejor solución en el futuro. Pero hasta entonces, creo que esta es la única forma. La otra forma sería enviar mensajes temáticos o mensajes grupales. Pero eso no estaba en el ámbito de la pregunta.

Actualizar:

El JSONArray se define así:

JSONArray regArray = new JSONArray(regIds);

regIds es una cadena array de identificadores de registro, al que desea enviar este mensaje. Tenga en cuenta que los ID de registro siempre deben estar en un array, incluso si desea enviarlo a un solo destinatario.

También he estado usando mensajes directos de dispositivo a dispositivo gcm en mi prototipo. Ha funcionado muy bien. No tenemos ningún servidor. Intercambiamos la identificación del registro de GCM usando sms / text y luego nos comunicamos usando GCM después de eso. Estoy poniendo aquí código relacionado con el manejo de GCM

************** Envío de mensaje 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");
    

************** Recibiendo mensaje 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)  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");
    


Comentarios y valoraciones

Eres capaz de añadir valor a nuestra información contribuyendo tu experiencia en las referencias.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *