Як надсилати повідомлення пристрою на пристрої за допомогою Firebase Cloud Messaging?


74

Після пошуку в документах я не зміг знайти інформацію про те, як надсилати повідомлення пристрою на пристрої за допомогою FCM без використання зовнішнього сервера.

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

Отже, як я можу надіслати push-повідомлення користувачеві "А", коли певний користувач "В" надсилає йому / їй повідомлення чату? Чи потрібен мені для цього зовнішній сервер, чи це можна зробити лише із серверами Firebase?


3
Я ще не використовував FCM, .... але я використовував GCM .... припускаючи, що FCM майже схожий на GCM ..... пристрій Надішліть повідомлення на сервер, який буде надсилати повідомлення на пристрій B. Перевірте firebase.google.com/support/faq/#messaging-difference
j4rey

1
@ j4rey89 Так, я знаю, що це можна зробити за допомогою зовнішнього сервера. Я запитую, чи можна обійтися без цього, оскільки це зажадає від мене обслуговування та оплати двох серверів замість одного.
Суяш

7
@Suyash обов’язковим є запуск власного сервера для надсилання повідомлень FCM між вашими пристроями. Якщо вас турбують витрати на роботу сервера, ви можете розпочати розгортання до Openshift Online (PaaS) або Google AppEngine (PaaS теж), які мають безкоштовну квоту.
MrBrightside

2
@ j4rey89 MrBrightside: це звучить як відповідь. :-)
Frank van Puffelen

Відповіді:


42

ОНОВЛЕННЯ: Тепер можна використовувати хмарні функції firebase як сервер для обробки push-сповіщень. Ознайомтеся з їх документацією тут

============

Згідно з документами, ви повинні реалізувати сервер для обробки push-повідомлень у зв'язку з пристроєм на пристрій.

Перш ніж ви зможете писати клієнтські програми, які використовують Firebase Cloud Messaging, у вас повинен бути сервер додатків, який відповідає таким критеріям:

...

Вам потрібно буде вирішити, який протокол (-и) сервера з’єднання FCM ви хочете використовувати, щоб дозволити серверу додатків взаємодіяти із серверами з’єднання FCM. Зверніть увагу, що якщо ви хочете використовувати обмін повідомленнями з клієнтських програм, ви повинні використовувати XMPP. Для більш детального обговорення цього питання див. Вибір протоколу сервера підключення FCM .

Якщо вам потрібно лише надіслати базові сповіщення своїм користувачам із сервера. Ви можете використовувати їх безсерверне рішення Firebase Notifications .

Подивіться тут порівняння між FCM та Firebase Notifications: https://firebase.google.com/support/faq/#messaging-difference


1
хороша відповідь. Чи знаєте ви якісь підручники чи відео, які можуть пояснити, як це зробити? дякую
Бен Акін


Не могли б ви допомогти мені зрозуміти, будь ласка. Наскільки я розумію, якщо мені потрібно надіслати пряме повідомлення від одного користувача до іншого, я повинен використовувати HTTP і надіслати це повідомлення на мій сервер, а наступний сервер буде використовувати FCM для надсилання сповіщення на рецепт і, таким чином, рецепт отримуватиме дані з ідентифікатором відправника. Наступним кроком рецепт не підключається до FCM і за допомогою ідентифікатора отримує всі дані з БД FCM? Такий спосіб?
Олексій Тимощенко

Прекрасна відповідь, я досліджував приблизно ці 2 дні. Дуже повна інформація про FCM і важко чи ні. Дякую !.
Кханг Тран

27

Надання запиту HTTP POST із посиланням https://fcm.googleapis.com/fcm/send із необхідним заголовком та даними мені допомогло. У наведеному нижче фрагменті коду Constants.LEGACY_SERVER_KEYє локальна змінна класу, ви можете знайти це у своєму проекті Firebase Settings->Cloud Messaging->Legacy Server key. Вам потрібно передати маркер реєстрації пристрою, тобто regTokenу фрагменті коду, на який посилається ТУТ.

Нарешті вам потрібна залежність бібліотеки okhttp , щоб отримати цей фрагмент.

public static final MediaType JSON
        = MediaType.parse("application/json; charset=utf-8");
private void sendNotification(final String regToken) {
    new AsyncTask<Void,Void,Void>(){
        @Override
        protected Void doInBackground(Void... params) {
            try {
                OkHttpClient client = new OkHttpClient();
                JSONObject json=new JSONObject();
                JSONObject dataJson=new JSONObject();
                dataJson.put("body","Hi this is sent from device to device");
                dataJson.put("title","dummy title");
                json.put("notification",dataJson);
                json.put("to",regToken);
                RequestBody body = RequestBody.create(JSON, json.toString());
                Request request = new Request.Builder()
                        .header("Authorization","key="+Constants.LEGACY_SERVER_KEY)
                        .url("https://fcm.googleapis.com/fcm/send")
                        .post(body)
                        .build();
                Response response = client.newCall(request).execute();
                String finalResponse = response.body().string();
            }catch (Exception e){
                //Log.d(TAG,e+"");
            }
            return null;
        }
    }.execute();

}

далі, якщо ви хочете надіслати повідомлення до певної теми, замініть regTokenу json таким чином

json.put("to","/topics/foo-bar")

і не забудьте додати дозвіл на ІНТЕРНЕТ у своєму AndroidManifest.xml.

ВАЖЛИВО : - Використання вищевказаного коду означає, що ваш серверний ключ знаходиться у клієнтській програмі. Це небезпечно, оскільки хтось може копатись у вашій програмі та отримати ключ сервера для надсилання шкідливих сповіщень вашим користувачам.


Привіт, чи є можливість надсилати повідомлення на підписаний канал?
Сучіт

10
Недоліком є ​​те, що ваш серверний ключ знаходиться у клієнтській програмі. Це небезпечно, оскільки хтось може копатись у вашій програмі та отримати ключ сервера для надсилання шкідливих сповіщень вашим користувачам. Тому ніколи не слід цього робити.
kirtan403

@ kirtan403 Сильне шифрування ключа сервера на стороні клієнта може зупинити це ???
Містер Популярний

@ Mr.Popular Можливо, але якщо хтось зможе декомпілювати ваш код (звичайно, може), тоді він зможе захопити те, що ви використовуєте для шифрування ключа сервера, і отримати ключ сервера. І тоді вони можуть надсилати сповіщення будь-кому без будь-яких обмежень .. Тому дуже погана ідея розміщувати ключ сервера на стороні клієнта. Дуже погана ідея ...
kirtan403

1
@Tabish, будь ласка, використовуйте remoteMessage.getNotification (). Ми не надсилаємо дані сюди.
brijesh kumar

4

Ви можете зробити це за допомогою запиту Volly Jsonobject ....

дотримуйтесь цих кроків спочатку:

1 скопіюйте застарілий серверний ключ і збережіть його як Legacy_SERVER_KEY

Ключ застарілого сервера

ви можете побачити на малюнку, як отримати

2 Вам потрібна залежність від волейболу

компілювати 'com.mcxiaoke.volley: бібліотека: 1.0.19'

введіть тут опис зображення

Код для відправки Push: -

private void sendFCMPush() {

    String Legacy_SERVER_KEY = YOUR_Legacy_SERVER_KEY;
    String msg = "this is test message,.,,.,.";
    String title = "my title";
    String token = FCM_RECEIVER_TOKEN;

    JSONObject obj = null;
    JSONObject objData = null;
    JSONObject dataobjData = null;

    try {
        obj = new JSONObject();
        objData = new JSONObject();

        objData.put("body", msg);
        objData.put("title", title);
        objData.put("sound", "default");
        objData.put("icon", "icon_name"); //   icon_name image must be there in drawable
        objData.put("tag", token);
        objData.put("priority", "high");

        dataobjData = new JSONObject();
        dataobjData.put("text", msg);
        dataobjData.put("title", title);

        obj.put("to", token);
        //obj.put("priority", "high");

        obj.put("notification", objData);
        obj.put("data", dataobjData);
        Log.e("!_@rj@_@@_PASS:>", obj.toString());
    } catch (JSONException e) {
        e.printStackTrace();
    }

    JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, Constants.FCM_PUSH_URL, obj,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    Log.e("!_@@_SUCESS", response + "");
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("!_@@_Errors--", error + "");
                }
            }) {
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String, String> params = new HashMap<String, String>();
            params.put("Authorization", "key=" + Legacy_SERVER_KEY);
            params.put("Content-Type", "application/json");
            return params;
        }
    };
    RequestQueue requestQueue = Volley.newRequestQueue(this);
    int socketTimeout = 1000 * 60;// 60 seconds
    RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
    jsObjRequest.setRetryPolicy(policy);
    requestQueue.add(jsObjRequest);
}

Просто зателефонуйте sendFCMPush () ;


Привіт, чи є можливість надсилати повідомлення на підписаний канал?
Сучіт

так можливо, ви повинні додати прапор для цього, і це залежить від того, що ви можете надіслати push для
підписаних

@RjzSatvara що, якщо програма не працює на телефоні-приймачі? він отримає повідомлення? Заздалегідь дякую
Джако

1
@Jaco, немає можливості для цього. ви повинні керувати цим іншим способом.
Rjz Satvara

3

1) передплатити ідентичну назву теми, наприклад:

  • ClientA.subcribe ("to / to_users_channel")
  • ClientB.subcribe ("to / to_users_channel")

2) надсилати повідомлення всередині програми

GoogleFirebase: інструкції щодо надсилання тематичних повідомлень


2
Чи все одно для цього не потрібно буде використовувати ключ авторизації на стороні клієнта? Що робить його небезпечним. Також я навіть не знаю, чи створення окремої теми для кожного користувача є хорошою ідеєю.
Суяш

Хороша ідея, але: Тематичні повідомлення оптимізовані для пропускної здатності, а не для затримки. Для швидкої, безпечної доставки на окремі пристрої або невеликі групи пристроїв націлюйте повідомлення на маркери реєстрації, а не на теми.
Michalsx

@ Maxim Firsoff - Як створити тему з консолі FCM чи будь-яким іншим способом?
Аджай Шарма

@AjaySharma, як я пам'ятаю, консоль FMC не має для цього інструментів, ви можете зробити тему програмно (див. Вище мій псевдокод).
Максим Фірсофф

3

Так, це можна зробити без будь-якого сервера. Ви можете створити сторону клієнта групи пристроїв, а потім обмінюватися повідомленнями в групі. Однак є обмеження:

  1. Ви повинні використовувати той самий обліковий запис Google на пристроях
  2. Ви не можете надсилати повідомлення з високим пріоритетом

Довідково: Firebase doc Див. Розділ "Керування групами пристроїв у клієнтських програмах Android"


Вам все ще потрібен сервер для відправки групового повідомлення
відхиляється

У жодному разі. Будь-який пристрій у групі може надіслати повідомлення
greywolf82

from docs: Авторизація: key = API_KEY Вам все ще потрібен ключ сервера. тому це рішення не підходить для виробництва
девіант

Ключ API - це обліковий запис Google, і спілкування обмежується одним обліковим записом користувача. Спробуйте, перш ніж коментувати.
greywolf82

2

Google Cloud Functions тепер дозволяє надсилати push-сповіщення з пристрою на пристрій без сервера додатків. Я створив хмарну функцію, яка запускається, коли в базу даних додається нове повідомлення

Це node.jsкод

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin'); admin.initializeApp();

exports.sendNotification = functions.database.ref('/conversations/{chatLocation}/{messageLocation}')
  .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();

       const toIDUser = original.toID;
       const isGroupChat = original.isGroupChat;

       if (isGroupChat) {
       const tokenss =  admin.database().ref(`/users/${toIDUser}/tokens`).once('value').then(function(snapshot) {

// Handle Promise
       const tokenOfGroup = snapshot.val()

      // get tokens from the database  at particular location get values 
       const valuess = Object.keys(tokenOfGroup).map(k => tokenOfGroup[k]);

     //console.log(' ____________ddd((999999ddd_________________ ' +  valuess );
    const payload = {
       notification: {
                 title:   original.senderName + " :- ",
                 body:    original.content
    }
  };

  return admin.messaging().sendToDevice(valuess, payload);



}, function(error) {

  console.error(error);
});

       return ;
          } else {
          // get token from the database  at particular location
                const tokenss =  admin.database().ref(`/users/${toIDUser}/credentials`).once('value').then(function(snapshot) {
                // Handle Promise
  // The Promise was "fulfilled" (it succeeded).

     const credentials = snapshot.val()



    // console.log('snapshot ......snapshot.val().name****^^^^^^^^^^^^kensPromise****** :- ', credentials.name);
     //console.log('snapshot.....****snapshot.val().token****^^^^^^^^^^^^kensPromise****** :- ', credentials.token);


     const deviceToken = credentials.token;

    const payload = {
       notification: {
                 title:   original.senderName + " :- ",
                 body:    original.content
    }
  };

  return admin.messaging().sendToDevice(deviceToken, payload);


}, function(error) {

  console.error(error);
});


          }





  return ;


    });

1

Якщо у вас є маркер fcm (gcm) пристрою, якому потрібно надіслати сповіщення. Це просто запит на відправлення повідомлення, щоб надіслати повідомлення.

https://github.com/prashanthd/google-services/blob/master/android/gcm/gcmsender/src/main/java/gcm/play/android/samples/com/gcmsender/GcmSender.java


1
Так, але для цього все-таки потрібен наш власний зовнішній сервер, чи не так? Тому що ми не повинні вбудовувати API_KEY безпосередньо в наших клієнтів. Моє запитання полягало в тому, чи можливо це без зовнішнього сервера, що в даний час не є таким, як пропонують інші відповіді.
Суяш,

1

Google Cloud Functions тепер дозволяє надсилати push-сповіщення з пристрою на пристрій без сервера додатків.

На відповідній сторінці в Google Cloud Functions:

Розробники можуть використовувати хмарні функції, щоб тримати користувачів залученими та отримувати актуальну інформацію про програму. Розглянемо, наприклад, програму, яка дозволяє користувачам стежити за діяльністю один одного в додатку. У такому додатку функція, яка запускається записом бази даних у реальному часі для зберігання нових послідовників, може створювати сповіщення Firebase Cloud Messaging (FCM), щоб відповідні користувачі знали, що вони отримали нових послідовників.

Приклад:

  1. Функція запускається при записі в шлях до бази даних реального часу, де зберігаються послідовники.

  2. Функція складає повідомлення для надсилання через FCM.

  3. FCM надсилає повідомлення-повідомлення на пристрій користувача.

Ось демонстраційний проект надсилання push-сповіщень із пристрою на пристрій за допомогою Firebase та Google Cloud Functions.


1

У моєму випадку я використовую модернізацію цього класу Повідомлення:

public class Message {

    private String to;
    private String collapseKey;
    private Notification notification;
    private Data data;

    public Message(String to, String collapseKey, Notification notification, Data data) {
        this.to = to;
        this.collapseKey = collapseKey;
        this.notification = notification;
        this.data = data;
    }
}

Дані

public class Data {

    private String body;
    private String title;
    private String key1;
    private String key2;

    public Data(String body, String title, String key1, String key2) {
        this.body = body;
        this.title = title;
        this.key1 = key1;
        this.key2 = key2;
    }
}

Повідомлення

public class Notification {

    private String body;
    private String title;

    public Notification(String body, String title) {
        this.body = body;
        this.title = title;
    }
}

це дзвінок

private void sentToNotification() {
    String to = "YOUR_TOKEN";
    String collapseKey = "";
    Notification notification = new Notification("Hello bro", "title23");
    Data data = new Data("Hello2", "title2", "key1", "key2");
    Message notificationTask = new Message(to, collapseKey, notification, data);

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://fcm.googleapis.com/")//url of FCM message server
            .addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
            .build();

    ServiceAPI api = new retrofit.create(ServiceAPI.class);

    Call<Message> call = api .sendMessage("key=YOUR_KEY", notificationTask);

    call.enqueue(new Callback<Message>() {
        @Override
        public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
            Log.d("TAG", response.body().toString());
        }

        @Override
        public void onFailure(Call<Message> call, Throwable t) {

            Log.e("TAG", t.getMessage());
        }
    });
}

наш сервісAPi

public interface ServiceAPI {
    @POST("/fcm/send")
    Call<Message> sendMessage(@Header("Authorization") String token, @Body Message message);
}

1

Ви можете використовувати модернізацію. Підпишіться на пристрої на тематичні новини. Надсилати повідомлення з одного пристрою на інший.

public void onClick(View view) {

    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    httpClient.addInterceptor(new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request original = chain.request();

            // Request customization: add request headers
            Request.Builder requestBuilder = original.newBuilder()
                    .header("Authorization", "key=legacy server key from FB console"); // <-- this is the important line
            Request request = requestBuilder.build();
            return chain.proceed(request);
        }
    });

    httpClient.addInterceptor(logging);
    OkHttpClient client = httpClient.build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://fcm.googleapis.com")//url of FCM message server
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
            .build();

    // prepare call in Retrofit 2.0
    FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);

    //for messaging server
    NotifyData notifydata = new NotifyData("Notification title","Notification body");

    Call<Message> call2 = firebaseAPI.sendMessage(new Message("topic or deviceID", notifydata));

    call2.enqueue(new Callback<Message>() {
        @Override
        public void onResponse(Call<Message> call, Response<Message> response) {

            Log.d("Response ", "onResponse");
            t1.setText("Notification sent");

        }

        @Override
        public void onFailure(Call<Message> call, Throwable t) {
            Log.d("Response ", "onFailure");
            t1.setText("Notification failure");
        }
    });
}

POJO

public class Message {
    String to;
    NotifyData notification;

    public Message(String to, NotifyData notification) {
        this.to = to;
        this.notification = notification;
    }

}

і

public class NotifyData {
    String title;
    String body;

    public NotifyData(String title, String body ) {

        this.title = title;
        this.body = body;
    }

}

та FirebaseAPI

public interface FirebaseAPI {

    @POST("/fcm/send")
    Call<Message> sendMessage(@Body Message message);

}

0

Для цього ви можете використовувати базу даних Firebase у режимі реального часу. Ви можете створити структуру даних для зберігання чатів і додати спостерігачів для ланцюжків розмов для обох користувачів. Це все ще робить архітектура пристрій - сервер - пристрій, але в цьому випадку додатковий сервер з боку розробників відсутній. Тут використовуються сервери Firebase. Ви можете переглянути тут підручник (ігноруйте частину інтерфейсу, хоча це також є хорошою відправною точкою для фреймворків інтерфейсу чату).

Чат Firebase у режимі реального часу


3
Користувач не використовуватиме програму постійно, і ми не можемо використовувати базу даних Firebase у режимі реального часу у фоновому режимі, оскільки вона підтримує постійне з’єднання сокета з сервером, яке занадто важке для акумулятора пристрою.
Суяш

Я можу надсилати повідомлення Firebase між пристроями та сповіщення за допомогою бібліотеки Smack. Я не реалізую жодного зовнішнього сервера в своєму коді Android. Smack управляє з'єднанням та вхідними / вихідними строфами повідомлень за допомогою протоколу XMPP.
i_o

0

Тож у мене тут була ідея. Див .: Якщо FCM, як і GCM, має запит кінцевої точки до http, де ми можемо надіслати post json із нашими даними повідомлень, включаючи маркер (и) пристроїв, на які ми хочемо доставити це повідомлення.

То чому б не надіслати повідомлення на сервер Firebase із цим сповіщенням, яке буде доставлено користувачеві B? ти розумієш ?

Отже, ви надсилаєте повідомлення та спілкуєтеся в чаті із повідомленням про дзвінок, щоб забезпечити доставку сповіщення, якщо користувач перебуває у вашому додатку у фоновому режимі. Мені це також незабаром потрібно, я перевірю пізніше. Що ти скажеш?


2
FCM вже має кінцеву точку, дивіться тут . Але використовувати його безпосередньо у наших клієнтах не можна, оскільки для цього потрібен серверний ключ API. І навіть якщо він буде загальнодоступним, це спричинить проблеми безпеки, оскільки будь-який користувач зможе надіслати будь-яке повідомлення FCM будь-кому.
Суяш

-3

Найпростіший спосіб:

void sendFCMPush(String msg,String token) {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    httpClient.addInterceptor(new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request original = chain.request();

            // Request customization: add request headers
            Request.Builder requestBuilder = original.newBuilder()
                    .header("Authorization", "key="+Const.FIREBASE_LEGACY_SERVER_KEY); // <-- this is the important line
            Request request = requestBuilder.build();
            return chain.proceed(request);
        }
    });

    httpClient.addInterceptor(logging);
    OkHttpClient client = httpClient.build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://fcm.googleapis.com/")//url of FCM message server
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
            .build();

    // prepare call in Retrofit 2.0
    FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);

    //for messaging server
    NotifyData notifydata = new NotifyData("Chatting", msg);

    Call<Message> call2 = firebaseAPI.sendMessage(new Message(token, notifydata));

    call2.enqueue(new Callback<Message>() {
        @Override
        public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
            Log.e("#@ SUCCES #E$#", response.body().toString());
        }

        @Override
        public void onFailure(Call<Message> call, Throwable t) {

            Log.e("E$ FAILURE E$#", t.getMessage());
        }
    });
}

Створити клас для створення об’єкта:

public class Message {
String to;
NotifyData data;

public Message(String to, NotifyData data) {
    this.to = to;
    this.data = data;
}
}

Створити клас для створення об’єкта:

public class Notification {
String title;
String message;
enter code here`enter code here`
public Notification(String title, String message) {
    this.title = title;
    this.message = message;
}
}

Const.FIREBASE_LEGACY_SERVER_KEY не безпечно використовувати в коді на стороні клієнта. Будь ласка, принаймні прочитайте інші відповіді та коментарі перед публікацією.
Суяш,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.