はじめに
こんにちは。カイザーです。今回は、Androidでのローカルプッシュ通知をするために、WorkManagerを使用した実装を紹介します。
WorkManagerとは?
Jetpackの機能の1つで、簡単に定期的もしくは単発のバックグラウンドタスクを定義し、実行することができます。
実際には、JobScheduler、FirebaseJobDispatcher、AlarmManagerのいずれかを、OSのバージョンや、Google Play Servicesのインストール状況によって使い分けてくれるものです。
WorkManagerの導入
app/build.gradleに以下追加します。
1 2 3 4 5 | dependencies { ・・・ def work_version = "2.2.0" implementation "androidx.work:work-runtime-ktx:$work_version" } |
この後の実装をした際、以下のエラーが出る場合は、JVM target 1.8に変更する必要があります。
Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option
その場合は、以下を追加してください。
1 2 3 4 5 6 7 8 9 10 11 | android { ・・・ compileOptions { sourceCompatibility = 1.8 targetCompatibility = 1.8 } kotlinOptions { jvmTarget = "1.8" } } |
Android Studio側にも同様の設定項目がありますが、記事執筆時点のAndroid Studio 3.5では、上記設定を記載しないと反映されません。
実装例
主な登場人物は、Worker、WorkManager、WorkRequestの3つです。
Workerは実際にバックグラウンドタスクとして呼ばれ、WorkRequestで実行方法を指定し、WorkManagerエンキューする流れになります。
Workerの実装
WorkerManagerに登録し、実際にバックグラウンドタスクとして動作するクラスを実装します。
今回は、ここでNotificationChannelにプッシュ通知する実装をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class LocalNotificationWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result { val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(applicationContext, MainActivity::class.java), PendingIntent.FLAG_ONE_SHOT) val notification = NotificationCompat.Builder(applicationContext, "default") .setContentTitle(inputData.getString("title")) // enqueue元から渡されたタイトルテキストを通知にセット .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentIntent(pendingIntent) .build() NotificationManagerCompat.from(applicationContext).notify(1, notification) return Result.success() } } |
inputDataは、Workerをエンキューする時にあらかじめデータを渡され、実行時に使用可能なプロパティです。今回は、プッシュ通知のtitleに表示するようにしています。
OneTimeWorkRequestBuilderの実装
OneTimeRequestBuilderは、Workerを1回だけ動作させるリクエストです。
1 2 3 4 5 6 | val inputData = Data.Builder().putString("title", "単発メッセージ").build() val workRequest = OneTimeWorkRequestBuilder<LocalNotificationWorker>() .setInitialDelay(5, TimeUnit.SECONDS) .setInputData(inputData) .build() WorkManager.getInstance(this).enqueue(workRequest) |
コンストラクタのGenericksでWorkerの型を指定します。
setInitialDelayで、Workerの実行遅延時間を設定できます。これにより、相対的にプッシュ通知を表示したい時間を設定できます。
PeriodicWorkRequestBuilderの実装
PeriodicWorkRequestBuilder
は、Workerを定期実行させるリクエストです。
1 2 3 4 5 6 7 | val inputData = Data.Builder().putString("title", "繰り返しメッセージ").build( // 15分ごとに、5分以内に実行する val workRequest = PeriodicWorkRequestBuilder<LocalNotificationWorker>(15, TimeUnit.MINUTES, 5, TimeUnit.MINUTES) .setInputData(inputData) .build() WorkManager.getInstance(this).enqueue(workRequest) |
こちらは、コンストラクタで、リピート間隔を指定する必要があります。(第1、第2引数)
しかし、これだけでは確実に定期実行される訳ではありません。AndroidがDozeモードになっていると、実行されません。
そこで、実行時間(リピートする際に、何分以内に実行すべきか)を指定することで、確実に実行することができます。(第3、第4引数)
注意点としては、PeriodicWorkRequestでは、時間指定に制限があります。
リピート間隔は15分以上、実行時間は5分以上とする必要があります。これを満たしていないと、エンキューの際に以下ログと共に、Workerが実行されないので注意してください。
1 2 3 | W/WM-WorkSpec: Interval duration lesser than minimum allowed value; Changed to 900000 2019-09-29 22:46:18.255 20701-20701/ W/WM-WorkSpec: Flex duration lesser than minimum allowed value; Changed to 300000 |
NotificationChannelの作成
ローカルプッシュ通知用のNotificationChannelを作成します。
1 2 3 4 5 6 7 8 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { return } val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channel = NotificationChannel("default", "Default", NotificationManager.IMPORTANCE_DEFAULT) channel.description = "Local Notification Sample" manager.createNotificationChannel(channel) |
これは、エンキューするまでには実行しておいた方が良いでしょう。
これで完了です。
プッシュ通知が表示されました。
さいごに
iOS は割と簡単に実装できるローカルプッシュ通知ですが、Androidでは全く異なる概念であることが分かりました。
バックグラウンドの実行制限も受けるため、ローカルプッシュ実装時にはその辺りの制約にも気をつけたいですね。