【问题标题】:how to use Coroutine in kotlin to call a function every second如何在kotlin中使用协程每秒调用一个函数
【发布时间】:2020-06-25 14:01:18
【问题描述】:

我刚刚创建了一个应用程序,其中我的函数 getdata() 每秒调用一次以从服务器获取新数据,并且 updateui() 函数将更新 UI 中的视图我在我的应用程序中不使用任何异步任务或协程我想这样做请告诉我该怎么做。

这是我的代码...

private fun getdata(){
        try {
            val api = RetroClient.getApiService()
            call = api.myJSON
            call!!.enqueue(object : Callback<ProductResponse> {
                override fun onResponse(
                    call: Call<ProductResponse>,
                    response: Response<ProductResponse>
                ) {
                    if (response.isSuccessful) {
                        productList = response.body()!!.data
                        for (list in productList) {
                            if (list.BB.equals("AAA")) {
                                aProductList.add(list)
                            }
                        }
                        if (recyclerView.adapter != null) {
                            eAdapter!!.updatedata(aProductList)
                        }
                        updateui()
                    }
                }

                override fun onFailure(call: Call<ProductResponse>, t: Throwable) {
                    println("error")
                }
            })
        } catch (ex: Exception) {
        } catch (ex: OutOfMemoryError) {
        }
Handler().postDelayed({
            getdata()
        }, 1000)
}


private fun updateui() {
        try {
            //some code to handel ui
 } catch (e: NumberFormatException) {

        } catch (e: ArithmeticException) {

        } catch (e: NullPointerException) {

        } catch (e: Exception) {

        }
    }

【问题讨论】:

  • 你可以试试TimerTask
  • WorkManager 是正确的方法。

标签: android kotlin coroutine


【解决方案1】:

不建议每秒都访问服务器。如果您需要连续获取数据,请尝试使用套接字。因为有时您的服务器需要超过几秒钟才能响应您的请求。那么您的所有请求都将排在队列中..如果您仍需要尝试此操作。

fun repeatFun(): Job {
    return coroutineScope.launch {  
        while(isActive) {
            //do your network request here
            delay(1000)
        }
    }
}

//start the loop
val repeatFun = repeatRequest()

//Cancel the loop
repeatFun.cancel()

【讨论】:

【解决方案2】:

使用协程每秒运行一个函数:

val scope = MainScope() // could also use an other scope such as viewModelScope if available
var job: Job? = null

fun startUpdates() {
    stopUpdates()
    job = scope.launch {
        while(true) {
            getData() // the function that should be ran every second
            delay(1000)
        }
    }
}

fun stopUpdates() {
    job?.cancel()
    job = null
}

但是,如果getData()开始一个网络请求并且不等待其完成,这可能不是一个好主意。该函数将在完成后一秒钟被调用,但由于网络请求是异步完成的,它可能会被调度太多。
例如,如果网络请求需要 5 秒,那么在第一次完成之前它会再启动 4 次!

要解决这个问题,您应该找到一种方法来暂停协程,直到网络请求完成。
这可以通过使用 blocking api 来完成,然后将 Dispatchers.IO 传递给 launch 函数以确保它在后台线程上完成。

或者,您可以使用suspendCoroutine 将基于回调的 api 转换为暂停的。


但是,总而言之,要改变这一切可能太麻烦了。简单地说:

Handler().postDelayed({
    getData()
}, 1000) 

在您的 updateui 函数的末尾并完成它。
这样它会在完成后一秒安排。

【讨论】:

    【解决方案3】:

    我最终用一个扩展函数做了这样的事情:

    fun CoroutineScope.launchPeriodicAsync(repeatMillis: Long, action: () -> Unit) = this.async {
      while (isActive) {
        action()
        delay(repeatMillis)
      }
    }
    

    然后这样称呼它:

    val fetchDatesTimer = CoroutineScope(Dispatchers.IO)
      .launchPeriodicAsync(TimeUnit.MINUTES.toMillis(1)) {
        viewModel.fetchDeliveryDates()
      }
    

    然后像这样取消它:

    fetchDatesTimer.cancel()
    

    【讨论】:

      【解决方案4】:

      对于 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()
      

      【讨论】:

      • 给定的解决方案只能在屏幕启动时使用。无法在按钮单击时运行
      • NonCancellable.isActive 现在是一个内部 API,但您仍然可以与注释一起使用。
      【解决方案5】:

      我在 MainViewModel 中的 Kotlin 解决方案

      fun apiCall() {
             viewModelScope.launch(Dispatchers.IO) {
               while(isActive) {
                  when(val response = repository.getServerData()) {
                      is NetworkState.Success -> {
                          getAllData.postValue(response.data)
                      }
                      is NetworkState.Error -> this@MainViewModel.isActive = false
                  }
      
                  delay(1000)
              }
          }
      }
      
      
      sealed class NetworkState<out R> {
          data class Success<out T>(val data: T): NetworkState<T>()
          data class Error(val exception: String): NetworkState<Nothing>()
          object Loading: NetworkState<Nothing>()
      }
      

      【讨论】:

        猜你喜欢
        • 2020-01-13
        • 2019-08-29
        • 2021-01-26
        • 2021-01-14
        • 2014-12-24
        • 2015-06-26
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        相关资源
        最近更新 更多