Kotlin Android - 分派到主线程的正确方法

zai*_*man 0 android kotlin android-livedata kotlin-coroutines coroutinescope

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

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)
Run Code Online (Sandbox Code Playgroud)

现在我找到了两种不同的方法来调度到主线程ViewModel(根据 AAC 指南,它没有引用 Context),请参见此处:

            GlobalScope.launch {
                withContext(Dispatchers.Main) {
                    // do whatever, e.g. update LiveData
                }
            }
Run Code Online (Sandbox Code Playgroud)

或者

            Handler(Looper.getMainLooper()).post(Runnable {
                   // do whatever, e.g. update LiveData
            })
Run Code Online (Sandbox Code Playgroud)

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

更新我确实发现我也可以这样做myLiveData.post(),并且它可以在后台线程中工作。

不过,我想知道在 kotlin 下现代 Android 中将工作分派到主线程的正确方法是什么

Ser*_*gey 5

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

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

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"
}
Run Code Online (Sandbox Code Playgroud)

使用依赖viewModelScope

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
Run Code Online (Sandbox Code Playgroud)

GlobalScope非常不鼓励使用,它只能在特定情况下使用,这里说明为什么不使用它