Are periodic work requests supposed to execute immediately?

Viewed 595

EDIT (TL;DR)

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.

Background

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)
            }
        }
    } 
}

Results

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

Device Info

OS Version 28 -- Samsung SM-T390

My Conclusion

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.

Question

Does my conclusion seem reasonable? Is there something I haven't considered?

2 Answers

You are overthinking it. Please dump the JS:

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging

Use adb shell dumpsys jobscheduler

And just check what are the Unsatisfied constraints in the dump:

  Required constraints: TIMING_DELAY CONNECTIVITY [0x90000000]
  Satisfied constraints: DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED WITHIN_QUOTA [0x3400000]
  Unsatisfied constraints: TIMING_DELAY CONNECTIVITY [0x90000000]

Also:

Minimum latency: +1h29m59s687ms

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

Related