I didn't realize there was more than one constructor for periodic work requests. The clue to my confusion was in the comments of the accepted answer.
I have a few special cases I am trying to solve for while scheduling work. One of them involves doing work immediately and then creating a periodic work request. I found this in the Android's PeriodicWorkRequest documentation:
This work executes multiple times until it is cancelled, with the first execution happening immediately or as soon as the given Constraints are met.
I figured that this meant work would execute upon creating a request. However, this was not what happened in my test implementation. (For this work there is no need for a CoroutineWorker or network connection constraints but its applicable to my business need so I am testing it)
Starting Worker
object WorkerManager {
private val TAG = "WORKER_MANAGER_TEST"
fun buildWorkRequest(
startingNumber: Int,
context: Context
) {
val constraints =
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val workRequest = PeriodicWorkRequest.Builder(
PeriodicWorker::class.java,
1,
TimeUnit.HOURS,
15,
TimeUnit.MINUTES
)
.setInputData(
workDataOf(Constants.INPUT_DATA_NUMBER to startingNumber)
)
.addTag(Constants.PERIODIC_WORKER_TAG)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
Constants.PERIODIC_WORKER_NAME,
ExistingPeriodicWorkPolicy.REPLACE,
workRequest
)
Log.d(TAG, "Worker started. Starting number: $startingNumber")
}
}
Worker:
class PeriodicWorker(context: Context, workerParams: WorkerParameters): CoroutineWorker(context,
workerParams
) {
companion object {
var isInit = false
var count: Int = 1
}
override suspend fun doWork(): Result = try {
if (!isInit) {
count = inputData.getInt(Constants.INPUT_DATA_NUMBER, Constants.DEFAULT_DATA_NUMBER)
isInit = true
} else {
count += 1
}
Repository.updateNumber(count)
Result.success()
} catch (exception: Exception) {
Result.failure()
}
}
Repo:
object Repository {
private val TAG = "REPOSITORY_TAG"
private val _number = MutableStateFlow(0)
val number: StateFlow<Int> = _number
suspend fun updateNumber(number: Int) {
Log.d(TAG, "Number updated to: $number")
_number.emit(number)
}
}
ViewModel:
class NumberViewModel : ViewModel() {
private val _count = MutableLiveData(0)
val count: LiveData<Int> = _count
init {
viewModelScope.launch {
Repository.number.collect {
_count.postValue(it)
}
}
}
}
I started a worker with 10 as the starting number.
Logs:
8:45am - Worker started. Starting number: 10
9:37am - Number updated to: 10 // work executed
10:37am - Number updated to: 11 // work executed
11:37am - Number updated to: 12 // work executed
OS Version 28 -- Samsung SM-T390
Constraints - Cannot be an issue. I had network connection during the above test and that is the only given constraint.
Battery Optimizations - I am sure that this app was white listed prior to running this test.
So in conclusion it seems that PeriodicWorkRequests DO NOT perform immediate work. The Android documentation should instead say:
This work executes multiple times until it is cancelled, with the first period beginning immediately. The first work execution then happens within the first flex interval given the constraints are met.
Does my conclusion seem reasonable? Is there something I haven't considered?
As I understand this constructor:
PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass,
long repeatInterval, TimeUnit repeatIntervalTimeUnit,
long flexInterval, TimeUnit flexIntervalTimeUnit)
means that your work will be executed inside flexInterval
of repeatInterval