〈kotlin〉 WorkManagerで指定時間後に通知を送信

Android studioでkotlinを使ってAndroidアプリ作成の勉強中。

ボタンを押したら指定時間経ったあと通知を送信する機能をWorkManagerを使って実装。

WorkManager

非同期で行う1回限りのタスクや定期的に実行したいタスクをバックグラウンドで処理するAPI
アプリ アーキテクチャ: データレイヤ - WorkManager でタスクのスケジュールを設定する - デベロッパー向け Android  |  Android デベロッパー  |  Android Developers

サンプルアプリ

JetpackComposeで作成。
TextFieldに入力された数値を指定時間として、ボタンを押すと指定時間経過した後で「◯分経過しました」と通知が来る。

Build.gradle

依存関係を追加する。

implementation "androidx.work:work-runtime-ktx:$work_version"

実行したい作業を定義

Workerクラスを使用。
doWork()メソッドに書かれた処理が、WorkManagerから提供されるバックグラウンドスレッドで非同期的に実行される。
doWork()メソッドは作業が成功したか失敗したか、再試行するかを示すResult型を返す。

const val MINUTE = "minute"

class NotificationWorker(context: Context, params: WorkerParameters):Worker(context, params) {

    private val notificationId = 1

    override fun doWork(): Result {
        return try {
            //WorkManagerに入力されたデータを受け取る
            val inputBreakTime = inputData.getLong(MINUTE, 0)

            val intent = Intent(applicationContext, MainActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
            }

            val pendingIntent = PendingIntent
                .getActivity(applicationContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)

            val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle("Notification")
                .setContentText(applicationContext.getString(R.string.notification_description, inputBreakTime.toInt()))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)

            with(NotificationManagerCompat.from(applicationContext)){
                notify(notificationId, builder.build())
            }

            Result.success()
        }catch (e:Exception){
            Log.d("Notification", "failure : $e")
            Result.failure()
        }
    }
}

WorkRequestを作成する

Workerで定義した処理が実行されるようにスケジュール設定をする。

OneTimeWorkRequest : 1回だけ実行する
PeriodicWorkRequest : 定期的に繰り返す
今回は1回だけ通知を送るためOneTimeWorkRequestを作成する。

class MainViewModel(application: Application) :ViewModel() {

    private val workManager = WorkManager.getInstance(application)

    fun createBreakTimeNotification(minute:Long){
        val inputData = workDataOf(
            MINUTE to minute,
        )

        val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
            .setInitialDelay(minute, TimeUnit.MINUTE)  //指定した時間遅延して実行する指定
            .setInputData(inputData)  //Workerに渡すデータを入力する
            .build()
       
        //WorkManagerにリクエストを送信
        workManager.enqueue(workRequest)
    }

    class MainViewModelFactory(private val application: Application) : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return MainViewModel(application) as T
            }
            throw IllegalArgumentException("Unknown ViewModel class")
        }
    }
}

データの入出力は Key-ValueペアのData オブジェクトで行う。
詳細は以下。
WorkManager に関する高度なトピック  |  Android デベロッパー  |  Android Developers
タスクについて優先度や実行時の条件(電池残量、ネットワーク状態など)も追加できる。
WorkRequest の定義  |  Android デベロッパー  |  Android Developers

ViewModelProvider.Factoryでハマった。

WorkManagerのリクエストをViewModel内で作成するために、ViewModelのコンストラクタにapplicationを追加し、Factoryを作成した。でもコンパイルエラー…。

inheritance from an interface with '@jvmdefault' members is only allowed with -xjvm-default option

gradleに以下を追加したらコンパイルできるようになったが、よく分かってないのでまた勉強したい。

android {
  kotlinOptions {
    freeCompilerArgs +=  "-Xjvm-default=all"
  }
}

Implementing ViewModelProvider.Factory fails with Inheritance from an interface with '@JvmDefault' after adding Android Jetpack Compose navigation lib - Stack Overflow
Google Issue Tracker