Android Shorts 🩳: WorkManager — Observing a WorkRequest (level paranoid)

Share
Android Shorts 🩳: WorkManager — Observing a WorkRequest (level paranoid)

Start a work request

(see how-to-hilt+workmanager in the previous episode)

A method in a ViewModel will do:

fun startTodoWork() {
    viewModelScope.launch {
        val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>()
            .addTag("myTag")
            .setConstraints(
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .build()
            )
            .build()


        workManager.enqueueUniqueWork(
            "uniqueWorkName",
            ExistingWorkPolicy.REPLACE,
            oneTimeWorkRequest
        )
    }
}

ViewModel.kt

Observing the state

WorkManager.getWorkInfoByIdLiveData returns LiveData , which isn’t really useful at the moment.

Turn that into a flow by adding the livedata-ktx dependency.

dependencies {
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
}

build.gradle.kts

Print out the state of the work request while you are at it.

workManager.enqueueUniqueWork(
                ....
)
            
workManager.getWorkInfoByIdLiveData(oneTimeWorkRequest.id).asFlow().collect {
     Log.d("TAG", it?.state?.name!!)
}

ViewModel.kt

Job done.

Or maybe..

The collection will be alive as long as the ViewModel is alive, patiently waiting for updates for a job that might be finished already.

Imagine starting a lot of work requests here. Clean up is in order.

supervisorScope {
    launch {
        Log.d("TAG", "WorkRequest - state observation - start")
        workManager.getWorkInfoByIdLiveData(oneTimeWorkRequest.id).asFlow().collect {
            Log.d("TAG", "WorkRequest - ${it?.state?.name!!}")
            if (it.state.isFinished) {
                Log.d("TAG", "WorkRequest - Work finished")
                cancel() // cancel the supervisorScope, it is now redundant
            }
        }
    }
}.invokeOnCompletion {
    Log.d("TAG", "WorkRequest - state observation - end")
}

ViewModel.kt

supervisorScope?

Calling cancel() on the supervisorScope will cancel its job and all its children. Just about what is needed.

Log statements confirm this too:

WorkRequest - state observation - start
WorkRequest - ENQUEUED
WorkRequest - RUNNING
WorkRequest - SUCCEEDED
WorkRequest - Work finished
WorkRequest - state observation - end

logs

The full method:

fun startTodoWork() {
    viewModelScope.launch {
        val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>()
            .addTag("myTag")
            .setConstraints(
                Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .build()
            )
            .build()


        workManager.enqueueUniqueWork(
            "uniqueWorkName",
            ExistingWorkPolicy.REPLACE,
            oneTimeWorkRequest
        )


        supervisorScope {
            launch {
                Log.d("TAG", "WorkRequest - state observation - start")
                workManager.getWorkInfoByIdLiveData(oneTimeWorkRequest.id).asFlow().collect {
                    Log.d("TAG", "WorkRequest - ${it?.state?.name!!}")
                    if (it.state.isFinished) {
                        Log.d("TAG", "WorkRequest - Work finished")
                        cancel() // cancel the supervisorScope, it is now redundant
                    }
                }
            }
        }.invokeOnCompletion {
            Log.d("TAG", "WorkRequest - state observation - end")
        }
    }
}

full.kt