美文网首页
Android-Flow的构造简易拆解和一点思考

Android-Flow的构造简易拆解和一点思考

作者: 无极小屋 | 来源:发表于2023-09-19 01:34 被阅读0次

    自上而下,关注整体思路,文章不考虑更多细节

    //flow示例
            CoroutineScope(Dispatchers.Default).launch {
                flow {
                 //这部分就是collector
                    (1..a).forEach{
                        delay(1000)
                        emit(it + 1)
                    }
                }.collect {
                    Log.e("flow collect"," $it")
                }
            }
    

    拆解

    class FlowCollectorImpl<Int>: FlowCollector<Int> {
        suspend fun block(){
            (1..a).forEach {
                // 每隔 1 秒发射 1 个数字
                delay(1000)
                emit(it + 1)
            }
        }
    
        override suspend fun emit(value: Int) {
            Log.e("flow collect"," $it")// 打印数字
        }
    }
    
    //源码中的内容示意
    private class Flow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block() //引擎点火!
        }
    }
    

    如果之前不太明白这点的通过上面拆解代码应该可以恍然大悟

    部分源码

    public fun interface FlowCollector<in T> {
        public suspend fun emit(value: T)
    }
    
    public fun <T> flow(
       @BuilderInference block: suspend FlowCollector<T>.() -> Unit
    ): Flow<T> = SafeFlow(block)
    
    
    
        private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : kotlinx.coroutines.flow.Flow<T>,
            CancellableFlow<T> {
    
    
    
    
            public final override suspend fun collect(collector: FlowCollector<T>) {
                val safeCollector = SafeCollector(collector, coroutineContext)
                try {
                    collectSafely(safeCollector)
                } finally {
                    safeCollector.releaseIntercepted()
                }
            }
    
            override suspend fun collectSafely(collector: FlowCollector<T>) {
                collector.block()
                //相当于调用上面拆解的block(),block()中调用了emit(),开始发射并收集数据
            }
        }
    
    

    flow{}后面的lambda其实相当于就是FlowCollector的一个扩展函数,
    collect{ }后面的lambda就是emit方法的实现。

    调用collect之后,会调用collector.block(),而block()调用了emit(),从而开始打印数据。


    Flow的filter、map等方法原理和关键源码:

    核心原理:过滤本质是依次收集原flow数据后按设定条件过滤或转换后生成新的flow对象,发射新的转换后的数据!

    filter和map方法都是使用transform()实现,而transform()其实就是接收了上游数据后,再生成新的flow发给下游。

    public inline fun <T> Flow<T>.filter(crossinline predicate: suspend (T) -> Boolean): Flow<T> = transform { value ->
        if (predicate(value)) return@transform emit(value)
    }
    
    public inline fun <T, R> Flow<T>.transform(
        @BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
    ): Flow<R> = flow { // Note: safe flow is used here, because collector is exposed to transform on each operation
        collect { value ->
            // kludge, without it Unit will be returned and TCE won't kick in, KT-28938
            return@collect transform(value)
        }
    }
    

    上面的源码中,会生成一个新的Flow对象,而该Flow对象的数据源(收集者)就是从原Flow数据中collect的数据。然后经过转换,再次发射出去,而这个新Flow就是下游即将接收的经过筛选或转换后的新数据流。

    看上面源码中的其中部分

    collect { value ->
            // kludge, without it Unit will be returned and TCE won't kick in, KT-28938
            return@collect transform(value)
        }
    

    这部分就是收集原flow中的数据,然后按转换条件去发射新的数据。(transform方法中会执行emit(value) )。


    冷流的设计核心部分就是依靠函数参数这一特性来实现的(函数参数大多数情况为lambda表达式),函数参数是kotlin最具创造性和颠覆性的特性之一,也是源码中很多复杂方法看起来难以理解的原因之一。函数参数就是一个死参数,开发者可以直接执行它(),或者不去执行而是继续向下传递该函数参数,如果你不主动去调用它(),它是不会执行它方法体内的代码的。比如上面案例源码:

    flow { // Note: safe flow is used here, because collector is exposed to transform on each operation
        collect { value ->
            // kludge, without it Unit will be returned and TCE won't kick in, KT-28938
            return@collect transform(value)
        }
    }
    

    生成新的flow时,虽然其内部调用了collect{},但是其并没有真正执行(它重写了emit()),只是作为一个参数继续在flow构造内部向下传递该参数,最后在

    private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
        override suspend fun collectSafely(collector: FlowCollector<T>) {
            collector.block()
        }
    }
    
        public final override suspend fun collect(collector: FlowCollector<T>) {
            val safeCollector = SafeCollector(collector, coroutineContext)
            try {
                collectSafely(safeCollector)
            } finally {
                safeCollector.releaseIntercepted()
            }
        }
    

    上面中的collectSafely()中才真正被执行,上面是使用函数参数(FlowCollector的扩展函数,这个扩展函数就是生成flow{}时其内的lambda表达式)重写了collectSafely()!而collectSafely()是在collect()中执行的,也就是开始收集时才执行的,这就形成了闭环。这也是冷流的‘冷’原因所在,前期的工作是在“造车”,最后的执行才“点火启动”。

    这一特性,使得kotlin拥有了无与伦比的功能构造能力。方法可以先通过传递函数参数来构造功能的整体架构,构造完成之后最后去执行来实现整个功能的运转。


    如有问题,欢迎各位指正,感谢

    相关文章

      网友评论

          本文标题:Android-Flow的构造简易拆解和一点思考

          本文链接:https://www.haomeiwen.com/subject/xugivdtx.html