首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Kotlin Android -向主线程发送的正确方法

Kotlin Android -向主线程发送的正确方法
EN

Stack Overflow用户
提问于 2022-01-17 04:53:46
回答 3查看 2.7K关注 0票数 0

我正在使用OkHttp库从我的androidx.lifecycle.ViewModel中下载一些数据,然后我想更新我的LiveData。从后台线程执行此操作似乎会引发异常,如下所示:

代码语言:javascript
复制
2022-01-17 15:47:59.589 7354-7396/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.example.myapplication, PID: 7354
    java.lang.IllegalStateException: Cannot invoke setValue on a background thread
        at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
        at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
        at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:86)
        at com.example.myapplication.MainActivityViewModel$getOneMoreCat$1.invoke(MainActivityViewModel.kt:39)
        at com.example.myapplication.singleton.CommunicationManager$sendRequest$1.onResponse(CommunicationManager.kt:24)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

现在,我发现了两种从ViewModel向主线程分派的不同方法(根据AAC准则,它不引用上下文),请参见以下内容:

代码语言:javascript
复制
            GlobalScope.launch {
                withContext(Dispatchers.Main) {
                    // do whatever, e.g. update LiveData
                }
            }

代码语言:javascript
复制
            Handler(Looper.getMainLooper()).post(Runnable {
                   // do whatever, e.g. update LiveData
            })

哪种方法是正确的?也就是说,在运行时影响最小。

更新我发现我也可以做myLiveData.post(),它可以从后台线程工作。

尽管如此,我还是想知道在kotlin的现代Android系统中,向主线程分派工作的正确方法是什么?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-01-17 08:58:36

使用背景线程向使用LivaData主线程分派工作的正确方法是使用LivaData.postValue()方法。它将一个任务发布到主线程以设置给定的值。

另一种方法是在viewModelScope类中使用ViewModel扩展属性,默认情况下它使用Dispatchers.Main上下文来执行coroutine,这意味着您可以在这样的coroutine中更新UI。例如,在ViewModel类中:

代码语言:javascript
复制
viewModelScope.launch {
    val result = makeNetworkCall()
    // use result to update UI
    liveData.value = result
}

// withContext - switches context to background thread
suspend fun makeNetworkCall(): String = withContext(Dispatchers.IO) {
    delay(1000) // simulate network call
    "SomeResult"
}

依赖项使用viewModelScope

代码语言:javascript
复制
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'

GlobalScope是非常不鼓励使用的,它只能在特定情况下使用,here is a description why not use it

票数 3
EN

Stack Overflow用户

发布于 2022-01-17 08:04:06

内景模型,

代码语言:javascript
复制
private val _downloading = MutableLiveData<Result<Boolean>>()
val downloading: LiveData<Result<Boolean>>
    get() = _downloading

fun downloadFile() {
    viewModelScope.launch {
        try {
            _downloading.value = Result.Loading
            val result = withContext(Dispatchers.IO) {
                // download something
            }
            _downloading.value = Result.Success(true)
        } catch (ex: Exception) {
            _downloading.value = Result.Failure(ex)
        }
    }
}

在活动/碎片方面,

代码语言:javascript
复制
 viewModel.downloading.observe(this, {
        when (it) {
            is Result.Failure -> TODO()
            Result.Loading -> TODO()
            is Result.Success -> TODO()
        }
    })

Result是一个捕获状态的密封类,这反过来将帮助我们相应地更新UI。另外,我们使用的是viewmodelscope而不是GlobalScope,因为我们不希望在视图模型被破坏时继续下载。

票数 0
EN

Stack Overflow用户

发布于 2022-01-17 05:54:22

有许多方法可以简单地将值发布到实时数据,使用dispatcher's和处理程序,它们运行在主线程上,因为您提供了主线程的活套。

另一种方法是,您可以使用高阶函数来更新视图模型,这是很容易使用的,并尝试一下。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70736669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档