Android 上的设备组消息传递

使用设备组消息传递功能,您可将一条消息发送至属于同一组的设备上运行的多个应用实例。通常而言,“组”是指属于单个用户的一组不同设备。一个组中的所有设备共享一个公用通知键,即 FCM 用于将消息扇出到该组中所有设备的令牌。

您可将设备组消息传递与 Admin SDK 结合使用,也可通过在应用服务器上实现 XMPPHTTP 协议来使用该功能。一个通知键最多支持 20 个成员设备。

管理设备组

向设备组发送消息之前,您必须:

  1. 为想要添加到该组的每一台设备获取注册令牌。

  2. 创建 notification_key。它用于将某个组(通常对应一名用户)映射到该组的所有关联注册令牌,以此来标识设备组。您可以在应用服务器或 Android 客户端应用上创建通知键。

设备组基本管理操作(创建和移除组,以及添加或移除设备)通常通过应用服务器执行。如需所支持的键的列表,请参阅旧版 HTTP 协议参考

Android 客户端应用也可以从客户端管理设备组。

在应用服务器上管理设备组

创建设备组

要创建设备组,需要发送一个 POST 请求并在其中提供组名称及其中设备的注册令牌列表。 FCM 会返回一个代表该设备组的新 notification_key

HTTP POST 请求

发送如下请求至 https://iid.googleapis.com/notification

https://iid.googleapis.com/notification
Content-Type:application/json
Authorization:key=API_KEY
project_id:SENDER_ID

{
   "operation": "create",
   "notification_key_name": "appUser-Chris",
   "registration_ids": ["4", "8", "15", "16", "23", "42"]
}

notification_key_name 是设备组的专属名称或标识符(例如,它可以是一个用户名)。每组注册令牌的 notification_key_namenotification_key 都各不相同。如果您拥有多个具有相同发送者 ID 的客户端应用,则每个客户端应用均必须拥有唯一的 notification_key_name。这样可以确保消息只会发送到预定的目标应用。

响应格式

成功的请求将返回如下所示的 notification_key

{
   "notification_key": "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ"
}

保存此 notification_key 和对应的 notification_key_name,以便在后续操作中使用。

检索通知密钥

如果您需要检索现有的通知密钥,可在 GET 请求中使用 notification_key_name,如下所示:

https://iid.googleapis.com/notification?notification_key_name=appUser-Chris
Content-Type:application/json
Authorization:key=API_KEY
project_id:SENDER_ID
{}

对于每个包含通知密钥名的 GET 请求,服务器都会返回一个独一无二的编码字符串。尽管每个字符串可能看起来像是不同的密钥,但它实际上是一个有效的“notification_key”值。

向设备组中添加设备和从中移除设备

要向现有设备组中添加设备或从中移除设备,需要发送一个 operation 参数设为 addremove 的 POST 请求,并提供要添加或移除的设备对应的注册令牌。

HTTP POST 请求

例如,要将注册 ID 为 51 的设备添加到 appUser-Chris,您应当发送一个如下所示的请求:

{
   "operation": "add",
   "notification_key_name": "appUser-Chris",
   "notification_key": "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ",
   "registration_ids": ["51"]
}

响应格式

如果添加或移除设备的请求成功,将返回如下所示的 notification_key

{
   "notification_key": "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ"
}

在 Android 客户端应用上管理设备组

当服务器不可用时,在客户端上管理设备组会非常有帮助。要在客户端创建设备组,设备上必须至少有一个 Google 帐号。请注意,在客户端创建通知键的流程与上面介绍的在服务器端创建的流程有很大不同。

在客户端创建设备组的操作步骤:

获取客户端 ID

  1. Google Developers Console 中打开您的项目。
  2. 从左上角的 菜单中,依次选择 API 和服务以及凭据
  3. 点击新建凭据,然后选择 OAuth 客户端 ID
  4. 创建客户端 ID 对话框中,选择网页应用作为应用类型,然后点击创建
  5. 复制显示的客户端 ID 值。该客户端 ID 代表您将用其生成 idToken 的 Google 帐号的“作用范围”。

在设备上验证 Google 帐号

从 Google Developers Console 获得客户端 ID 后,立即检查设备上是否存在 Google 帐号。

// This snippet takes the simple approach of using the first returned Google account,
// but you can pick any Google account on the device.
public String getAccount() {
    Account[] accounts = AccountManager.get(getActivity()).
        getAccountsByType("com.google");
    if (accounts.length == 0) {
        return null;
    }
    return accounts[0].name;
}

获取身份验证令牌

接着,使用 GoogleAuthUtil 类获取一个身份验证令牌 (idToken)。例如:

String accountName = getAccount();

// Initialize the scope using the client ID you got from the Console.
final String scope = "audience:server:client_id:"
        + "1262xxx48712-9qs6n32447mcj9dirtnkyrejt82saa52.apps.googleusercontent.com";
String idToken = null;
try {
    idToken = GoogleAuthUtil.getToken(context, accountName, scope);
} catch (Exception e) {
    log("exception while getting idToken: " + e);
}
...

向设备组中添加设备或从中移除设备

构建一个发送至 https://android.googleapis.com/gcm/googlenotification 的 HTTP POST 请求,以便向设备组中添加注册令牌或从中移除注册令牌。请求标头需要将 project_id 设置为发送者 ID,并将 Content-Type 设置为 JSON。

添加到设备组

添加操作需要以下几个键: 设为 addoperation、设为在上述步骤中获取的 idTokenid_token,以及 notification_key_nameregistration_ids。下面显示的 userEmail 变量可以从 accounts[0].name 的值派生。客户端只有权管理映射至此电子邮件/帐号的设备组。

public String addNotificationKey(
        String senderId, String userEmail, String registrationId, String idToken)
        throws IOException, JSONException {
    URL url = new URL("https://android.googleapis.com/gcm/googlenotification");
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setDoOutput(true);

    // HTTP request header
    con.setRequestProperty("project_id", senderId);
    con.setRequestProperty("Content-Type", "application/json");
    con.setRequestProperty("Accept", "application/json");
    con.setRequestMethod("POST");
    con.connect();

    // HTTP request
    JSONObject data = new JSONObject();
    data.put("operation", "add");
    data.put("notification_key_name", userEmail);
    data.put("registration_ids", new JSONArray(Arrays.asList(registrationId)));
    data.put("id_token", idToken);

    OutputStream os = con.getOutputStream();
    os.write(data.toString().getBytes("UTF-8"));
    os.close();

    // Read the response into a string
    InputStream is = con.getInputStream();
    String responseString = new Scanner(is, "UTF-8").useDelimiter("\\A").next();
    is.close();

    // Parse the JSON string and return the notification key
    JSONObject response = new JSONObject(responseString);
    return response.getString("notification_key");

}

如果操作成功,将返回 notification_key。 保存此 notification_key 和对应的 notification_key_name,以便在后续操作中使用。

从设备组中移除

移除操作需要以下几个键: 设为 removeoperation、设为在上述步骤中获取的 idTokenid_token,以及 notification_key_nameregistration_ids

// HTTP request
JSONObject data = new JSONObject();
data.put("operation", "remove");
data.put("notification_key_name", userEmail);
data.put("registration_ids", new JSONArray(Arrays.asList(registrationId)));
data.put("id_token", idToken);

向设备组发送下行消息

向设备组发送消息与向单台设备发送消息十分类似。您需要将 to 参数设为设备组专属的通知键。需要详细了解负载支持,请参阅消息类型。本页的示例展示了如何通过 HTTP 和 XMPP 协议向设备组发送数据消息。

设备组 HTTP POST 请求

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

{
  "to": "aUniqueKey",
  "data": {
    "hello": "This is a Firebase Cloud Messaging Device Group Message!",
   }
}

设备组 HTTP 响应

以下是一个“成功”的示例:notification_key 有 2 个关联的注册令牌,且消息已成功发送至这两个令牌:

{
  "success": 2,
  "failure": 0
}

以下是一个“部分成功”的示例:notification_key 有 3 个关联的注册令牌。 消息仅成功发送至其中 1 个注册令牌。响应消息列出了未收到消息的注册令牌:

{
  "success":1,
  "failure":2,
  "failed_registration_ids":[
     "regId1",
     "regId2"
  ]
}

当消息未能发送至与某个 notification_key 关联的一个或多个注册令牌时,应用服务器会重试,并在两次重试之间退避。

如果服务器尝试向没有成员的设备组发送消息,则响应如下所示(0 个成功,0 个失败):

{
  "success": 0,
  "failure": 0
}

设备组 XMPP 消息

  <message id="">
  <gcm xmlns="google:mobile:data">
  {
      "to": "aUniqueKey",
      "message_id": "m-1366082849205" ,
      "data": {
          "hello":"This is a Firebase Cloud Messaging Device Group Message!"
      }
  }
  </gcm>
</message>

设备组 XMPP 响应

当消息成功发送至设备组中的任何一台设备时,XMPP 连接服务器都会发送 ACK 响应。当发送至组中所有设备的消息都失败时,XMPP 连接服务器会发送 NACK 响应。

以下是一个“成功”的示例:notification_key 有 3 个关联的注册令牌,且消息已成功发送至这 3 个令牌:

{
  "from": "aUniqueKey",
  "message_type": "ack",
  "success": 3,
  "failure": 0,
  "message_id": "m-1366082849205"
}

以下是一个“部分成功”的示例:notification_key 有 3 个关联的注册令牌。 消息仅成功发送至其中 1 个注册令牌。响应消息列出了未收到消息的注册令牌:

{
  "from": "aUniqueKey",
  "message_type": "ack",
  "success":1,
  "failure":2,
  "failed_registration_ids":[
     "regId1",
     "regId2"
  ]
}

当 FCM 连接服务器未能将消息发送至组中的所有设备时,应用服务器将收到一个 NACK 响应。

如需消息选项的完整列表,请参阅您所选的连接服务器协议(HTTPXMPP)的参考信息。

向设备组发送上行消息

通过将消息定向至 to 字段中相应的通知键,客户端应用可以向设备组发送上行消息。

以下对 FCM 的调用将向相应通知键发送一条上行消息。该消息对象由以下键值对组成。

FirebaseMessaging fm = FirebaseMessaging.getInstance();
String to = aUniqueKey; // the notification key
AtomicInteger msgId = new AtomicInteger();
fm.send(new RemoteMessage.Builder(to)
  .setMessageId(msgId)
  .addData("hello", "world")
  .build());

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面