Как отправить одно сообщение с помощью Firebase Messaging
Я пытаюсь прочитать официальные документы и руководства о том, как отправлять сообщения с одного устройства на другое. Я сохранил регистрационный токен обоих устройств в базе данных реального времени, таким образом, у меня есть регистрационный токен другого устройства.
Я пробовал следующий способ отправить сообщение
RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken())
.setMessageId(incrementIdAndGet())
.addData("message", "Hello")
.build();
FirebaseMessaging.getInstance().send(message);
Однако это не работает. Другое устройство не получает никакого сообщения. Я даже не уверен, если я могу использовать восходящую передачу сообщений для передачи устройства на коммуникацию устройства.
PS: Я просто хочу знать, возможна ли обмен сообщениями между устройствами через FCM? Если да, то есть ли у меня код, который я использовал? Если да, то каков правильный путь.
Update:
Мой вопрос состоял в том, чтобы спросить, возможно ли обмен сообщениями устройства с устройством без использования какого-либо отдельного сервера, отличного от firebase, обмен сообщениями или нет, если да, то как, поскольку нет документации об этом. Я не понимаю, что здесь объяснить? В любом случае я получил ответ и обновляю его как ответ, как только вопрос будет вновь открыт.
Ответы
Ответ 1
Предупреждение Существует очень важная причина, почему мы нигде не упоминаем этот подход. Это предоставляет ваш серверный ключ в APK, который вы накладываете на каждое клиентское устройство. Она может (и, следовательно, будет) взята из там и может привести к злоупотреблению вашим проектом. я очень рекомендую против такого подхода, за исключением приложений, которые вы используете только ваши собственные устройства. - Франк ван Пуффелен
Хорошо, поэтому ответ Фрэнка был правильным, что Firebase
не поддерживает устройство для обмена сообщениями с устройством. Однако в этом есть одна лазейка. Сервер Firebase не определяет, отправляете ли вы запрос с реального сервера или делаете ли вы его с вашего устройства.
Итак, все, что вам нужно сделать, это отправить сервер обмена сообщениями Post Request
в 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 придет с лучшим решением в будущем. Но до тех пор я думаю, что это единственный способ. Другой способ - отправить текстовое сообщение или групповое сообщение. Но это не было предметом вопроса.
Update:
JSONArray определяется следующим образом:
JSONArray regArray = new JSONArray(regIds);
regIds - массив String идентификаторов регистрации, на который вы хотите отправить это сообщение. Имейте в виду, что идентификаторы регистрации всегда должны быть в массиве, даже если вы хотите, чтобы он отправил одному получателю.
Ответ 2
Firebase имеет две функции для отправки сообщений на устройства:
- панель "Уведомления" в консоли Firebase позволяет отправлять уведомления определенным устройствам, группам пользователей или темам, на которые подписаны пользователи.
- позвонив в Firebase Cloud Messaging API, вы можете отправлять сообщения с любой стратегией таргетинга, которую вы предпочитаете. Для вызова API FCM требуется доступ к ключу вашего сервера, который вы никогда не должны открывать на клиентских устройствах. Вот почему вы всегда должны запускать такой код на сервере приложений.
Документация Firebase показывает это визуально:
![The two ways to send messages to device with Firebase]()
Отправка сообщений с одного устройства на другое не поддерживается с помощью Firebase Cloud Messaging.
Обновление: я написал сообщение в блоге, подробно описывающее как отправлять уведомления между устройствами Android с помощью базы данных Firebase, облачных сообщений и Node.js.
Обновление 2: теперь вы также можете использовать Облачные функции для Firebase для безопасной отправки сообщений, без раскрутки сервера. См. этот пример использования, чтобы начать.
Ответ 3
Я также использовал прямое устройство для обмена сообщениями gcm в моем прототипе. Он работает очень хорошо. У нас нет сервера. Мы обмениваем идентификатор GCM reg с помощью 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");
}
}
Ответ 4
Я опоздал, но приведенные выше решения помогли мне записать этот простой ответ, вы можете отправить свое сообщение напрямую на устройства Android из приложения Android, вот простая реализация, которую я сделал, и она прекрасно работает для меня.
скомпилировать библиотеку Android Volley
compile 'com.android.volley:volley:1.0.0'
Просто скопируйте и вставьте эту простую функцию;) и ваша жизнь станет гладкой, как нож в масле.: 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);
headers.put("Content-Type","application/json");
return headers;
}
};
Volley.newRequestQueue(activity).add(myReq);
}
Примечание
Если вы хотите отправить сообщение темам, вы можете изменить параметр instanceIdToken на что-то вроде /themes/topicName.
Для групп реализация такая же, но вам просто нужно позаботиться о параметрах. ознакомьтесь с документацией Firebase, и вы можете передать эти параметры.
дайте мне знать, если у вас возникнут какие-либо проблемы.
Ответ 5
В соответствии с новой документацией, которая была обновлена на October 2, 2018
, вы должны отправить запрос, как показано ниже
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA //Server key
{
"to": "sent device registration token",
"data": {
"hello": "message from someone",
}
}
Чтобы получить маркер регистрации устройства, расширьте FirebaseMessagingService
и переопределите onNewToken(String token)
Для получения дополнительной информации обратитесь к документу https://firebase.google.com/docs/cloud-messaging/android/device-group