处理Kotlin Coroutines生产商内部的取消

时间:2018-06-25 18:22:59

标签: android kotlin kotlin-coroutines

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

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)
        }
}

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

2 个答案:

答案 0 :(得分:4)

首先,您不应该使用produce构建器来通过监听器调整API,因为在produce构建器主体中,通道会立即关闭并且将停止为其提供服务功能。相反,您应该只创建一个Channel()并创建相应的连接。

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

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)
    }
}

答案 1 :(得分:1)

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

但是,在此期间,有解决方案可以解决此问题。 一种解决方案是subclass the channel you're looking for, as Roman Elizarov suggested.

另一种解决方案是使用产生方式如下:

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)
    }
}