【问题标题】:Polling using kotlin coroutine with custom scope and view lifecycle使用具有自定义范围和视图生命周期的 kotlin 协程进行轮询
【发布时间】:2019-12-11 00:20:47
【问题描述】:

我只是从 Kotlin 协程开始。我正在尝试使用协程轮询服务器,并希望在 ActivityFragment 暂停时停止轮询并相应地恢复轮询。所以我的pollScope 的生命周期比ViewModel.viewModelScope 提供的生命周期短。我对目前的实现并不完全满意,有几个问题:

  1. 这是创建pollScope 的正确方法吗?我希望它在viewModelScope 也被取消时取消,这就是我指定父作业的原因。
  2. 如果我使用coroutineContext.cancel() 取消pollJobs,为什么协程不在onResume() 中启动?如果我保留一份工作清单并取消它们,它们就会开始正常工作。
  3. 这是整体正确的方法吗?有没有更好的方法?
    import androidx.lifecycle.LifecycleOwner
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import com.spruce.messenger.utils.FullLifecycleObserverAdapter
    import kotlinx.coroutines.*
    import java.io.IOException
    import java.util.concurrent.CopyOnWriteArrayList
    import kotlin.coroutines.CoroutineContext


    suspend fun poll(initialDelay: Long = 5000,
                     maxDelay: Long = 30000,
                     factor: Double = 2.0,
                     block: suspend () -> Unit) {

        var currentDelay = initialDelay
        while (true) {
            try {
                try {
                    block()
                    currentDelay = initialDelay
                } catch (e: IOException) {
                    currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
                }
                delay(currentDelay)
                yield()
            } catch (e: CancellationException) {
                break
            }
        }
    }


    class MyDataModel : ViewModel() {
        val pollScope = CloseableCoroutineScope(SupervisorJob(parent = viewModelScope.coroutineContext[Job]) + Dispatchers.Main)

        private val pollJobs = CopyOnWriteArrayList<Job>()

        inner class CloseableCoroutineScope(context: CoroutineContext) : FullLifecycleObserverAdapter(), CoroutineScope {
            override val coroutineContext: CoroutineContext = context

            override fun onPause(owner: LifecycleOwner) {
                super.onPause(owner)
                // coroutineContext.cancel() cancels it but then coroutine doesn't start again in onResume() thats why cancelling jobs instead
                pollJobs.forEach { it.cancel() }
            }

            override fun onResume(owner: LifecycleOwner) {
                super.onResume(owner)
                refresh()
            }
        }

        fun refresh() {
            if (pollJobs.count { it.isActive } == 0) {
                startPoll()
            }
        }

        private fun startPoll() = pollScope.launch {
            try {
                poll {
                    //fetch data from server
                }
            } catch (e: Exception) {
                //ignore
            }
        }.also {
            track(it)
        }

        private fun track(job: Job) {
            pollJobs.add(job)
            job.invokeOnCompletion {
                pollJobs.remove(job)
            }
        }
    }

然后在我的片段中添加 pollScope 作为生命周期观察者viewLifecycleOwner.lifecycle.addObserver(viewModel.pollScope)

【问题讨论】:

  • 我不认为 Kotlin 协程是你要找的
  • 我认为 Kotlin 协程是他绝对可以使用的东西。

标签: android kotlin coroutine kotlin-coroutines


【解决方案1】:
  1. 我觉得你的pollScope 很好。

  2. 当您取消Job 时,它会取消该Job 的所有协程。

  3. 我会使用ViewModelCoroutineScope,然后从中进行轮询。确保管理我的 Job 并在 VM 死机时取消我的协程。

class MyViewModel() : ViewModel(),
CoroutineScope by CoroutineScope(Dispatchers.Main + SupervisorJob()) {
    // ...
    override fun onCleared() {
        cancel()
        super.onCleared()
    }
}

【讨论】:

    猜你喜欢
    • 2019-05-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多