【问题标题】:Are periodic work requests supposed to execute immediately?定期工作请求是否应该立即执行?
【发布时间】:2021-08-31 14:07:40
【问题描述】:

编辑(TL;DR)

我没有意识到周期性工作请求的构造函数不止一个。我的困惑的线索是在接受答案的 cmets 中。

背景

我在安排工作时尝试解决一些特殊情况。其中之一涉及立即工作,然后创建定期工作请求。我在 Android 的 PeriodicWorkRequest documentation:

中找到了这个

这项工作会执行多次,直到它被取消,第一次执行会立即发生,或者一旦满足给定的约束就会发生。

我认为这意味着工作将在创建请求时执行。但是,这不是我的测试实现中发生的事情。 (对于这项工作,不需要 CoroutineWorker 或网络连接限制,但它适用于我的业务需求,因此我正在对其进行测试)

起始工人

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

工人

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

回购

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

视图模型

class NumberViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    init {
        viewModelScope.launch {
            Repository.number.collect {
                _count.postValue(it)
            }
        }
    } 
}

结果

我以 10 作为起始编号启动了一个工人。

日志:

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

设备信息

操作系统版本 28 -- 三星 SM-T390

我的结论

约束 - 不能成为问题。在上述测试期间我有网络连接,这是唯一给定的限制。

电池优化 - 我确信此应用在运行此测试之前已被列入白名单。

总之,PeriodicWorkRequests 不要立即执行工作。 Android 文档应该改为:

这项工作会执行多次,直到被取消,第一阶段立即开始。在满足约束的情况下,第一个工作执行将在第一个弹性间隔内发生。

问题

我的结论看起来合理吗?有什么我没有考虑到的吗?

【问题讨论】:

  • 有趣的是,updateNumber 第一次在上午 9 点 37 分被调用,number 的值是 11,所以 count 已经递增了。这应该意味着 else 分支已被占用,因此工作人员应该已经执行过一次。我不明白为什么它没有出现在日志中。
  • 我很抱歉。我手动编写了这些日志,它们是错误的。感谢那!我将它们更新为它们实际出现的方式。
  • 答案在你的请求构造函数中
  • @Alex Rmcf 的意思是:在请求构造函数中声明的 repeatInterval 也被认为是在初始执行之前必须满足的约束。
  • 英语不是我最好的技能)

标签: android android-workmanager


【解决方案1】:

你想多了。请转储JS:

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

使用adb shell dumpsys jobscheduler

只需检查转储中的 Unsatisfied 约束是什么:

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

还有:

Minimum latency: +1h29m59s687ms

【讨论】:

  • Minimum latency: +45m0s0ms .... Required constraints: TIMING_DELAY CONNECTIVITY [0x90000000] Satisfied constraints: DEVICE_NOT_DOZING [0x2400000] Unsatisfied constraints: TIMING_DELAY [0x80000000] 那么 PeriodicWorkRequest 的限制之一就是延迟已经过去。
  • 您至少需要 45 分钟才能通过。与入队时间相比。除此之外,如果我没记错的话,计时延迟不仅是您延迟了,还包括根据退避策略将工作设置为重试。
【解决方案2】:

据我了解这个构造函数:

          PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, 
    long repeatInterval, TimeUnit repeatIntervalTimeUnit,
 long flexInterval, TimeUnit flexIntervalTimeUnit)

表示您的工作将在flexInterval of repeatInterval 内部执行

【讨论】:

  • 这是任何周期性工作请求的情况。我只是对文档中的措辞感到困惑。
  • 实际上任何周期性工作请求都不是这种情况。如果您使用此构造函数 - PeriodicWorkRequest.Builder(Class extends ListenableWorker> workerClass, long repeatInterval, TimeUnit repeatIntervalTimeUnit) 您的定期工作请求将在您调用它后立即执行 - WorkManager.getInstance(context).enqueueUniquePeriodicWork( Constants.PERIODIC_WORKER_NAME, ExistingPeriodicWorkPolicy .REPLACE, workRequest)
  • 啊,我什至没有意识到您可以在没有弹性间隔的情况下提出定期工作请求。我将对此进行测试。谢谢!
猜你喜欢
  • 1970-01-01
  • 2012-02-23
  • 1970-01-01
  • 2013-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多