Android: 如何检测 workmanager 已经处于排队模式多长时间?

Android: How to detect how long workmanager is already in enqueue mode?

我想检测特定作品已经处于 enqueue 模式多长时间。我需要这些信息,以便通知用户他的状态(例如,当 workmanager 在 enqueue mode 中超过 10 秒时 -> 取消工作 -> 通知用户他需要做 X 以实现 Y)。像这样:

伪代码

workInfo.observe(viewLifecylceOwner) {
    when(it.state) {
       WorkInfo.State.ENQUEUED -> if(state.enqueue.time > 10) cancelWork()
    }
}

我在任何地方都找不到关于此的任何信息。这可能吗?

感谢您的帮助。

我已经设法创建了一个有点健壮的“Workmanager watcher”。我的意图如下:当 Workmanager 未在 7 秒内完成时,告诉用户发生了错误。 Workmanager 本身永远不会被取消,而且我的功能甚至不与 Workmanager 本身交互。这适用于 99% 的所有情况:

工人帮手

object WorkerHelper {
private var timeStamp by Delegates.notNull<Long>()

private var running = false
private var manuallyStopped = false
private var finished = false

open val maxTime: Long = 7000000000L

// Push the current timestamp, set running to true
override fun start() {
    timeStamp = System.nanoTime()
    running = true
    manuallyStopped = false
    finished = false
    Timber.d("Mediator started")
}

// Manually stop the WorkerHelper (e.g when Status is Status.Success)
override fun stop() {
    if (!running) return else {
        running = false
        manuallyStopped = true
        finished = true
        Timber.d("Mediator stopped")
    }
}

override fun observeMaxTimeReachedAndCancel(): Flow<Boolean> = flow {
    try {
        coroutineScope {
            // Check if maxTime is not passed with => (System.nanoTime() - timeStamp) <= maxTime
            while (running && !finished && !manuallyStopped && (System.nanoTime() - timeStamp) <= maxTime) {
                emit(false)
            }
            // This will be executed only when the Worker is running longer than maxTime
            if (!manuallyStopped || !finished) {
                emit(true)
                running = false
                finished = true
                this@coroutineScope.cancel()
            } else if (finished) {
                this@coroutineScope.cancel()
            }
        }
    } catch (e: CancellationException) {
    }
}.flowOn(Dispatchers.IO)

然后在我的 Workmanager.enqueueWork 函数中:

fun startDownloadDocumentWork() {
    WorkManager.getInstance(context)
        .enqueueUniqueWork("Download Document List", ExistingWorkPolicy.REPLACE, downloadDocumentListWork)
    pushNotification()
}

private fun pushNotification() {
    WorkerHelper.start()
}

最后在我的 ViewModel 中

   private fun observeDocumentList() = viewModelScope.launch {
        observerWorkerState(documentListWorkInfo).collect {
            when(it) {
                is Status.Loading -> {
                    _documentDataState.postValue(Status.loading())

                    // Launch another Coroutine, otherwise current viewmodelscrope will be blocked
                    CoroutineScope(Dispatchers.IO).launch {
                        WorkerHelper.observeMaxTimeReached().collect { lostConnection ->
                            if (lostConnection) {
                                _documentDataState.postValue(Status.failed("Internet verbindung nicht da"))
                            }
                        }
                    }
                }
                is Status.Success -> {
                    WorkerHelper.finishWorkManually()
                    _documentDataState.postValue(Status.success(getDocumentList()))
                }
                is Status.Failure -> {
                    WorkerHelper.finishWorkManually()
                    _documentDataState.postValue(Status.failed(it.message.toString()))
                }
            }
        }
    }

我还创建了一个函数,可以将 workmanager 的状态转换为自定义状态 class:

状态

sealed class Status<out T> {
    data class Success<out T>(val data: T) : Status<T>()
    class Loading<T> : Status<T>()
    data class Failure<out T>(val message: String?) : Status<T>()

    companion object {
        fun <T> success(data: T) = Success<T>(data)
        fun <T> loading() = Loading<T>()
        fun <T> failed(message: String?) = Failure<T>(message)
    }
}

函数

suspend inline fun observerWorkerState(workInfoFlow: Flow<WorkInfo>): Flow<Status<Unit>> = flow {
    workInfoFlow.collect {
        when (it.state) {
            WorkInfo.State.ENQUEUED -> emit(Status.loading<Unit>())

            WorkInfo.State.RUNNING -> emit(Status.loading<Unit>())

            WorkInfo.State.SUCCEEDED -> emit(Status.success(Unit))

            WorkInfo.State.BLOCKED -> emit(Status.failed<Unit>("Workmanager blocked"))

            WorkInfo.State.FAILED -> emit(Status.failed<Unit>("Workmanager failed"))

            WorkInfo.State.CANCELLED -> emit(Status.failed<Unit>("Workmanager cancelled"))
        }
    }
}