flow在api文档中给了一个使用例子
fun fibonacci(): Flow<BigInteger> = flow {
var x = BigInteger.ZERO
var y = BigInteger.ONE
while (true) {
println("fibonacci while $x")
emit(x)
x = y.also {
y += x
}
}
}
测试
fun testJob(){
testJob= viewModelScope.launch {
var i=0
repository.fibonacci().take(100).collect{
i++
println("fibonacci $it")
println("fibonacci $i")
}
}
}
take表示当消费了100个元素之后,flow将取消。
可以看到 while循环里的代码也只是打到100就结束了。
API文档里面还有说到 flow 中的emit只能严格的在block所在的dispatcher中执行,来维持flow的上下文,即我们不能再flow中切换dispatcher。
flow {
emit(1) // Ok
withContext(Dispatcher. IO) {
emit(2) // Will fail with ISE
}
}
当我们需要切换并行发送元素的时候,怎么办呢,有了channelflow,
* fun <T> Flow<T>.merge(other: Flow<T>): Flow<T> = channelFlow {
* // collect from one coroutine and send it
* launch {
* collect { send(it) }
* }
* // collect and send from this coroutine, too, concurrently
* other.collect { send(it) }
* }
channelflow提供了一个缓冲区域,
channelflow还可以保持开放,正常情况下在 channelFlow lambda执行结束后,通道将关闭。
但是如果调用了 awaitClose ,通道保持开放,直到调用了channel 的close。
send、trySend和trySendBlocking都是用于向Channel中发送数据的方法,但它们在使用方式和行为上有所不同:
send方法:
send方法用于向Channel发送数据。如果Channel未满,数据会被立即发送;如果Channel已满,发送操作会阻塞,直到Channel有可用空间为止。
示例代码:channel.send(value)
trySend方法:
trySend方法尝试发送数据到Channel中,如果发送成功则返回true,如果Channel已满则立即返回false,而不会阻塞。
示例代码:if (channel.trySend(value)) { /* 成功发送 */ } else { /* 发送失败 */ }
trySendBlocking方法:
trySendBlocking方法尝试发送数据到Channel中,如果发送成功则返回true,如果Channel已满则会阻塞当前协程,直到Channel有可用空间为止。
示例代码:if (channel.trySendBlocking(value)) { /* 成功发送 */ } else { /* 发送失败 */ }
补充说明:
ChannelFlow默认带有64个缓存区,这意味着在大多数情况下,数据可以立即被缓存并异步处理
callbackflow的话,相当于是channelflow的子集,强制运行 awaitclose,这对于需要取消注册,资源清理的时候很有用
fun networkAvailability(context: Context) = callbackFlow {
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
trySend(true)
}
override fun onLost(network: Network) {
super.onLost(network)
trySend(false)
}
}
val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
//注册
manager.registerNetworkCallback(
NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), callback
)
awaitClose {
//取消注册
manager.unregisterNetworkCallback(callback)
}
}