複数端末を所持している人を考慮してプッシュ通知を送る


Gmailとかハングアウトとかって、複数端末で通知を受けても、1つの端末で消せばちゃんと全部の端末で通知消えるんですよね。

これがやりたい。

あれ、どうやるの? という話です。

ざっくり

プッシュを受け取った端末が「受け取ったよ!」って空プッシュを送って send-to-sync する、みたいなことをやらないといけないようです。

以下のリファレンスを見ると、Device Group Messagingというのをつかうだけで、GCMがいい感じにやってくれるようにも見えますが、実際に動かしてみると、プッシュ消去は同期されませんでした・・・・・・。

http://www.androiddocs.com/google/gcm/notifications.html
https://firebase.google.com/docs/cloud-messaging/android/device-group

Device Group Messaging

FCMには、メッセージ送信の to パラメータにグループを指定することで、そのグループに属する端末たちの通知が同期される仕組みがあります。
グループがあれば、個々のデバイストークンを指定せずにメッセージが送れるので、ユーザ単位でプッシュを送りたいケースでは、1デバイスであっても使う価値はありそうです。

グループ?

https://android.googleapis.com/gcm/notification というエンドポイントにPOSTリクエストを出すことで、グループのメンバー管理ができるようです。

初めてグループを作るとき

{
   "operation": "create",
   "notification_key_name": "HogeAppUser0003",
   "registration_ids": ["xxxxx", "yyyyy"]
}

みたいなのをPOSTすると、グループのID文字列(notification_key)が返ってくるので、
それをどこかに覚えておいて送信時に使います。

registration_id は、Androidアプリ側で FirebaseInstanceId.getInstance().getToken(); で取得できるやつを指定。(これ、リファレンスには曖昧な書き方されてて、ちょっとハマるとこw)

グループにメンバーを追加・削除するとき

{
   "operation": "add",
   "notification_key": "作成時にもらったグループのID文字列",
   "registration_ids": ["zzzzz"]
}

とか

{
   "operation": "remove",
   "notification_key": "作成時にもらったグループのID文字列",
   "registration_ids": ["xxxxx"]
}

とかをPOSTすると、グループのID文字列(notification_key)が返ってきて成功! となります。

非常にくせのある仕様・・・

https://android.googleapis.com/gcm/notification のAPIは、
グループが0人か1人以上かによって挙動が変わります。これがすごくめんどくさい。

おそらく

  • 0人の時→createのみ可能。新規notification_keyが発行される。
  • 1人の時→add/removeのみ可能。notification_keyが引き継がれる

のような仕様なんだと思うんですが、

  • create したあと create →400エラー
  • create したあと同じ人を add →200 OK (notification_keyが返る)
  • add したあと同じ人を add →200 OK (notification_keyが返る)

あたりはわかるんですが、

  • createもしくはaddした人を remove →200 OK  ・・・ notification_keyが返るが、グループの残りが0人だったら、もうこのnotification_keyは使えない
  • remove した人を removeグループメンバーがのこり0人であれば 400エラー 、1人以上いたら 200 OK
  • remove した人を addグループメンバーがのこり0人であれば 400エラー 、1人以上いたら 200 OK
  • remove した人を create →グループメンバーが0人であれば 200 OK、 1人以上いたら 400エラー

あたりは、かなりくせっぽいなー (≒ こんな管理をやりたくないなー) と・・・w

ちなみに、Androidアプリから登録する際には、notification_key の指定無しで add/removeができるので、このようなめんどくさい考慮はいりません。(が、別のめんどくさい考慮が必要です・・・後述)
 
 

誰がメンバー管理をするべきか?

基本的に、「サーバーサイドで端末グループ管理なんて自前でやりたくないよー」っていう場合のことを書きます。

パターン1 グループ登録までAndroid側でやる

Android側で

  • ユーザIDから、一意で推測されにくい文字列を生成する ・・・これをグループ名としてつかう
  • アプリにログイン完了後、FCMトークンを取得し、グループを作成 or メンバー追加する
  • グループIDをサーバに送る

みたいなことをやる流れになります。

サーバーサイドでは

User has_one fcm_group_key

みたいな関連を持っておくだけでプッシュが送れるので、シンプルな構成になります。

ただ、AndroidのAPIは、 id_token が必要で、こいつは Googleログインを組み込んであげないと取得できない シロモノでして・・・
まぁ頑張って実装しましょう・・・。

 

とはいかないこともあるので・・・

パターン2 サーバー側でグループ登録処理だけはやる

Android側

  • アプリにログイン完了後、FCMトークンを取得後、それをサーバーに送る

サーバー側

  • もらったFCMトークンをもとに、グループを作成 or メンバー追加する
  • グループIDを登録する

という流れになります。

この場合は

User
 has_many fcm_tokens
 has_one fcm_group_key

のように2つの関連をもつことになるでしょう。

誰がどのグループに属しているかは知らなくてもよいですが、先に述べたような「くせのある仕様」を攻略する必要があります。

とはいえ、端末個別にもプッシュはおくりたいし、端末グループ単位(ユーザごと)にもプッシュはおくりたい、 という場合は必然的にこちらのパターンを採用することになるでしょう・・・。

  
  

で、どっちがいいの? といわれると、

  • サーバー側でFCMトークンを持つということ自体が何かと不便
  • そもそも端末単位で送りたいプッシュなんて、そんなにないでしょ?

という独断と偏見で、私はパターン1をおすすめします。 が、システムによって要件はまちまちなので、そこは適宜考えて下さい。

まとめ

まず、プッシュ通知をおくる、となったときには

  • 重要じゃない(≒ アプリの機能と関係のない)通知を送ろうとしてないか?
  • プッシュ通知頻度は適正か?

だけでなく、

  • 複数端末にプッシュした際に、プッシュの消去が同期されるべきか、否か?

という観点を持ちましょう。

プッシュの消去を同期したかったら Device Group Messaging を使いましょう。