Working with WorkManager
The Architecture Component for deferrable, asynchronous background work ⚙️
By Nick Rout
Android Engineer at Over
Twitter: @ricknout
What is it ❓
What is it ❓
When should it be used 🤔
When should it be used 🤔
Rugby Ranker 🏉
Basic setup 🏁
implementation “android.arch.work:work-runtime-ktx:$work-version”
implementation “android.arch.work:work-testing:$work-version”
Your first Worker 🥇
Your first Worker 🥇
class RankingsWorker(
context: Context,
workerParams: WorkerParameters
private val rankingsRepository: RankingsRepository
) : Worker(context, workerParams) {
override fun doWork() = fetchAndCacheLatestWorldRugbyRankings()
private fun fetchAndCacheLatestWorldRugbyRankings(): Result {
val success = rankingsRepository
.fetchAndCacheLatestWorldRugbyRankingsSync()
return if (success) Result.success() else Result.retry()
}
}
Your first Worker 🥇
class RankingsWorker(
context: Context,
workerParams: WorkerParameters
private val rankingsRepository: RankingsRepository
) : Worker(context, workerParams) {
override fun doWork() = fetchAndCacheLatestWorldRugbyRankings()
private fun fetchAndCacheLatestWorldRugbyRankings(): Result {
val success = rankingsRepository
.fetchAndCacheLatestWorldRugbyRankingsSync()
return if (success) Result.success() else Result.retry()
}
}
Your first Worker 🥇
class RankingsWorker(
context: Context,
workerParams: WorkerParameters
private val rankingsRepository: RankingsRepository
) : Worker(context, workerParams) {
override fun doWork() = fetchAndCacheLatestWorldRugbyRankings()
private fun fetchAndCacheLatestWorldRugbyRankings(): Result {
val success = rankingsRepository
.fetchAndCacheLatestWorldRugbyRankings()
return if (success) Result.success() else Result.retry()
}
}
Define some constraints 📱
Define some constraints 📱
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
Create a WorkRequest (one-time) 1️⃣
Create a WorkRequest (periodic) ⏲️
Create a WorkRequest (periodic) ⏲️
val rankingsWorkRequest =
PeriodicWorkRequestBuilder<RankingsWorker>(1L, TimeUnit.Days)
.setConstraints(constraints)
.build()
// Use OneTimeWorkRequestBuilder for one-time requests
Enqueue your work! 🎉
Enqueue your work! 🎉
val workManager = WorkManager.getInstance()
val uniqueWorkName = “rankings_worker”
workManager.enqueueUniquePeriodicWork(
uniqueWorkName, ExistingPeriodicWorkPolicy.KEEP, rankingsWorkRequest
)
// Use enqueueUniqueWork for one-time work
Monitor Worker state 👂
Monitor Worker state 👂
val workManager = WorkManager.getInstance()
val uniqueWorkName = “rankings_worker”
val rankingsWorkInfoLiveData =
workManager.getWorkInfosForUniqueWorkLiveData(uniqueWorkName)
// Use getWorkInfosByTagLiveData for tags
rankingsWorkInfoLiveData.observe(lifecycleOwner, Observer { workInfos ->
val workInfo = workInfos?.firstOrNull() ?: return@Observer
when (workInfo.state) {
State.RUNNING -> // Show Snackbar
else -> // Hide Snackbar
})
Monitor Worker state 👂
val workManager = WorkManager.getInstance()
val uniqueWorkName = “rankings_worker”
val rankingsWorkInfoLiveData =
workManager.getWorkInfosForUniqueWorkLiveData(uniqueWorkName)
// Use getWorkInfosByTagLiveData for tags
rankingsWorkInfoLiveData.observe(lifecycleOwner, Observer { workInfos ->
val workInfo = workInfos?.firstOrNull() ?: return@Observer
when (workInfo.state) {
State.RUNNING -> // Show Snackbar
else -> // Hide Snackbar
})
Retrieve output data 🎁
Cancel running work ❌
Gotcha: Retrofit network calls 📡
Dependency injection with Dagger 💉
Dependency injection with Dagger 💉
Let’s open up the article...
Incorporating Kotlin Coroutines ↪️
Incorporating Kotlin Coroutines ↪️
// Before
class RankingsWorker(
context: Context,
workerParams: WorkerParameters
private val rankingsRepository: RankingsRepository
) : Worker(context, workerParams) {
override fun doWork() = runBlocking {
fetchAndCacheLatestWorldRugbyRankings()
}
private suspend fun fetchAndCacheLatestWorldRugbyRankings(): Result {
val success = rankingsRepository
.fetchAndCacheLatestWorldRugbyRankings() // Now suspending
return if (success) Result.success() else Result.retry()
}
}
Incorporating Kotlin Coroutines ↪️
// After
class RankingsWorker(
context: Context,
workerParams: WorkerParameters
private val rankingsRepository: RankingsRepository
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork() = fetchAndCacheLatestWorldRugbyRankings()
private suspend fun fetchAndCacheLatestWorldRugbyRankings(): Result {
val success = rankingsRepository
.fetchAndCacheLatestWorldRugbyRankings() // Now suspending
return if (success) Result.success() else Result.retry()
}
}
More resources 📚
Any questions? 🤷♂️