在 Kotlin Coroutines 生产者内部处理取消

Handle cancelation inside Kotlin Coroutines producer

是否可以在生产者构建器内部处理生产者取消?取消订阅回调可能很有用:

private fun changes(key: String) = produce<Unit>(UI, CONFLATED) {
        val listener = OnSharedPreferenceChangeListener { _, changedKey ->
             if (key == changedKey) offer(Unit)
        }
        prefs.registerOnSharedPreferenceChangeListener(listener)
        ???.onCancel { 
                 prefs.unregisterOnSharedPreferenceChangeListener(listener)
        }
}

或者可能存在另一种方法来实现这种情况?

首先,你不应该使用 produce 构建器以这种方式适应 API 与监听器,因为在 produce 构建器主体存在时通道会立即关闭并将停止发挥其作用。相反,您应该只创建一个 Channel() 并创建相应的连接。

遗憾的是,频道目前不提供安装取消侦听器的开箱即用方式(请参阅 issue #341)。在通道关闭时立即得到通知的唯一方法是扩展相应的通道 class,这将导致以下代码:

private fun changes(key: String): ReceiveChannel<Unit> = object : ConflatedChannel<Unit>() {
    val listener = OnSharedPreferenceChangeListener { _, changedKey ->
        if (key == changedKey) offer(Unit)
    }

    init {
        prefs.registerOnSharedPreferenceChangeListener(listener)
    }

    override fun afterClose(cause: Throwable?) {
        prefs.unregisterOnSharedPreferenceChangeListener(listener)
    }
}

即将推出的 kotlinx.coroutines 库版本 should expose a Channel.invokeOnClose { ... } method 可以满足此类用例。

但是,同时有解决此问题的方法。 一种解决方案是

另一种解决方案是使用以下方式生成:

fun SharedPreferences.changes(key: String) = produce {
    val changesChannel = ConflatedChannel<Unit>()
    val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, changedKey ->
        if (key == changedKey) changesChannel.offer(Unit)
    }
    registerOnSharedPreferenceChangeListener(listener)
    try {
        for (change in changesChannel) {
            send(change)
        }
    } finally {
        unregisterOnSharedPreferenceChangeListener(listener)
    }
}