首页 > 解决方案 > Android Kotlin Coroutines:flow、callbackFlow、channelFlow、...其他流构造函数有什么区别

问题描述

我的代码应该通过流将 SharedPreferences 更改为可观察的存储,所以我有这样的代码

internal val onKeyValueChange: Flow<String> = channelFlow {
        val callback = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
            coroutineScope.launch {
                //send(key)
                offer(key)
            }
        }

        sharedPreferences.registerOnSharedPreferenceChangeListener(callback)

        awaitClose {
            sharedPreferences.unregisterOnSharedPreferenceChangeListener(callback)
        }
    }

或这个

internal val onKeyValueChange: Flow<String> = callbackFlow {
        val callback = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
            coroutineScope.launch {
                send(key)
                //offer(key)
            }
        }

        sharedPreferences.registerOnSharedPreferenceChangeListener(callback)

        awaitClose {
            sharedPreferences.unregisterOnSharedPreferenceChangeListener(callback)
        }
    }

然后我观察token、userId、companyId的这个偏好,然后登录但是有一个奇怪的事情,因为我需要构建应用程序三次,比如更改token不会导致tokenFlow发出任何东西,然后第二次新的userId不会导致userIdFlow发出任何东西,然后在第三次登录后我可以注销/登录并且它可以工作。注销时,我正在清除 prefs 令牌、userId、companyId 中的所有 3 个属性存储。

标签: kotlinkotlin-coroutineskotlin-flow

解决方案


对于callbackFlow

您不能在回调中emit()用作简单的Flow(因为它是一个suspend函数)。因此callbackFlow,该选项为您提供了一种同步的方式来执行此操作trySend()

例子:

fun observeData() = flow {
 myAwesomeInterface.addListener{ result ->
   emit(result) // NOT ALLOWED
 }
}

因此,协程为您提供了以下选项callbackFlow

fun observeData() = callbackFlow {
 myAwesomeInterface.addListener{ result ->
   trySend(result) // ALLOWED
 }
 awaitClose{ myAwesomeInterface.removeListener() }
}

对于channelFlow

文档Flow中描述了它和基本的主要区别:

使用具有默认缓冲区大小的通道。在结果流上使用缓冲区运算符来指定用户定义的值,并控制当数据生成速度快于消耗时发生的情况,即控制背压行为。

trySend()仍然代表同样的事情。这只是一种同步方式(一种非suspending方式)emit()send()

我建议您查看 Romans Elizarov博客以获取更多详细信息,尤其是这篇文章。

关于您的代码,因为callbackFlow您不需要协程启动:

coroutineScope.launch {
                send(key)
                //trySend(key)
            }

只需使用trySend()


推荐阅读