【问题标题】:how to call a function every several millisecond using coroutine in kotlin如何在kotlin中使用协程每隔几毫秒调用一次函数
【发布时间】:2020-01-13 21:53:54
【问题描述】:

我想每 3 秒收到一次网络请求,并在某些情况下停止它。我正在使用Coroutine 进行网络请求。我使用了postDelayed() 方法并且它工作正常。但是我想在上一个请求的上一个响应完成后发出下一个请求。我使用了Coroutinedelay 方法,但是 UI 已冻结,我的应用程序仍处于无限循环中。如何使用postDelayed 还是协程?我在此存储库中创建网络请求:

     class ProcessRepository  @Inject constructor(private val apiService: ApiService) {
    val _networkState = MutableLiveData<NetworkState>()
    val _networkState_first = MutableLiveData<NetworkState>()

    val completableJob = Job()
    private val coroutineScope = CoroutineScope(Dispatchers.IO + completableJob)

    private val brokerProcessResponse = MutableLiveData<BrokerProcessResponse>()
 fun repeatRequest(processId:String):MutableLiveData<BrokerProcessResponse>{
        var networkState = NetworkState(Status.LOADING, userMessage)
        _networkState.postValue(networkState)
        coroutineScope.launch {
            val request = apiService.repeatRequest(processId, token)
            withContext(Dispatchers.Main) {
                try {
                    val response = request.await()
                    if (response.isSuccessful) {
                        brokerProcessResponse.postValue(response.body())
                        var networkState = NetworkState(Status.SUCCESS, userMessage)
                        _networkState.postValue(networkState)
                    } else {
                        var networkState = NetworkState(Status.ERROR, userMessage)
                        _networkState.postValue(networkState)
                    }
                } catch (e: IOException) {
                    var networkState = NetworkState(Status.ERROR, userMessage)
                    _networkState.postValue(networkState)
                } catch (e: Throwable) {
                    var networkState = NetworkState(Status.ERROR, userMessage)
                    _networkState.postValue(networkState)
                }
            }
            delay(3000) // I only just add this line for try solution using coroutine 
        }

        return brokerProcessResponse
    }

这是我片段中的代码:

     private fun repeatRequest(){
        viewModel.repeatRequest(processId).observe(this, Observer {

                if(it!=null){
                    process=it.process
                    if(it.process.state== FINDING_BROKER || it.process.state==NONE){
                        inProgress(true)
                    }else{
                        inProgress(false)
                 }
                    setState(it!!.process.state!!,it.process)
                }

        })
    }
 private fun pullRequest(){
        while (isPullRequest){
            repeatRequest()
        }

    }

我使用postDelayed的解决方案:

     private fun init() {
        mHandler = Handler()
}


private fun pullRequest() {

        mRunnable = Runnable {

            repeatRequest()
            Log.e(TAG, "in run")
            mHandler.postDelayed(
                mRunnable,
                3000
            )
        }

        mHandler.postDelayed(
            mRunnable,
            3000
        )
    }
 private fun cancelRequest() {
    viewModel.cancelRequest(processId).observe(this, Observer {
        if (it != null) {
            processShareViewModel.setProcess(it.process)
            mHandler.removeCallbacks(mRunnable)
            mHandler.removeCallbacksAndMessages(null)

            isPullRequest = false
            if (findNavController().currentDestination?.id == R.id.requstBrokerFragment) {
                val nextAction = RequstBrokerFragmentDirections.actionRequstBrokerFragmentToHomeFragment()
                // nextAction.keyprocess = it.process
                findNavController().navigate(nextAction)
            }

        }
        Log.e(TAG, "response request borker: " + it.toString())
    })
}


 override fun onDestroy() {
        super.onDestroy()
        // Stop the periodic task
        isPullRequest = false
        mHandler.removeCallbacks(mRunnable)
        mHandler.removeCallbacksAndMessages(null)
    }

【问题讨论】:

    标签: android kotlin coroutine


    【解决方案1】:

    对于 Coroutine 的新手

    在 Build.gradle 中添加协程

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
    

    添加重复作业

        /**
         * start Job
         * val job = startRepeatingJob()
         * cancels the job and waits for its completion
         * job.cancelAndJoin()
         * Params
         * timeInterval: time milliSeconds 
         */
        private fun startRepeatingJob(timeInterval: Long): Job {
            return CoroutineScope(Dispatchers.Default).launch {
                while (NonCancellable.isActive) {
                    // add your task here
                    doSomething()
                    delay(timeInterval)
                }
            }
        }
    

    开始:

      Job myJob = startRepeatingJob(1000L)
    

    停止:

        myJob .cancel()
    

    【讨论】:

      【解决方案2】:

      看看这个question,它有一个自定义的loop,它的用法可以是这样的

      
          // if loop block takes less than 3 seconds, then this loop will iterate after every 3 seconds
          // if loop block takes more than 3 seconds, then this loop will iterate after block is finished
          // loop iterate after block duration or provided minimum interval whichever is maximum
          loop(3.seconds) {
           // make request
           // process response
      
           stopIf(condition)
          }
      
      

      【讨论】:

        【解决方案3】:

        您的 UI 冻结了,因为您的 while 循环正在不间断地运行:

        while (isPullRequest){
             repeatRequest()
        }
        

        您正在repeatRequest 中异步启动协程并在那里调用delay。这不会暂停pullRequest 函数。

        您应该在协程中运行循环(repeatRequest 函数)。然后你可以把这个协程的job对象给调用者,并在你想停止循环的时候调用cancel

        fun repeatRequest(): Job {
            return coroutineScope.launch {  
                while(isActive) {
                    //do your request
                    delay(3000)
                }
            }
        }
        
        //start the loop
        val repeatJob = repeatRequest()
        
        //Cancel the loop
        repeatJob.cancel()
        

        【讨论】:

        • 如何定义coroutineScope变量?
        • 和你一样CoroutineScope(Dispatchers.IO + completableJob)
        • 你的解决方案是否满足这个条件? ''我想在上一个请求的响应完成后发出下一个请求。''
        • 是的,如果循环中的请求在结果到达之前一直阻塞。如果循环中的请求是异步的,那么就不是。
        • 取消后如何重新开始我的工作?当我取消repeatRequest() 作业时,它没有取消,我的循环继续,我必须取消completableJob,当调用completableJob.start() 时,我的作业没有重新启动。
        猜你喜欢
        • 1970-01-01
        • 2020-06-25
        • 2019-06-17
        • 1970-01-01
        • 1970-01-01
        • 2018-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多