77

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

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

那么当某个用户“B”向他/她发送聊天消息时,我将如何向用户“A”发送推送通知?我需要一个外部服务器吗?还是可以只使用 Firebase 服务器来完成?

4

14 回答 14

41

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

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

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

在您可以编写使用 Firebase 云消息传递的客户端应用之前,您必须拥有一个满足以下条件的应用服务器:

...

您需要决定要使用哪个 FCM 连接服务器协议来使您的应用服务器能够与 FCM 连接服务器进行交互。请注意,如果要使用来自客户端应用程序的上游消息传递,则必须使用 XMPP。有关这方面的更详细讨论,请参阅选择 FCM 连接服务器协议

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

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

于 2016-06-25T12:04:45.663 回答
29

使用带有所需标头和数据的链接https://fcm.googleapis.com/fcm/send发出 HTTP POST 请求对我有帮助。在下面的代码片段 Constants.LEGACY_SERVER_KEY中是一个本地类变量,您可以在 Firebase Project 中找到它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 中添加 INTERNET 权限。

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

于 2017-02-01T14:39:04.923 回答
5

您可以使用 Volly Jsonobject 请求来做到这一点......

首先按照以下步骤操作:

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

旧版服务器密钥

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

2你需要 Volley 依赖

编译'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()

于 2017-02-25T04:46:03.273 回答
2

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

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

参考:Firebase doc请参阅“在 Android 客户端应用程序上管理设备组”部分

于 2016-12-10T09:43:06.647 回答
2

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 ;


    });
于 2018-09-26T11:55:32.373 回答
2

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

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

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

GoogleFirebase:如何发送主题消息

于 2016-09-05T10:26:30.647 回答
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);

}
于 2017-01-28T18:53:29.630 回答
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

于 2016-10-14T06:49:44.983 回答
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());
        }
    });
}

我们的 ServiceAPI

public interface ServiceAPI {
    @POST("/fcm/send")
    Call<Message> sendMessage(@Header("Authorization") String token, @Body Message message);
}
于 2018-10-18T09:18:48.530 回答
1

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

从Google Cloud Functions的相关页面:

开发人员可以使用 Cloud Functions 来保持用户的参与度并及时了解有关应用程序的相关信息。例如,考虑一个允许用户在应用程序中关注彼此活动的应用程序。在这样的应用程序中,由实时数据库写入触发以存储新关注者的功能可以创建 Firebase 云消息传递 (FCM) 通知,让适当的用户知道他们获得了新关注者。

例子:

  1. 该函数在写入存储关注者的实时数据库路径时触发。

  2. 该函数编写要通过 FCM 发送的消息。

  3. FCM 将通知消息发送到用户的设备。

这是一个使用 Firebase 和 Google Cloud Functions 发送设备到设备推送通知的演示项目

于 2017-11-04T01:12:13.820 回答
0

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

Firebase 实时聊天

于 2016-06-05T20:41:14.983 回答
0

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

  1. 在移动应用程序代码中,我们通过像这里这样的 Android 库创建自己的通知功能,而不使用像这里这样的 Firebase 库,没有 Firebase Cloud 消息传递。以下是 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. 在应用程序代码中,实现将新记录添加到 Pending Notifications 集合的选项。例如。如果用户 A 向用户 B 发送消息,则在集合中创建 ID 为用户 B(将被通知)的文档。

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

于 2021-06-14T17:14:05.810 回答
-1

所以我在这里有了一个想法。请参阅:如果 FCM 和 GCM 都有一个指向 http 请求的端点,我们可以在其中发送带有我们的消息数据的 post json,包括我们希望传递此消息的设备的令牌。

那么为什么不向 Firebase 服务器发送一条带有此通知的帖子以发送给用户 B 呢?你明白 ?

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

于 2016-06-10T13:44:12.500 回答
-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;
}
}
于 2017-06-21T12:03:19.907 回答