如何使用 Firebase Cloud Messaging 向设备发送消息?

How to send device to device messages using Firebase Cloud Messaging?

搜索文档后,我找不到任何有关如何在不使用外部服务器的情况下使用 FCM 向设备发送消息的信息。

例如,如果我正在创建一个聊天应用程序,我需要向用户发送有关未读消息的推送通知,因为他们不会一直在线,而且我无法在后台提供持久服务将始终连接到实时数据库,因为那样会占用太多资源。

那么当某个用户 "B" 发送 him/her 聊天消息时,我如何向用户 "A" 发送推送通知?为此我需要一个外部服务器还是仅使用 Firebase 服务器就可以完成?

您可以使用 firebase 实时数据库来执行此操作。您可以创建用于存储聊天的数据结构,并为两个用户的对话线程添加观察者。它仍然采用设备 - 服务器 - 设备架构,但在这种情况下,开发人员没有额外的服务器。这使用 firebase 服务器。您可以在此处查看教程(忽略 UI 部分,不过,这也是聊天 UI 框架的良好起点)。

Firebase Realtime Chat

所以我想到了一个主意。请参阅:如果 FCM 和 GCM 都有一个 http 请求的端点,我们可以在其中发送 post json 和我们的消息数据,包括我们想要的设备的令牌要传递的消息。

那么为什么不向 Firebase 服务器发送一个 post 并将此通知发送给用户 B?你明白 ?

因此,如果用户在后台使用您的应用程序,您可以通过通话发送消息和聊天 post 以确保通知的传送。我也很快需要它,我稍后会测试。你说什么?

更新:现在可以使用 firebase 云函数作为处理推送通知的服务器。查看他们的文档 here

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

根据文档,您必须实现一个服务器来处理设备到设备通信中的推送通知。

Before you can write client apps that use Firebase Cloud Messaging, you must have an app server that meets the following criteria:

...

You'll need to decide which FCM connection server protocol(s) you want to use to enable your app server to interact with FCM connection servers. Note that if you want to use upstream messaging from your client applications, you must use XMPP. For a more detailed discussion of this, see Choosing an FCM Connection Server Protocol.

如果您只需要从服务器向您的用户发送基本通知。您可以使用他们的无服务器解决方案 Firebase Notifications.

在此处查看 FCM 和 Firebase 通知之间的比较: https://firebase.google.com/support/faq/#messaging-difference

1) 订阅相同的主题名称,例如:

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

2) 在应用程序内部发送消息

GoogleFirebase : How-to send topic messages

如果您有要向其发送通知的设备的 fcm(gcm) 令牌。这只是发送通知的 post 请求。

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

是的,可以在没有任何服务器的情况下进行。您可以创建一个设备组客户端,然后在组中交换消息。但是也有限制:

  1. 您必须在设备上使用相同的 Google 帐户
  2. 您不能发送高优先级消息

参考:Firebase doc参见"Managing device groups on Android client apps"

部分

您可以使用 Retrofit。为设备订阅主题新闻。从一台设备向另一台设备发送通知。

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

}

使用 link https://fcm.googleapis.com/fcm/send 发出 HTTP POST 请求,所需 header 和数据帮助了我。在下面的代码片段中 Constants.LEGACY_SERVER_KEY 是一个本地 class 变量,您可以在您的 Firebase 项目 Settings->Cloud Messaging->Legacy Server key 中找到它。您需要传递设备注册令牌,即下面引用 HERE.

的代码片段中的 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();

}

此外,如果您想向特定主题发送消息,请将 json 中的 regToken 替换为

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

并且不要忘记在您的 AndroidManifest.xml 中添加 INTERNET 权限。

重要:- 使用以上代码意味着您的服务器密钥驻留在客户端应用程序中。这很危险,因为有人可以深入您的应用程序并获取服务器密钥以向您的用户发送恶意通知。

您可以使用 Volly Jsonobject 请求来完成....

首先执行以下步骤:

1 复制遗留服务器密钥并将其存储为 Legacy_SERVER_KEY

Legacy Server key

你可以在图片中看到如何获得

2 你需要 Volley 依赖

compile 'com.mcxiaoke.volley:library:1.0.19'

发送推送代码:-

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

最简单的方法:

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

创建 Class 以生成对象:

public class Message {
String to;
NotifyData data;

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

创建 Class 以生成对象:

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

Google Cloud Functions 现在可以在没有应用程序服务器的情况下从设备到设备发送推送通知。

From the relevant page Google 云函数:

Developers can use Cloud Functions to keep users engaged and up to date with relevant information about an app. Consider, for example, an app that allows users to follow one another's activities in the app. In such an app, a function triggered by Realtime Database writes to store new followers could create Firebase Cloud Messaging (FCM) notifications to let the appropriate users know that they have gained new followers.

Example:

  1. The function triggers on writes to the Realtime Database path where followers are stored.

  2. The function composes a message to send via FCM.

  3. FCM sends the notification message to the user's device.

Here is a demo project 用于使用 Firebase 和 Google Cloud Functions 发送设备到设备的推送通知。

Google Cloud Functions 现在可以在没有应用程序服务器的情况下从设备到设备发送推送通知。 我制作了云功能,当新消息添加到数据库时会触发该功能

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 ;


    });

在我的例子中,我将 retrofit 与此 class 消息一起使用:

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

我们的 ServiceAPI

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

这里介绍了如何在没有除 Firebase 之外的第二台服务器的情况下获取通知。所以我们只使用 Firebase,没有额外的服务器。

  1. 在移动应用程序代码中,我们通过 Android 库(如 here, not using Firebase libraries like here)创建了自己的通知功能,而没有使用 Firebase 云消息传递。 这是 Kotlin 的示例:

    私人娱乐通知(){ createNotificationChannel()

     val intent = Intent(this, LoginActivity::class.java).apply {
         flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
     }
     val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
    
     val notificationBuilder = NotificationCompat.Builder(this, "yuh_channel_id")
         .setSmallIcon(R.drawable.ic_send)
         .setContentText("yuh")
         .setContentText("yuh")
         .setAutoCancel(true)
         .setPriority(NotificationCompat.PRIORITY_DEFAULT)
         .setContentIntent(pendingIntent)
     val notificationManager =
         getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
     notificationManager.notify(0, notificationBuilder.build())
    
     with(NotificationManagerCompat.from(this)) {
         // notificationId is a unique int for each notification that you must define
         notify(0, notificationBuilder.build())
     }
    

    }

     private fun createNotificationChannel() {
     // Create the NotificationChannel, but only on API 26+ because
     // the NotificationChannel class is new and not in the support library
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
         val name = "yuh_channel"
         val descriptionText = "yuh_description"
         val importance = NotificationManager.IMPORTANCE_DEFAULT
         val CHANNEL_ID = "yuh_channel_id"
         val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
             description = descriptionText
         }
         // Register the channel with the system
         val notificationManager: NotificationManager =
             getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
         notificationManager.createNotificationChannel(channel)
     }
    
  1. 在 Firebase 数据库中,创建“待处理通知”集合。文档应包含用户名(用于向其发送通知)和来源名称(用户点击通知后应该去哪里)。

  2. 在应用程序代码中,实现用于将新记录添加到待处理通知集合的选项。例如如果用户 A 向用户 B 发送消息,则在集合中创建 ID 为用户 B(将收到通知)的文档。

  3. 在应用程序代码中,设置后台(当应用程序对用户不可见时)服务。喜欢here。在后台服务中,为“Notifications Pending”集合中的更改设置监听器。当带有用户 id 的新记录进入集合时,调用在段落 1 supra 中创建的通知函数,并从集合中删除后续记录。