You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin协程管道与Data Binding遇ClosedReceiveChannelException视图不更新

Why isn't my ViewModel's text updating when catching ClosedReceiveChannelException?

Great question! The root issue here lies in how Kotlin coroutines handle ClosedReceiveChannelException—this exception is a subclass of CancellationException, which carries a special meaning for coroutines: it signals that the coroutine should be cancelled.

What's happening differently?

When you throw a regular IllegalStateException, your catch block runs in an active coroutine—the coroutine isn't marked for cancellation, so any updates to your text property (backed by LiveData/ObservableField for Data Binding) work as expected.

But when processedTextPipe.receive() throws ClosedReceiveChannelException, even though you catch Throwable, the coroutine is already marked as cancelled. Some Data Binding updates might fail to propagate in a cancelled coroutine context, especially if your property updates rely on the coroutine's active state (or if the cancellation subtly interferes with how Data Binding observes changes).

Fixes to try:

1. Run the update in a new active coroutine

Since the original coroutine is cancelled, spin up a new one using viewModelScope to ensure the update happens in an active context:

fun onProcessStart(cachedImageId: String) = viewModelScope.launch {
    text = "Started for image: $cachedImageId"
    try {
        val extractedTextPipe = extractText(cachedImageId)
        val processedTextPipe = processText(extractedTextPipe)
        val processedText = processedTextPipe.receive()
        text = "Finished for image: $cachedImageId"
    } catch (t: Throwable) {
        Log.d("TEST", "${t.message} $t")
        // Launch a new active coroutine to update the text
        viewModelScope.launch {
            text = "Failed for image: $cachedImageId with code: ${t.message}"
        }
    }
}

2. Force the update on the Main thread directly

Bypass the coroutine context entirely by using a Handler to post the update to the Main thread (where Data Binding operates):

import android.os.Handler
import android.os.Looper

fun onProcessStart(cachedImageId: String) = viewModelScope.launch {
    text = "Started for image: $cachedImageId"
    try {
        val extractedTextPipe = extractText(cachedImageId)
        val processedTextPipe = processText(extractedTextPipe)
        val processedText = processedTextPipe.receive()
        text = "Finished for image: $cachedImageId"
    } catch (t: Throwable) {
        Log.d("TEST", "${t.message} $t")
        Handler(Looper.getMainLooper()).post {
            text = "Failed for image: $cachedImageId with code: ${t.message}"
        }
    }
}

3. Handle channel closure explicitly before cancellation

If you control the channel logic, you can check if the channel is closed before calling receive(), avoiding the cancellation exception altogether:

fun onProcessStart(cachedImageId: String) = viewModelScope.launch {
    text = "Started for image: $cachedImageId"
    try {
        val extractedTextPipe = extractText(cachedImageId)
        val processedTextPipe = processText(extractedTextPipe)
        // Check if channel is open before receiving
        if (!processedTextPipe.isClosedForReceive) {
            val processedText = processedTextPipe.receive()
            text = "Finished for image: $cachedImageId"
        } else {
            text = "Failed for image: $cachedImageId with code: Channel closed"
        }
    } catch (t: Throwable) {
        Log.d("TEST", "${t.message} $t")
        text = "Failed for image: $cachedImageId with code: ${t.message}"
    }
}

Any of these approaches should ensure your Data Binding updates trigger correctly even when handling channel closure exceptions.

内容的提问来源于stack exchange,提问作者Cativail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:23:41