我刚刚创建了一个应用程序,我的函数 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) {
}
}
使用协程每秒运行一个函数:
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()
仅 starts 网络请求并且不等待其完成,这可能不是一个好主意。该函数将在完成后一秒被调用,但由于网络请求是异步完成的,因此可能会被调度得太多。要解决此问题,您应该找到一种方法来挂起协程,直到网络请求完成。
这可以通过使用 blocking api 来完成,然后将
Dispatchers.IO
传递给 launch
函数以确保它在后台线程上完成。
或者,您可以使用 suspendCoroutine 将基于回调的 api 转换为挂起的 api。
在具有 Android 生命周期的组件内,您可以使用以下代码来自动重复 ui 更新:
fun startUpdates() {
val lifecycle = this // in Activity
val lifecycle = viewLifecycleOwner // in Fragment
lifecycle.lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
// this block is automatically executed when moving into
// the started state, and cancelled when stopping.
while (true) {
getData() // the function to repeat
delay(1000)
}
}
}
}
此代码需要当前的
androidx.lifecycle:lifecycle-runtime-ktx
依赖项。
上面关于 getData() 内的异步、阻塞或挂起代码的评论仍然适用。
不建议每秒都访问服务器。如果您需要连续获取数据,请尝试套接字。因为有时您的服务器需要花费几秒钟的时间来响应您的请求。那么您的所有请求都将排在队列中..如果您仍然需要尝试此操作。
fun repeatFun(): Job {
return coroutineScope.launch {
while(isActive) {
//do your network request here
delay(1000)
}
}
}
//start the loop
val repeatFun = repeatFun()
//Cancel the loop
repeatFun.cancel()
在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()
我最终用扩展函数做了这样的事情:
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()
我在 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 -> [email protected] = 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>()
}
我的解决方案是一次运行代码来检查是否启用了某些功能(例如蓝牙),如果没有启用,则定期自动检查。功能是:
fun CoroutineScope.launchPeriodic(repeatMillis: Long, action: () -> Unit) : Job {
return launch {
while (!enabled) {
action()
delay(repeatMillis)
}
}
}
为了启动这个周期性函数,代码在这里(其中动作每 2 秒运行一次)。当启用该功能并且运行一些代码(放置在注释文本的位置)时,它会自动结束:
CoroutineScope(Dispatchers.IO).launchPeriodic(TimeUnit.SECONDS.toMillis(2)) {
if(checkIfSomethingIsEnabledCodeIsHere) {
enabled = true
//some code here to run when it is enabled
}
}