0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

仿Flow构建器创建数据流教程

jf_78858299 来源:北洋洋洋 作者:北洋 2023-02-14 15:31 次阅读

第一步

首先回顾下前面的知识点:

flow提供的只是一个 「扩展函数」 返回的是一个保存了这个方法的类实例,并且该类提供emit方法以供flow中调用

构建Flow

「flow方法」

object Flow {
    fun  flow(collect: Collector<T>.() -> Unit): SafeFlowCollector {
        return SafeFlowCollector(collect)
    }
}

定义一个Flow类,内部提供flow方法。

「SafeCollector」 类:

class SafeFlowCollector<T>(val collect: Collector.() -> Unit) {
    //将该Function保存在调用flow后创建的实例中获取实例创建FlowCollector
    fun collectFunction(a: (T) -> Unit) {
        val co = Collector(a)
        co.collect()
    }

「Collector类」

class Collector<T>(val action: (a: T) -> Unit) {
    fun emit(value: T) {
        action(value)
    }
}

这是flow方法需要创建的三个类,虽然功能不多,但是对于简单的构建流还是绰绰有余的。

分析

可以看到flow方法传入的方法参数collect被定义为了Collector的扩展函数,并且保存在了刚创建的SafeCollector的类中用collect函数表示。

「第一个功能」 :flow参数提交的类型和collect中收到的类型一致,我采用了更加直接的形式定义flow时需要设置传输的类型,在emit和collect中都是对应的类型。

「实现」

flow定义类型和emit类型保持一致:通过Collector< T >实现

flow定义类型和收集到的类型一致:通过SafeFlowCollector< T >实现

第二步

构建collect收集器

第一步发射器设置好后,我们限制了发送的类型和接受的类型,并且将发送逻辑保存在了实例中。接下来我们需要调用该实例对象以触发发送逻辑,在发送逻辑中还需要调用到我们收集的逻辑。

因此收集逻辑需要单独存放,因此需要单独构建一个类,这个类还必须可以调用到发送逻辑。

❝注:Flow中采用的是collect收集触发flow流发送逻辑,本人使用时是按照collectFunction定义的。两者逻辑一样只是名称不同

flow要构造成哪个类的构造函数,该类就需要持有collect传入的方法。这也是Collector的功能

SafeFlowCollector和Collector

Collector保存collect传入的方法,flow扩展Collector以使他可以触发发射逻辑也就是flow传入的参数。

class SafeFlowCollector<T>(val collect: Collector.() -> Unit)  //扩展函数

//发送逻辑emit,这个action是从哪里来的呢?
class Collector<T>(val action: (a: T) -> Unit) {
    fun emit(value: T) {
        action(value)
    }
}

再解collectFunction方法:

上面说到action方法,接着看:

fun collectFunction(a: (T) -> Unit) {
    //可以看到调用collectFunction方法将传入的方法参数保存到了Collector中也就是action方法
    val co = Collector(a)
    //触发collect,collect也就是flow中传入的方法。
    co.collect()
}

原生flow,collect的demo:

❝注:将上述三个类拷贝到您的项目中即可调用

Flow.flow {
    Log.i(TAG, "emit before")
    emit("1")
    Log.i(TAG, "emit after")
}.collectFunction {
    Log.i(TAG, "collectionFunction is : $it")
}

总结

因此可以看到调用collectFunction方法会调用到flow传入的方法中,在flow传入的方法中调用emit又会执行collectFunction传入的方法。

❝ps:collectFunction类比于原生Flow中的collect方法即可

扩展中间转换符

flow和collect我们支持了,现在我们来扩展转换操作符。这里还是简单实现,原生Flow的实现看的很绕。

map操作符

map方法接受的是一个方法。并且该方法的参数是原数据,经过转换后返回的值是collect接受的值。

首先我们需要确定几个点:

1、map的参数如何确定?

❝map的参数即上一个Flow emit的值,而在我们这个里面emit的值是通过flow< T >指定的,所以参数直接写T就可以。2.map的返回值如何确定?

❝map的返回值要经过两个阶段,收到上一个flow发送的值调用转换函数把值传入得到结果,因此map中最后一行即为返回值。

3.支持链式调用map

❝map之后还需要再次经历map或者collectFunction方法,因此他返回的也必须是一个flowSafeCollector。参数是map传入方法的返回值。

确定好这些来实现,由于需要调用到上一个flow的collect方法, 「原生中是扩展函数this即代表上一个Flow。本demo异曲同工,直接定义为该类的函数」 。于是实现如下, 「修改SafeFlowCollector」 类即可:

class SafeFlowCollector<T>(val collect: Collector.() -> Unit) {
    fun collectFunction(a: (T) -> Unit) {
        val co = Collector(a)
        co.collect()
    }

    //对比发现只是扩展了这个map方法
    fun  map(mapFunction: (T) -> R): SafeFlowCollector {
                //外层调用经map转换后的collectionFunction会走到flow里面
        return Flow.flow {
                //this@SafeFlowCollector是为了保证调用到调用map函数的flow触发这个flow的收集
                this@SafeFlowCollector.collectFunction {
                    //当最内层flow函数调用emit,此collectFunction会收到回调,进行转换后调用flow返回给最外层的collectFunction方法。
                    emit(mapFunction(it))
                }
        }
    }

}

读者可以直接将上面的SafeFlowCollector换成这个即可。

可支持map转换Flow的demo

Flow.flow {
    emit("2")
}.map {
    it.toInt()
}.map {
    it.toString()
}.collectFunction {
    Log.i(TAG, "onCreate: it   $it")
}

「最后还是放出这张图片:」

图片

1649984327(1).png

扩展中间操作符

还是像map一样,直接把该方法添加到 「SafeFlowCollector」 类中即可

//新增扩展zip操作流,用于多个流发射,就近原则谁先返回用谁
//tips1:收集器类型及·返回流类型,由于返回值相同。因此复用调用方流的泛型即可
//2:开启收集后触发多个流的收集,利用标志位进行判断是否发射。目前采用这种方式。缺点:流发射后应该关闭但是此处只是限制了流的发射逻辑
fun zip(twoFlow: SafeFlowCollector<T>): SafeFlowCollector {
    return Flow.flow {
        var a = false //通过定义condition判断,不支持协程需要手动切换线程测试效果
        //开启两个刘的收集,谁先收集到谁先返回
        this@SafeFlowCollector.collectFunction {
            if (!a) {
                a = true
                this.emit(it)
            }
        }
        twoFlow.collectFunction {
            if (!a) {
                a = true
                this.emit(it)
            }
        }
    }
}

❝该种方案可支持多个Flow发射,zip中将参数变为可变参数即可。SafeCollector中扩展

zip操作符Demo

val flow1 = Flow.flow<Int> {
        emit(2)
}
Flow.flow<String> {
    Thread{
        emit("111")
    }.interrupt()
}.map {
    it.toInt()
}.zip(flow1).collectFunction {
    Log.i(TAG, "onCreate: ZIP操作符下的Flow收集器收集到的数据位 $it")
}

❝上述方案采用标志位实现不太优雅,有更好的方式可以提出~~

「不支持协程~~~,需要调度手动切换线程」

总结

「转换操作符只是中间操作符的一种,其他中间操作符原理大致都一样。都是经过在封装一次flow然后触发上级flow的收集,最后调用到最里层的flow,调用emit在一层层经过中间操作符处理给到最外层」

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3558

    浏览量

    93524
  • 函数
    +关注

    关注

    3

    文章

    4276

    浏览量

    62303
  • Flow
    +关注

    关注

    0

    文章

    10

    浏览量

    8825
收藏 人收藏

    评论

    相关推荐

    SSIS体系结构控制数据流技术对比分析

    数据流(Data Flow)是控制中的核心组件,用于把数据提取到服务内存中,转换数据并把
    发表于 11-06 11:19 4700次阅读

    大众数据流分析

    、3.0发动机数据流定义与解释别克君威2.0发动机数据流定义与解释凯越数据流列表凯越发动机数据流定义赛欧数据流列表赛欧
    发表于 06-15 12:28

    大众防盗数据流

    汽车防盗数据流
    发表于 08-31 16:23

    研究labview的数据流

    我在NI上下载了labview一个程序,运行起来还有点小问题,我想运行 看看他的数据流,深入的研究下,哪位大神能指导下,怎么增加个仿真信号 和输出采集,这样能清楚的看到其整个数据流的过程,谢谢~
    发表于 12-31 10:40

    LabVIEW中的数据流编程基础

      LabVIEW按照数据流(dataflow)模式运行VI。 当接受到所有所需的输入时,程序框图节点将运行。节点在运行时产生输出端数据并将该数据传送给数据流路径中的下一个节点。
    发表于 11-20 10:47

    关于高速数据流盘处理技术看完你就懂了

    本文讨论了支持高速数据流处理的技术、最大化系统处理性能的应用设计和在数据流导入磁盘与数据流导入存储应用中可获得的
    发表于 04-29 06:25

    基于数据流的Java字节码分析

    本文基于数据流框架理论,提出了如何将数据流分析方法应用于JAVA 字节码中,通过建立数据流与半格、数据流和函数调用图的关系,从而对类型信息进行分析。实验表明该
    发表于 12-25 13:22 9次下载

    网络数据流存储算法分析与实现

    针对网络数据流存储的瓶颈问题,提出了一种网络数据流存储算法分析与实现方法,仿真结果表明,模型能显著提高网络数据流的实时存储能力
    发表于 05-26 15:57 21次下载
    网络<b class='flag-5'>数据流</b>存储算法分析与实现

    基于数据流特征的电子文件访问方法

    的概念,提取了常用网络协议传输电子文件的特征值,设计和构建了扩展性强的数据流特征数据库,并给出了该特征数据库检测和防止电子文件网络泄漏过程。实验数据
    发表于 11-15 14:15 5次下载

    基于FPGA芯片的数据流结构分析

    Virtex 型FPGA 芯片是Xilinx 公司芯片系列中的一种,Virtex 系列的数据流及配置逻辑与XC4000 的数据流及配置逻辑有显著不同,但却与Xilinx 的FPGA 家族保持了很大
    发表于 11-18 11:37 2281次阅读

    数据流编程模型优化

    数据流编程模型将程序的计算与通信分离,暴露了应用程序潜在的并行性并简化了编程难度。分布式计算框架利用廉价PC构建多核集群解决了大规模并行计算问题,但多核集群层次性存储结构和处理单元对数据流程序的性能
    发表于 11-23 15:48 3次下载
    <b class='flag-5'>数据流</b>编程模型优化

    基于角度方差的数据流异常检测算法

    传统基于欧氏距离的异常检测算法在高维数据检测中存在精度无法保证以及运行时间过长的问题。为此,结合高维数据流的特点运用角度方差的方法,提出一种改进的基于角度方差的数据流异常检测算法。通过构建
    发表于 01-17 11:29 1次下载
    基于角度方差的<b class='flag-5'>数据流</b>异常检测算法

    数据流是什么

    数据流最初是通信领域使用的概念,代表传输中所使用的信息的数字编码信号序列。然而,我们所提到的数据流概念与此不同。这个概念最初在1998年由Henzinger在文献87中提出,他将数据流定义为“只能以事先规定好的顺序被读取一次的
    的头像 发表于 02-27 15:25 6997次阅读

    Labview数据流编程的简单介绍

    Labview数据流编程基本概念视频教学
    的头像 发表于 08-05 06:05 4009次阅读

    控制数据流的区别

    控制数据流的区别  在计算机科学中,控制数据流是两个非常重要的概念。虽然它们经常一起使用,但它们具有非常不同的含义。本文将讨论控制
    的头像 发表于 09-13 11:17 5189次阅读