WorkManager Periodicity

Pietro Maggi
Jun 14 · 9 min read
Illustration by Virginia Poltrack

Repeating work

API

val work = PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS)
.build()
PeriodicWorkRequest with a charging constraints
val constraints = Constraints.Builder()
.setRequiresCharging(true)
.build()
val work = PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
val workManager = WorkManager.getInstance(context)
workManager.enqueuePeriodicWork(work)

Interval and FlexInterval

val logBuilder = PeriodicWorkRequestBuilder<MyWorker>(
30, TimeUnit.MINUTES,
15, TimeUnit.MINUTES)
PeriodicWorkRequest with a 30' Interval and a 15' flexInterval

Daily Jobs

val currentDate = Calendar.getInstance()
val dueDate = Calendar.getInstance()
// Set Execution around 05:00:00 AM
dueDate.set(Calendar.HOUR_OF_DAY, 5)
dueDate.set(Calendar.MINUTE, 0)
dueDate.set(Calendar.SECOND, 0)
if (dueDate.before(currentDate)) {
dueDate.add(Calendar.HOUR_OF_DAY, 24)
}
val timeDiff = dueDate.timeInMillis — currentDate.timeInMillis
val dailyWorkRequest = OneTimeWorkRequestBuilder<DailyWorker>
.setConstraints(constraints) .setInitialDelay(timeDiff, TimeUnit.MILLISECONDS)
.addTag(TAG_OUTPUT) .build()
WorkManager.getInstance(context).enqueue(dailyWorkRequest)
class DailyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {  override fun doWork(): Result {
val currentDate = Calendar.getInstance()
val dueDate = Calendar.getInstance()
// Set Execution around 05:00:00 AM
dueDate.set(Calendar.HOUR_OF_DAY, 5)
dueDate.set(Calendar.MINUTE, 0)
dueDate.set(Calendar.SECOND, 0)
if (dueDate.before(currentDate)) {
dueDate.add(Calendar.HOUR_OF_DAY, 24)
}
val timeDiff = dueDate.timeInMillis — currentDate.timeInMillis val dailyWorkRequest = OneTimeWorkRequestBuilder<DailyWorker>()
.setInitialDelay(timeDiff, TimeUnit.MILLISECONDS)
.addTag(TAG_OUTPUT)
.build()
WorkManager.getInstance(applicationContext)
.enqueue(dailyWorkRequest)
return Result.success()
}
}

Status for Periodic Work

PeriodiWorkRequest states

Data in and out

val myPeriodicWorkRequest =
PeriodicWorkRequestBuilder<MyPeriodicWorker>(1, TimeUnit.HOURS).build()
WorkManager.getInstance(context).enqueue(myPeriodicWorkRequest)WorkManager.getInstance()
.getWorkInfoByIdLiveData(myPeriodicWorkRequest.id)
.observe(lifecycleOwner, Observer { workInfo ->
if ((workInfo != null) &&
(workInfo.state == WorkInfo.State.ENQUEEDED)) {
val myOutputData = workInfo.outputData.getString(KEY_MY_DATA)
}
})

Unique Work

class MyApplication: Application() {  override fun onCreate() {
super.onCreate()
val myWork = PeriodicWorkRequestBuilder<MyWorker>(
1, TimeUnit.HOURS)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
“MyUniqueWorkName”,
ExistingPeriodicWorkPolicy.KEEP,
myWork)
}
}

KEEP or REPLACE?

Testing Periodic Work

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker.Result
import androidx.work.WorkManager
import androidx.work.testing.TestListenableWorkerBuilder
import com.google.samples.apps.sunflower.workers.SeedDatabaseWorker
import org.hamcrest.CoreMatchers.`is`
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class RefreshMainDataWorkTest {
private lateinit var context: Context @Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
}
@Test
fun testRefreshMainDataWork() {
// Get the ListenableWorker
val worker = TestListenableWorkerBuilder<SeedDatabaseWorker>(context).build()
// Start the work synchronously
val result = worker.startWork().get()
assertThat(result, `is`(Result.success()))
}
}

Conclusion

WorkManager’s Resources

Android Developers

The official Android Developers publication on Medium

Pietro Maggi

Written by

Android Developer Advocate @Google

Android Developers

The official Android Developers publication on Medium