この Codelab は、Kotlin を使った高度な Android 開発コースの一部です。Codelab を順番に進めると、このコースを最大限に活用できますが、これは必須ではありません。コースの Codelab はすべて、Kotlin を使った高度な Android 開発の Codelab ランディング ページに記載されています。
はじめに
前の Codelab では、アプリ内で作成およびトリガーされる通知をタイマーに追加しました。通知のもう 1 つの重要なユースケースは、アプリが実行されていない場合でも受信できるプッシュ通知をリモートで送信することです。
プッシュ通知とは
プッシュ通知とは、サーバーがモバイル デバイスに「プッシュ」する通知のことです。アプリが実行中かどうかに関係なく、デバイスに配信できます。
プッシュ通知は、ユーザーにアップデートを知らせたり、タスクや機能を思い出させたりするのに最適な方法です。商品が再入荷するのを待っているとします。プッシュ通知を使用すると、ショッピング アプリは在庫状況を毎日確認しなくても、在庫の更新を知らせてくれます。
プッシュ通知は、パブリッシュ/サブスクライブ パターンを利用して、バックエンド アプリが関連性の高いコンテンツを関心のあるクライアントにプッシュできるようにします。パブリッシュ/サブスクライブ モデルがない場合、アプリのユーザーはアプリの更新を定期的に確認する必要があります。このプロセスはユーザーにとって面倒で信頼性が低いものです。また、クライアントの数が増えると、これらの定期的なチェックは、アプリのサーバーとユーザーのデバイスの両方で、ネットワークと処理リソースに過大な負荷をかけることになります。
他のすべての種類の通知と同様に、プッシュ通知でもユーザーを尊重するようにしてください。通知の内容がユーザーにとって興味深いものでなかったり、タイミングが適切でなかったりする場合は、アプリからのすべての通知を簡単にオフにできます。
Firebase Cloud Messaging とは
Firebase Cloud Messaging は、モバイル開発用の Firebase プラットフォームの一部です。通常は、モバイル デバイスと通信して通知をトリガーできるサーバーをゼロから設定する必要があります。Firebase Cloud Messaging を使用すると、サーバーを設定しなくても、インストール済みアプリのすべてのユーザーまたは一部のユーザーに通知を送信できます。たとえば、ユーザーにリマインダーを送信したり、無料ギフトなどの特別なプロモーションを提供したりできます。通知を 1 台または複数のデバイスにリモートでプッシュできます。
Firebase Cloud Messaging を使用して、バックエンド アプリや Firebase プロジェクトからユーザーにデータを転送することもできます。
この Codelab では、Firebase Cloud Messaging を使用して Android アプリのプッシュ通知を送信する方法と、データを送信する方法を学びます。
この Codelab で問題(コードのバグ、文法的な誤り、不明確な表現など)が見つかった場合は、Codelab の左下隅にある [誤りを報告] から問題を報告してください。
前提となる知識
以下について把握しておく必要があります。
- Kotlin で Android アプリを作成する方法。特に、Android SDK を使用します。
- アーキテクチャ コンポーネントとデータ バインディングを使用してアプリを設計する方法。
- ブロードキャスト レシーバに関する基本的な知識。
- AlarmManager に関する基礎知識。
- NotificationManager を使用して通知を作成して送信する方法。
学習内容
- Firebase Cloud Messaging を介してユーザーにメッセージをプッシュする方法。
- Firebase Cloud Messaging の一部であるデータ メッセージを使用して、バックエンドからアプリにデータを送信する方法。
演習内容
- スターター アプリにプッシュ通知を追加します。
- アプリの実行中に Firebase Cloud Messaging を処理します。
- Firebase Cloud Messaging を使用してデータを転送する。
この Codelab では、Android アプリで通知を使用するの前の Codelab のコードを使用します。前の Codelab では、調理タイマーが終了したときに通知を送信するエッグタイマー アプリを作成しました。この Codelab では、Firebase Cloud Messaging を追加して、アプリユーザーにプッシュ通知を送信し、卵を食べるよう促します。
サンプルアプリを入手するには、次のいずれかを行います。
GitHub からリポジトリのクローンを作成し、starter ブランチに切り替えます。
$ git clone https://github.com/googlecodelabs/android-kotlin-notifications-fcm
または、リポジトリを ZIP ファイルとしてダウンロードし、Android Studio で開くこともできます。
ステップ 1: Firebase プロジェクトを作成する
Android アプリに Firebase を追加する前に、Android アプリに接続するための Firebase プロジェクトを作成します。
- Firebase コンソールにログインします。
- [プロジェクトを追加] をクリックし、[プロジェクト名] を選択するか、新しいプロジェクト名を入力します。プロジェクトに fcm-codelab という名前を付けます。
- [続行] をクリックします。
- [このプロジェクトで Google アナリティクスを有効にする ] ボタンをオフにすると、Google アナリティクスの設定をスキップできます。
- [Create Project] をクリックして、Firebase プロジェクトの設定を完了します。
ステップ 2: アプリを Firebase に登録する
Firebase プロジェクトを作成したら、プロジェクトに Android アプリを追加できます。
- Firebase コンソールの [プロジェクトの概要] ページの中央にある Android アイコンをクリックして設定ワークフローを起動します。
- [Android パッケージ名] フィールドに「
com.example.android.eggtimernotifications
」と入力します。 - [アプリの登録] をクリックします。
重要: アプリを Firebase プロジェクトに登録した後で、この値を追加または変更することはできません。そのため、アプリの正しい ID を入力してください。
ステップ 3: Firebase 構成ファイルをプロジェクトに追加する
Firebase Android 構成ファイルをアプリに追加します。
- [google-services.json をダウンロード] をクリックして、Firebase Android 構成ファイル(
google-services.json
)を取得します。構成ファイルに余分な文字が付加されておらず、名前がgoogle-services.json
であることを確認してください。 - 構成ファイルをアプリのモジュール(アプリレベル)ディレクトリに移動します。
ステップ 4: Firebase プロダクトを有効にするように Android プロジェクトを構成する
アプリで Firebase プロダクトを有効にするには、Gradle ファイルに google-services プラグインを追加する必要があります。
- ルートレベル(プロジェクト レベル)の Gradle ファイル(
build.gradle
)で、Google の Maven リポジトリがあることを確認します。 - 次に、ルールを追加して Google サービス プラグインを指定します。
build.gradle
buildscript {
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
}
dependencies {
// ...
// Add the following line:
classpath 'com.google.gms:google-services:4.3.2' // Google Services plugin
}
}
allprojects {
// ...
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
// ...
}
}
- モジュール(アプリレベル)の Gradle ファイル(通常は
app/build.gradle
)で、ファイルの末尾にプラグインを適用する行を追加します。
app/build.gradle
apply plugin: 'com.android.application'
android {
// ...
}
// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin
このタスクでは、プッシュ通知を利用するために Firebase Cloud Messaging(FCM)をプロジェクトに追加します。
この Codelab の FCM の Android サービスコードは MyFirebaseMessagingService.kt
にあります。次の手順では、Android アプリにコードを追加します。
実装をテストするには、Notifications Composer を使用します。Notifications Composer は、Firebase コンソールのウェブサイトからメッセージを作成して送信するのに役立つツールです。
MyFirebaseMessagingService.kt
を開く- ファイル、特に次の関数を検査します。
onNewToken()
- サービスが Android マニフェストに登録されている場合、自動的に呼び出されます。この関数は、アプリを初めて実行したときと、Firebase がアプリの新しいトークンを発行するたびに呼び出されます。トークンは、Firebase バックエンド プロジェクトへのアクセスキーです。これは特定のクライアント デバイス用に生成されます。このトークンにより、Firebase はバックエンドがメッセージを送信するクライアントを認識します。Firebase は、このクライアントが有効で、この Firebase プロジェクトにアクセスできるかどうかも認識しています。onMessageReceived
- アプリが実行中で、Firebase がアプリにメッセージを送信したときに呼び出されます。この関数は、通知またはデータ メッセージのペイロードを伝達できるRemoteMessage
オブジェクトを受け取ります。通知とデータ メッセージ ペイロードの違いについては、この Codelab の後半で詳しく説明します。
ステップ 1: 単一のデバイスに FCM 通知を送信する
通知コンソールでは、通知の送信をテストできます。コンソールを使用して特定のデバイスにメッセージを送信するには、そのデバイスの登録トークンを知っておく必要があります。
Firebase バックエンドが新しいトークンまたは更新されたトークンを生成すると、onNewToken()
関数が呼び出され、新しいトークンが引数として渡されます。単一のデバイスをターゲットにする場合、またはブロードキャスト メッセージを送信するデバイスのグループを作成する場合は、FirebaseMessagingService
を拡張して onNewToken()
をオーバーライドすることで、このトークンにアクセスする必要があります。
AndroidManifest.xml
を開き、次のコードのコメントを解除して、タイマー アプリのMyFirebaseMessagingService
を有効にします。Android マニフェストのサービス メタデータは、MyFirebaseMessagingService
をサービスとして登録し、このサービスが FCM から送信されたメッセージを受信するようにインテント フィルタを追加します。メタデータの最後の部分では、Firebase のbreakfast_notification_channel_id
をdefault_notification_channel_id
として宣言しています。この ID は次のステップで使用します。
<!-- AndroidManifest.xml -->
<!-- TODO: Step 3.0 uncomment to start the service -->
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<!-- [START fcm_default_icon] -->
<!--
Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more.
-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/common_google_signin_btn_icon_dark"/>
<!--
Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more.
-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent"/> <!-- [END fcm_default_icon] -->
<!-- [START fcm_default_channel] -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/breakfast_notification_channel_id" />
<!-- [END fcm_default_channel] -->
ユーザーがタイマーや FCM プッシュ通知を個別に有効/無効にできるように、FCM 用の新しい通知チャンネルを作成することをおすすめします。
ui/EggTimerFragment.kt
を開きます。onCreateView()
に、次のチャンネル作成コードを追加します。
// EggTimerFragment.kt
// TODO: Step 3.1 create a new channel for FCM
createChannel(
getString(R.string.breakfast_notification_channel_id),
getString(R.string.breakfast_notification_channel_name)
)
MyFirebaseMessagingService.kt
を開き、onNewToken()
関数のコメントを解除します。この関数は、新しいトークンが生成されたときに呼び出されます。
// MyFirebaseMessagingService.kt
// TODO: Step 3.2 log registration token
// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the
* InstanceID token is initially generated so this is where you would retrieve
* the token.
*/
override fun onNewToken(token: String?) {
Log.d(TAG, "Refreshed token: $token")
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(token)
}
// [END on_new_token]
- エッグタイマー アプリを実行します。
- logcat([View] > [Tool Windows] > [Logcat])を観察します。次のようなトークンを示すログ行が表示されます。このデバイスにメッセージを送信するには、このトークンが必要です。この関数は、新しいトークンが作成された場合にのみ呼び出されます。
2019-07-23 13:09:15.243 2312-2459/com.example.android.eggtimernotifications D/MyFirebaseMsgService: Refreshed token: f2esflBoQbI:APA91bFMzNNFaIskjr6KIV4zKjnPA4hxekmrtbrtba2aDbh593WQnm11ed54Mv6MZ9Yeerver7pzgwfKx7R9BHFffLBItLEgPvrtF0TtX9ToCrXZ5y7Hd-m
注: logcat メッセージにトークンが表示されない場合は、アプリがすでにトークンを受け取っている可能性があります。その場合は、アプリをアンインストールすると、新しいトークンを受け取ることができます。
通知を送信してテストできるようになりました。通知を送信するには、Notifications Composer を使用します。
- Firebase コンソールを開き、プロジェクトを選択します。
- 次に、左側のナビゲーションから [クラウド メッセージング] を選択します。
- [初めてのメッセージを送信する] をクリックします。
- 通知のタイトルに
Time for Breakfast!
、通知のテキストにDon't forget to eat eggs!
と入力し、[テスト メッセージを送信] を選択します。[Test on device] ポップアップ ダイアログが表示され、FCM 登録トークンを入力するよう求められます。
- logcat からアプリトークンをコピーします。
- このトークンをポップアップ ウィンドウの [FCM 登録トークンを追加] フィールドに貼り付け、トークンの横にある [追加] ボタンをクリックします。
- 表示されたチェックボックスのリストで、トークンを選択します。[テスト] ボタンが有効になります。
- デバイスで、Egg Timer アプリをバックグラウンド に移動します。
- ポップアップで [テスト] をクリックします。
- [テスト] をクリックすると、アプリをバックグラウンドで実行しているターゲット クライアント デバイスのシステム通知トレイに通知が届きます。(アプリがフォアグラウンドで動作しているときに FCM メッセージを処理する方法については、後で詳しく説明します)。
タスク: トピックに FCM 通知を送信する
FCM トピック メッセージングは、パブリッシュ/サブスクライブ モデルに基づいています。
メッセージ アプリは、パブリッシュ/サブスクライブ モデルの好例です。アプリが 10 秒ごとに新着メッセージをチェックするとします。これにより、スマートフォンのバッテリーが消耗するだけでなく、不要なネットワーク リソースが使用され、アプリのサーバーに不要な負荷がかかります。代わりに、クライアント デバイスはサブスクライブして、アプリ経由で新しいメッセージが配信されたときに通知を受け取ることができます。
トピックを使用すると、特定のトピックにオプトインした複数のデバイスにメッセージを送信できます。クライアントにとって、トピックはクライアントが関心のある特定のデータソースです。サーバーの場合、トピックは特定のデータソースの更新を受け取るようにオプトインしたデバイスのグループです。トピックは、ニュース、天気予報、スポーツの結果などの通知のカテゴリを表示するために使用できます。この Codelab のこのパートでは、「朝食」トピックを作成して、朝食に卵を食べるよう関心のあるアプリユーザーにリマインドします。
トピックに登録するには、クライアント アプリが Firebase Cloud Messaging subscribeToTopic(
)
関数を呼び出し、トピック名 breakfast
を指定します。この呼び出しには 2 つの結果が考えられます。呼び出しが成功すると、サブスクライブされたメッセージで OnCompleteListener
コールバックが呼び出されます。クライアントが登録に失敗すると、コールバックは代わりにエラー メッセージを受け取ります。
アプリでは、ユーザーが朝食トピックに自動的に登録されます。ただし、ほとんどのプロダクション アプリでは、ユーザーが購読するトピックを制御できるようにすることをおすすめします。
EggTimerFragment.kt
を開き、空のsubscribeTopic()
関数を見つけます。FirebaseMessaging
のインスタンスを取得し、トピック名を指定してsubscibeToTopic()
関数を呼び出します。addOnCompleteListener
を追加して、サブスクリプションが成功したか失敗したかについて FCM から通知を受け取ります。
// EggTimerFragment.kt
// TODO: Step 3.3 subscribe to breakfast topic
private fun subscribeTopic() {
// [START subscribe_topics]
FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
.addOnCompleteListener { task ->
var msg = getString(R.string.message_subscribed)
if (!task.isSuccessful) {
msg = getString(R.string.message_subscribe_failed)
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
// [END subscribe_topics]
}
- アプリの起動時に
subscribeTopic()
関数を呼び出して、トピックをサブスクライブします。onCreateView()
までスクロールして、subscribeTopic()
の呼び出しを追加します。
// EggTimerFragment.kt
// TODO: Step 3.4 call subscribe topics on start
subscribeTopic()
return binding.root
- 朝食トピックを購読するには、アプリをもう一度実行します。「トピックに登録しました」というトースト メッセージが表示されます。
これで、トピックへのメッセージ送信をテストできます。
- Notifications Composer を開き、[Compose Notification] を選択します。
- 通知の [通知のタイトル] と [通知のテキスト] を以前と同様に設定します。
- 今回は、メッセージを単一のデバイスに送信するのではなく、[ターゲット] で [トピック] をクリックし、メッセージ トピックとして
breakfast
を入力します。
- [Scheduling] で [Now] を選択します。
- テストデバイスでアプリがバックグラウンドで実行されていることを確認します。
- [確認]、[公開] の順にクリックします。複数のデバイスでアプリを実行できる場合は、このトピックに登録されているすべてのデバイスで通知が受信されることをテストして確認できます。
アプリには、Egg と Breakfast の 2 つの通知チャンネルがあります。クライアント デバイスで、アプリアイコンを長押しし、[情報] を選択して、[通知] をクリックします。次のスクリーンショットに示すように、[Egg] と [Breakfast] の通知チャンネルが表示されます。[Breakfast] チャンネルの選択を解除すると、このチャンネルで送信された通知はアプリに届かなくなります。
通知を使用する際は、ユーザーがいつでも通知チャンネルをオフにできることを常に念頭に置いてください。
ステップ 1: データ メッセージ
FCM メッセージには、クライアント アプリでメッセージを処理するデータ ペイロードを含めることもできます。通知メッセージではなくデータ メッセージを使用します。
データ メッセージを処理するには、MyFirebaseMessagingService
の onMessageReceived()
関数でデータ ペイロードを処理する必要があります。ペイロードは remoteMessage
オブジェクトの data
プロパティに格納されます。remoteMessage
オブジェクトと data
プロパティの両方を null
にできます。
MyFirebaseMessagingService.
を開くremoteMessage
オブジェクトのdata
プロパティに値があるかどうかを確認し、データをログに出力します。
// MyFirebaseMessagingService.kt
// [START receive_message]
override fun onMessageReceived(remoteMessage: RemoteMessage?) {
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "From: ${remoteMessage?.from}")
// TODO: Step 3.5 check messages for data
// Check if the message contains a data payload.
remoteMessage?.data?.let {
Log.d(TAG, "Message data payload: " + remoteMessage.data)
}
}
// [END receive_message]
コードをテストするには、Notifications Composer を再度使用します。
- 通知コンポーザーを開き、新しいメッセージを作成して、そのターゲット を「朝食」トピックに設定します。
- 今回は、手順 4 の [追加オプション] で、[カスタムデータ] のキーと値のプロパティを次のように設定します。
- キー:
eggs
- 値:
3
- アプリがフォアグラウンドで実行されていることを確認します。アプリがバックグラウンドにある場合、FCM メッセージによって自動通知がトリガーされ、ユーザーが通知をクリックすると
onMessageReceived()
関数はremoteMessage
オブジェクトのみを受け取ります。 - Notifications Composer からメッセージを送信し、logcat に表示されるデータ メッセージのログを確認します。
ステップ 2: フォアグラウンドとバックグラウンドでのメッセージの処理
アプリを実行しているクライアント デバイスが通知ペイロードとデータ ペイロードの両方を含むメッセージを受信した場合、アプリの動作は、そのデバイスでアプリがバックグラウンドとフォアグラウンドのどちらで動作しているかによって異なります。
- アプリがバックグラウンドで実行されている場合、メッセージに通知ペイロードが含まれていると、通知が通知トレイに自動的に表示されます。メッセージにデータ ペイロードも含まれている場合、ユーザーが通知をタップすると、アプリによってデータ ペイロードが処理されます。
- アプリがフォアグラウンドで実行されている場合、メッセージ通知に通知ペイロードが含まれていると、通知は自動的に表示されません。アプリは、
onMessageReceived()
関数で通知を処理する方法を決定する必要があります。メッセージにデータ ペイロードも含まれている場合は、両方のペイロードがアプリによって処理されます。
この Codelab では、アプリのユーザーに朝食に卵を食べるようリマインドします。データを送信する予定はないが、アプリがフォアグラウンドまたはバックグラウンドのどちらで動作しているかに関係なく、リマインダー通知が常に表示されるようにしたい。
卵タイマー アプリがインストールされているデバイスに FCM メッセージを送信すると、アプリが実行されていない場合やバックグラウンドで実行されている場合は、通知メッセージが自動的に表示されます。ただし、アプリがフォアグラウンドで実行されている場合は、通知は自動的に表示されません。代わりに、アプリのコードによってメッセージの処理方法が決定されます。FCM メッセージを受信したときにアプリがフォアグラウンドで動作している場合、onMessageReceived()
関数は FCM メッセージとともに自動的にトリガーされます。アプリはここで、通知とデータ ペイロードをサイレントに処理したり、通知をトリガーしたりできます。
アプリでは、アプリがフォアグラウンドにあるときにユーザーにリマインダーが届くようにする必要があります。通知をトリガーするコードを実装しましょう。
MyFirebaseMessagingService
でonMessageReceived()
関数を再度開きます。- データ メッセージを確認するために先ほど追加したコードの直後に、通知フレームワークを使用して通知を送信する次のコードを追加します。
// MyFirebaseMessagingService.kt
// TODO: Step 3.6 check messages for notification and call sendNotification
// Check if the message contains a notification payload.
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
sendNotification(it.body as String)
}
- アプリを再度実行し、通知コンポーザーを使用して通知を送信すると、アプリがフォアグラウンドで動作しているかバックグラウンドで動作しているかにかかわらず、Codelab の最初の部分で見たような通知が表示されます。
解答コードは、ダウンロードしたコードの master ブランチにあります。
FirebaseMessagingService
を拡張して FCM BroadcastReceiver を実装します。- Firebase Cloud Messaging(FCM)プロジェクトを設定し、Android アプリに FCM を追加します。
- Notifications Composer からプッシュ通知を送信して、アプリをテストします。
FirebaseMessaging
クラスのsubscribeToTopic()
関数を呼び出して、FCM トピックに登録します。RemoteMessage
オブジェクトを使用してデータ ペイロードを送信します。onMessageReceived()
関数でデータを処理します。- アプリがフォアグラウンドにある場合とバックグラウンドにある場合の両方で FCM を処理するロジックを追加します。
Udacity コース:
Firebase のドキュメント:
このコースの他の Codelab へのリンクについては、Kotlin を使った高度な Android 開発の Codelab のランディング ページをご覧ください。