[この記事は Frank van Puffelen、エンジニアによる The Firebase Blog の記事 "" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]




Frank van Puffelen




Frank van Puffelen
エンジニア

本記事では、Firebase Database、Cloud Messaging、Node.js を使用して Android 端末間で通知を送信する方法を説明します。

Firebase Database は、マルチユーザー モバイル アプリケーション構築のためのすばらしいバックエンドです。複数のユーザーがアプリケーションを開いていれば、あるユーザーによる変更はすべての接続中のユーザーにミリ秒以内に同期されます。


チャット アプリケーションがその良い例です。あるユーザーが送信したメッセージは即座に他のユーザーに同期されるため、たいへんスムーズな使用感を実現できます。

しかし、Android 端末上でアプリを実行していないユーザーはどうなるでしょうか。その場合、端末は Firebase Database サーバーに接続されていないため、変更を自動的に受信することはできません。通常のチャット メッセージの場合はそれでも大丈夫ですが、ほとんどのチャットアプリで @ をつけて誰かを呼び出すときは、そのユーザーを引き戻したいことがほとんどでしょう(例: 「@puf ちょっといいかな?」)。

通常、そのような場合は、Firebase Cloud Messaging を使ってそのユーザーの端末に通知を送ります。このような通知は端末の通知エリアに表示され、何かおもしろいイベントが起こったときにユーザーをアプリに引き戻す手段となります。



本記事では、Firebase Cloud Messaging を使って端末に通知を送信してみます。API キーを Android 端末に公開しなくてもいいように、FCM と通信する Node スクリプトを記述します。しかし、まず最初に、Android 端末から通知を 送信 するコードがどのようなものかを理解しましょう。

Android アプリからプッシュ通知を送信する

ユーザーに通知を送信するには、Android アプリで次のようにします。
sendNotificationToUser("puf", "Hi there puf!");
sendNotificationToUser() メソッドは、次のように実装されているヘルパー メソッドです。
public static void sendNotificationToUser(String user, final String message) {
    Firebase ref = new Firebase(FIREBASE_URL);
    final Firebase notifications = ref.child("notificationRequests");

    Map notification = new HashMap<>();
    notification.put("username", user);
    notification.put("message", message);

    notifications.push().setValue(notification);
}

おそらく、これは予想とは異なるものでしょう。このコードは、通知データをデータベースに書き込んでいるだけです。これでどうやって通知を送信しているのでしょうか。
実は、通知の送信は行っていません。ここでは、データベースをキューとして使用しています。Android アプリは通知を送信するリクエストをデータベースに書き込みます。それを Node スクリプトがピックアップして、Cloud Messaging 経由で通知を送信します。そのスクリプトは後ほど見てゆきます。まずは、このアプリのデータ構造について少しお話ししましょう。

データ構造

他のアプリケーションと同じく、Firebase をバックエンドとしたアプリケーションでもデータ構造は特に重要な部分の 1 つです。通知を送信するためのデータ構造は既に見ています。
  notificationRequests
    $pushid
      message: "Hello there"
      username: "puf"

これが Android アプリが書き込んだノードです。各通知リクエストは、送信するメッセージと通知対象のユーザー名で構成されています。ユーザー名を実際の通知にどのようにマッピングするかは、アプリケーションによって異なります。この場合は、トピックベースの通知を使用します。つまり、ユーザー名は/topics/user_ というトピックにマッピングされます。この場合、メッセージは /topics/user_puf に送信されます(Android アプリは、このトピックをサブスクライブします)。
では、先ほどお話しした Node コードを見てみましょう。

Node コード

ここまでで、Android アプリからデータベースに通知リクエストを書き込む方法と、データベース構造がどのようなものかがわかりました。それでは、実際に通知を送信するコードを書いてゆきます。
これは、先ほど確認した通知キューを監視する Node プロセスになります。このキューに子供が追加されるたびに必要な情報を取り出し、Cloud Messaging REST API を呼び出して通知を送信します。処理が成功した場合、キューから通知リクエストを削除します。

var firebase = require('firebase');
var request = require('request');

var API_KEY = "..."; // Your Firebase Cloud Server API key

firebase.initializeApp({
  serviceAccount: ".json",
  databaseURL: "https://.firebaseio.com/"
});
ref = firebase.database().ref();

function listenForNotificationRequests() {
  var requests = ref.child('notificationRequests');
  ref.on('child_added', function(requestSnapshot) {
    var request = requestSnapshot.val();
    sendNotificationToUser(
      request.username,
      request.message,
      function() {
        request.ref().remove();
      }
    );
  }, function(error) {
    console.error(error);
  });
};

function sendNotificationToUser(username, message, onSuccess) {
  request({
    url: 'https://fcm.googleapis.com/fcm/send',
    method: 'POST',
    headers: {
      'Content-Type' :' application/json',
      'Authorization': 'key='+API_KEY
    },
    body: JSON.stringify({
      notification: {
        title: message
      },
      to : '/topics/user_'+username
    })
  }, function(error, response, body) {
    if (error) { console.error(error); }
    else if (response.statusCode >= 400) {
      console.error('HTTP Error: '+response.statusCode+' - '+response.statusMessage);
    }
    else {
      onSuccess();
    }
  });
}

// start listening
listenForNotificationRequests();
child_added イベントをリッスンしているため、キューの各通知リクエストに対して sendNotificationToUser() が呼び出されます。送信が成功すると、キューからリクエストを削除します。今回の単純なスクリプトでは、自動リトライは行っていません。そのため、スクリプトを再起動した場合のみ、失敗した通知がリトライされます。もう少し拡張性があるアプローチをとる場合は、firebase-queue ライブラリの使用を検討してください。

スクリプトに API_KEY 定数が含まれていることにお気づきかもしれません。これは、メッセージを送信するために Firebase Cloud Messaging から取得したキーです。まさにこの点が、Android アプリでこのコードを実行しない理由です。API キーがわかれば、そのアプリの名前でメッセージを送信することができるので、悪用される可能性があります。このキーをサーバー上の Node スクリプトで保持することによって、Android アプリのユーザーがキーを入手できないようにしています。

Android アプリで通知を受信する

Firebase Cloud Messaging SDK が通知メッセージを扱う方法のおかげで、Android のコードは最低限で済みます。アプリがバックグラウンドで動作している際に通知メッセージを受け取ると、システム通知エリアにメッセージが表示されます。ユーザーがメッセージをクリックすると、アプリが自動的に開きます。こういった形でアプリを再び開いてもらうことは、まさにこのアプリで実現したかったことです。そのために行う必要があるのは、Firebase Messaging ライブラリをインクルードしてユーザー名のトピックをサブスクライブすることだけです。

トピックのサブスクライブ


あるユーザー宛てのメッセージを識別するために、ユーザー名に一致するトピックを使用します。

String username = "puf";
FirebaseMessaging.getInstance().subscribeToTopic("user_"+username);

この部分はアプリに依存する処理であるため、このスニペットではユーザー名をハードコードしています。しかし、一度ユーザー名さえ決まってしまえば、通知の登録に必要なのは 1 行のコードだけで済むことはわかるでしょう(あとは、アプリがバックグラウンドである場合に通知を表示する部分だけです)。
アプリがフォアグランドである場合に通知の処理を行いたい場合や、メッセージとともに他のデータも送りたい場合は、Firebase Cloud Messaging とメッセージ タイプのドキュメントをご覧ください。

まとめ


本記事では、Firebase と Node スクリプトを使用して Android アプリにプッシュ通知を送る方法を説明しました。今回は Android コードから通知を送信しましたが、Firebase Database にアクセスできる他のアプリケーションからも同じように簡単に送信することができます。iOS サポートを追加することも簡単で、依存性を追加してリモート通知用の登録を行うだけです。


Posted by Yoshifumi Yamaguchi - Developer Relations Team