コンソールへ移動

Android でデバイス グループにメッセージを送信する

デバイス グループ メッセージングを使用すると、1 つのグループに複数のデバイスを追加できます。 これはトピック メッセージングに似ていますが、グループ メンバーシップがサーバーによってのみ管理されるようにする認証が含まれています。たとえば、さまざまなスマートフォン モデルに応じて異なるメッセージを送信する場合、サーバーは適切なグループに対して登録を追加または削除し、適切なメッセージを各グループに送信できます。デバイス グループ メッセージングはトピック メッセージングとは異なり、サーバーからデバイス グループを管理します(アプリケーション内で直接管理するのではありません)。

アプリサーバーでは、以前の XMPP プロトコルや HTTP プロトコルを介してデバイス グループ メッセージングを使用できます。以前のプロトコルに基づく Node.js 用の Firebase Admin SDK でも、デバイス グループ メッセージング機能を利用できます。1 つの通知キーで送信できるメンバーの最大数は 20 です。

デバイス グループの管理

デバイス グループにメッセージを送信する前に、次の手順を行う必要があります。

  1. グループに追加するデバイスそれぞれについて、登録トークンを取得します。

  2. notification_key を作成します。これは、特定のグループ(通常はユーザー)とそのグループに関連付けられたすべての登録トークンをマッピングさせることで、デバイス グループを識別します。通知キーは、アプリサーバー上でも Android クライアント アプリ上でも作成できます。

デバイス グループの基本管理(グループの作成と削除、デバイスの追加や削除)は通常、アプリサーバーを介して行われます。サポートされているキーの一覧については、以前の HTTP プロトコルのリファレンスをご覧ください。

あるいは、Android クライアント アプリでクライアント側からデバイス グループを管理することもできます。

アプリサーバーでのデバイス グループの管理

デバイス グループの作成

デバイス グループを作成するには、グループの名前を指定する POST リクエストと、デバイスの登録トークンのリストを送信します。FCM は、デバイス グループを表す新しい notification_key を返します。

HTTP POST リクエスト

次のようなリクエストを https://fcm.googleapis.com/fcm/notification に送信します。

https://fcm.googleapis.com/fcm/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://fcm.googleapis.com/fcm/notification?notification_key_name=appUser-Chris
Content-Type:application/json
Authorization:key=API_KEY
project_id:SENDER_ID
{}

指定した通知キー名に対する GET リクエストごとに、サーバーからエンコードされた一意の文字列が返されます。各文字列は異なるキーであるかのように見える場合がありますが、実際には有効な「notification_key」値です。

デバイス グループでのデバイスの追加と削除

既存のグループでデバイスを追加または削除するには、operation パラメータを add または remove に設定した 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 クライアント アプリでのデバイス グループの管理

クライアント上でデバイス グループを管理できると、サーバーが利用できない場合に便利です。クライアント上でデバイス グループを作成するには、デバイスに少なくとも 1 つの Google アカウントがある必要があります。クライアント上で通知キーを作成するプロセスは、上記のサーバー側でのプロセスと大きく異なります。

クライアント上でデバイス グループを作成するには:

クライアント ID を取得する

  1. Google Developers Console でプロジェクトを開きます。
  2. 左上の メニューから、[API とサービス]、[認証情報] の順に選択します。
  3. [認証情報を作成] をクリックし、[OAuth クライアント ID] を選択します。
  4. クライアント ID の作成ダイアログで、アプリケーションの種類として [ウェブ アプリケーション] を選択し、[作成] をクリックします。
  5. クライアント ID に対して表示されている値をコピーします。このクライアント ID は idToken の生成に使用する Google アカウント「scope」を表します。

デバイス上で Google アカウントを検証する

Google Developers Console からクライアント ID を取得したら、デバイスに Google アカウントがあるかどうかを確認します。

Java

// 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() {
    // This call requires the Android GET_ACCOUNTS permission
    Account[] accounts = AccountManager.get(this /* activity */).
            getAccountsByType("com.google");
    if (accounts.length == 0) {
        return null;
    }
    return accounts[0].name;
}

Kotlin

// This snippet takes the simple approach of using the first returned Google account,
// but you can pick any Google account on the device.
@SuppressLint("MissingPermission")
fun getAccount(): String {
    // This call requires the Android GET_ACCOUNTS permission
    val accounts = AccountManager.get(this /* activity */).getAccountsByType("com.google")
    return if (accounts.isEmpty()) {
        ""
    } else accounts[0].name
}

認証トークンを取得する

次に GoogleAuthUtil クラスを使用して、認証トークン(idToken)を取得します。次に例を示します。

Java

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(this, accountName, scope);
} catch (Exception e) {
    Log.w(TAG, "Exception while getting idToken: " + e);
}

Kotlin

val accountName = getAccount()

// Initialize the scope using the client ID you got from the Console.
val scope = "audience:server:client_id:" +
        "1262xxx48712-9qs6n32447mcj9dirtnkyrejt82saa52.apps.googleusercontent.com"

var idToken: String? = null
try {
    idToken = GoogleAuthUtil.getToken(this, accountName, scope)
} catch (e: Exception) {
    Log.w(TAG, "Exception while getting idToken: $e")
}

グループでデバイスを追加または削除する

グループの登録トークンを追加または削除する、https://fcm.googleapis.com/fcm/googlenotification への HTTP POST リクエストを作成します。リクエスト ヘッダーでは project_id送信者 ID に、Content-Type が JSON に設定されている必要があります。

グループに追加する

追加オペレーションには、operation キー(add に設定)、id_token キー(上記の手順で取得した idToken に設定)、notification_key_name キー、registration_ids キーが必要です。userEmail 変数は、以下に示すように、accounts[0].name の値から派生できます。クライアントには、このメールまたはアカウントにマッピングされたグループだけを管理する権限が与えられています。

Java

public String addToGroup(
        String senderId, String userEmail, String registrationId, String idToken)
        throws IOException, JSONException {
    URL url = new URL("https://fcm.googleapis.com/fcm/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");
}

Kotlin

@Throws(IOException::class, JSONException::class)
fun addToGroup(
    senderId: String,
    userEmail: String,
    registrationId: String,
    idToken: String
): String {
    val url = URL("https://fcm.googleapis.com/fcm/googlenotification")
    val con = url.openConnection() as HttpURLConnection
    con.doOutput = true

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

    // HTTP request
    val data = JSONObject()
    data.put("operation", "add")
    data.put("notification_key_name", userEmail)
    data.put("registration_ids", JSONArray(arrayListOf(registrationId)))
    data.put("id_token", idToken)

    val os = con.outputStream
    os.write(data.toString().toByteArray(charset("UTF-8")))
    os.close()

    // Read the response into a string
    val `is` = con.inputStream
    val responseString = Scanner(`is`, "UTF-8").useDelimiter("\\A").next()
    `is`.close()

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

オペレーションが成功すると、notification_key が返されます。今後のオペレーションで使用するために、notification_key と、対応する notification_key_name を保存します。

グループから削除する

削除オペレーションには、operation キー(remove に設定)、id_token キー(上記の手順で取得した idToken に設定)、notification_key_name キー、registration_ids キーが必要です。

Java

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

Kotlin

// HTTP request
val data = JSONObject()
data.put("operation", "remove")
data.put("notification_key_name", userEmail)
data.put("registration_ids", JSONArray(arrayListOf(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 に関連付けられた 1 つ以上の登録トークンにメッセージを配信できない場合、アプリサーバーはバックオフを行いながら再試行を繰り返します。

メンバーのないデバイス グループにサーバーがメッセージを送信しようとすると、レスポンスには次のように成功 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 つの登録トークンが関連付けられており、メッセージはそれらすべてに正しく送信されました。

{
  "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 レスポンスが返されます。

メッセージ オプションの一覧については、選択した接続サーバー プロトコル(HTTP または XMPP)のリファレンス情報をご覧ください。

デバイス グループへのアップストリーム メッセージの送信

クライアント アプリは、to フィールドに適切な通知キーを設定してメッセージの宛先を指定することによって、デバイス グループにアップストリーム メッセージを送信できます。

FCM に次の呼び出しを行うことで、アップストリーム メッセージが通知キーに送信されます。オブジェクトは Key-Value ペアで構成されます。

Java

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

Kotlin

val to = "a_unique_key" // the notification key
val msgId = AtomicInteger()
FirebaseMessaging.getInstance().send(RemoteMessage.Builder(to)
        .setMessageId(msgId.get().toString())
        .addData("hello", "world")
        .build())