首页 > 其他分享 >kotlin开发 Flow的学习

kotlin开发 Flow的学习

时间:2023-07-29 15:46:26浏览次数:37  
标签:map Log 结果 kotlin Flow flow 学习 collect emit

前言

  Flow是配合Kotlin协程使用的异步编程工具。其实Flow的概念并不是独家的,更早之前Java端就有自带的stream与大名鼎鼎的RxJava,它们的思想都是响应式编程思想(或者也可以称呼链式编程),当时的响应式编程思想就是为了解决Java各个线程的异步处理结果进行同步。其更底层的思想核心是观察者模式或者生产消费者模式。所以回到Flow,它看似复杂有很多新概念,其实核心归根结底就是观察者模式思想与生产消费者模式思想。

Flow的使用结构

一个简单的例子

一个极简的例子快速了解一下,emit为发射,collect为接收

fun demo1() {
    GlobalScope.launch {
        flow<Int> {
            for (i in 0..10) {
                //emit为发射函数
                emit(i)
            }
        }.collect {
            //collect接收,它还是挂起函数必须在协程中调用
            Log.e("zh", "结果 = ${it} ")
        }
    }
}

/*  输出结果:
   结果 = 0
   结果 = 1
   结果 = 2
   结果 = 3
   结果 = 4
   结果 = 5
   结果 = 6
   结果 = 7
   结果 = 8
   结果 = 9
   结果 = 10
*/

构建方式

Flow有多种多样的构建方式,下面一一举例:

方式一 默认方式 

以默认的方式创建flow,上面的例子已经举例了,这里再举例一下。

fun demo1() {
    GlobalScope.launch {
        flow<String> {
            delay(500)
            emit("苹果")
            delay(500)
            emit("西瓜")
            delay(500)
            emit("香蕉")
            delay(500)
            emit("哈密瓜")
        }.collect{
            Log.e("zh", "时间 = ${System.currentTimeMillis()} 结果 = ${it}", )
        }
    }
}

/*  
    输出结果:
    时间 = 1690598408614 结果 = 苹果
    时间 = 1690598409116 结果 = 西瓜
    时间 = 1690598409617 结果 = 香蕉
    时间 = 1690598410118 结果 = 哈密瓜
 */

方式二 从集合发射流

通过集合调用asFlow()函数

fun demo2() {
    GlobalScope.launch {
        //从集合发射流
        (0..3).asFlow().collect {
            Log.e("zh", "结果 = ${it}", )
        }
        
        listOf<String>("苹果","西瓜","香蕉","哈密瓜").asFlow().collect{
            Log.e("zh", "结果 = ${it}", )
        }
    }
}
/*
    输出结果:
        结果 = 0
        结果 = 1
        结果 = 2
        结果 = 3
        结果 = 苹果
        结果 = 西瓜
        结果 = 香蕉
        结果 = 哈密瓜
 */

方式三 自建集合发射流

通过调用flowOf()函数

fun demo2() {
    GlobalScope.launch {
        flowOf("苹果" to 1, "香蕉" to 2, "西瓜" to 3).collect { pair ->
            Log.e("zh", "结果 = ${pair.first} ${pair.second}", )
        }
    }
}
/*
    输出结果:
        结果 = 苹果 1
        结果 = 香蕉 2
        结果 = 西瓜 3
 */

冷流

Flow是冷流的,冷流的概念是只有订阅者订阅后,发布者才会生产数据。 并且订阅者与发布者是一一对应关系,数据只会给目标订阅者发送,不会发送给其他。这类似于你去工厂下订单,工厂才会生产你需要的产品。

有冷流就会有热流的概念,热流其实是观察者模式(或者广播)的概念,发布端无论有没有订阅者,都会始终执行,并且有多个订阅者时,发布端跟订阅者是一对多的关系,热流可以与多个订阅者共享信息。

以下代码验证Flow的冷流特性:

fun demo3() {
    val flow = flow<String> {
        val simpleDateFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
        val timeString = simpleDateFormat.format(System.currentTimeMillis())
        emit(timeString)
    }

    GlobalScope.launch {
        for (index in 0..2){
            delay(1000)
            flow.collect {
                //collect调用它了,flow才会执行
                Log.e("zh", "结果 = ${it} ")
            }
        }
    }
}

/*
    输出结果:
        结果 = 13:53:53
        结果 = 13:53:54
        结果 = 13:53:55
 */

过滤filter

filter属于中间操作符,将数据过滤

fun demo3() {
    GlobalScope.launch {
        flow<Int> {
            for (i in 0..10){
                emit(i)
            }
        }.filter {
            //除余,过滤掉奇数
            it % 2 == 0
        }.collect{
            Log.e("zh", "结果 = ${it}")
        }
    }
}

/*
    输出结果:
        结果 = 0
        结果 = 2
        结果 = 4
        结果 = 6
        结果 = 8
        结果 = 10
 */

过滤filterNot

filterNot属于中间操作符, 它是反向过滤的,会将true的结果去除,将false结果继续发射

fun demo3() {
    GlobalScope.launch {
        flow<Int> {
            for (i in 0..10){
                emit(i)
            }
        }.filterNot {
            //除余,过滤掉偶数
            it % 2 == 0
        }.collect{
            Log.e("zh", "结果 = ${it}")
        }
    }
}

/*
    输出结果:
        结果 = 1
        结果 = 3
        结果 = 5
        结果 = 7
        结果 = 9
 */

转换组合map

map属于中间操作符,用于将数据转换或者组合

fun demo3() {
    val fruits = listOf<String>("苹果", "香蕉", "芒果")
    GlobalScope.launch {
        flow<Int> {
            for (i in 0..2) {
                emit(i)
            }
        }.map { it: Int ->
            Pair<String, Int>(fruits[it], it)
        }.collect { it: Pair<String, Int> -> //这里其实可以不用写it: Pair<String, Int>,写出来就是想表达,上面的flow<Int>被转换成了Pair<String, Int>
            Log.e("zh", "结果 = ${it}")
        }
    }
}

/*
    输出结果:
        结果 = (苹果, 0)
        结果 = (香蕉, 1)
        结果 = (芒果, 2)
 */

转换组合mapLatest

有时候我们的map方法处理转换需要耗时,但是发送端会一直发射新数据,导致map的耗时堆积积累,这会出现获取最后一个数据时已经耗时了很久。在一些情况下我们并不关切前面的数据,只希望获取最新的数据,这里就可以使用mapLatest。

fun demo3() {
    val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
    val flow = flow<Int> {
        for (i in 1..3){
            emit(i)
        }
    }
    GlobalScope.launch {
        //这里实现map作为下面mapLatest的对比
        flow.map {
            //这里写30毫秒延迟,模拟处理数据需要耗时
            delay(30)
            timeFormat.format(System.currentTimeMillis()) to it
        }.collect{
            Log.e("zh", "map >> 结果 = ${it}")
        }

        flow.mapLatest {
            //这里写30毫秒延迟,模拟处理数据需要耗时
            delay(30)
            timeFormat.format(System.currentTimeMillis()) to it
        }.collect{
            Log.e("zh", "mapLatest >> 最新数据结果 = ${it}")
        }
    }
}

/*
输出结果:
    map >> 结果 = (15:25:03, 1)
    map >> 结果 = (15:25:03, 2)
    map >> 结果 = (15:25:03, 3)
    mapLatest >> 最新数据结果 = (15:25:03, 3)
 */

切换线程

 

异常捕获

 

end

标签:map,Log,结果,kotlin,Flow,flow,学习,collect,emit
From: https://www.cnblogs.com/guanxinjing/p/17588612.html

相关文章

  • 反编译工具 Fernflower
    反编译.class文件工具Fernflower首先需要下载依赖包 http://the.bytecode.club/fernflower.jar下载后,切换到文件当前目录,直接使用命令 java-jarfernflower.jar目标文件目标路径 进行反编译即可反编译后的文件会生成到指定目录......
  • KV存储架构学习
    原文: 美团万亿级KV存储架构与实践美团KV存储历程一致性hash->memcached宕机摘除节点,会丢失数据扩容一致性hash也会丢数据。一致性hash->redis主从哨兵可以管理节点宕机扩缩容还是不行。阿里Tair美团基于tair研发出Cellar美团自研Squirrel基于开源redi......
  • Java学习-1.jdk和IntelliJ IDEA安装
    1.jdk下载与安装下载地址:https://www.oracle.com/java/technologies/downloads/#jdk20-windows 环境配置:1.我的电脑–》右键属性–》高级系统设置–》环境变量2.系统变量–》新建系统变量–》完成后点击确定3.双击系统变量下的PATH变量,新建两个路径(最下方两个)4. 新......
  • 小鱼书学习笔记
    第3章神经网络将输入信号的总和转换为输出信号,这种函数一般称为激活函数(activationfunction)激活函数的作用在于决定如何来激活输入信号的总和。本书在使用“感知机”一词时,没有严格统一它所指的算法。一般而言,“朴素感知机”是指单层网络,指的是激活函数使用了阶跃函数A的模型。......
  • React18学习笔记
    目录使用Create-React-App创建项目使用Vite创建项目JSX语法基础标签属性事件插入JS变量和表达式条件判断循环实践:列表页组件Props组件通讯HooksuseState基本使用特点使用useState实现问卷的增删改使用immer解决不可变数据问题useEffectuseRefuseMemo......
  • 人工智能学习之机器学习总结1
    人工智能里面分机器学习和深度学习,机器学习里有线性回归,逻辑回归,聚类,深度学习里有卷积神经网络和循环神经网和多层感知器首先学习了线性回归,其思想就是使用梯度下降算法(求导数)对a和b求导数,不断搜索迭代以求最好的线性a,b,使得预测值和真实值的差距越来越小,同时有损失函数MSE和R2......
  • JavaScript学习 -- SM3算法基本原理
    SM3算法是一种由国家密码管理局发布的哈希算法,被广泛用于数字签名和消息认证等应用中。在JavaScript中,我们可以使用第三方库来计算数据的SM3哈希值。本篇文章将介绍SM3算法的基本原理和相关技术,并提供一些实例来演示如何在JavaScript中使用SM3算法。SM3算法基本原理与MD5、SHA-1、S......
  • c语言学习笔记5
    内存空间在C语言中,内存空间可以被划分为以下几个部分:1.栈(Stack):这部分内存由编译器自动分配和释放,用于存放函数的参数值,局部变量等。其操作方式类似于数据结构中的堆栈,先进后出。2.堆(Heap):堆是用于动态内存分配的。与栈不同,堆的分配和释放必须由程序员自己操作。在C语言中,使用m......
  • day3c++学习
    1内存分区模型C++程序在执行时,将内存大方向划分为4个区域代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放函数的参数值,局部变量等堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收......
  • 线性基学习笔记
    线性基简介线性基是一种擅长处理异或问题的数据结构.设值域为\([1,N]\),就可以用一个长度为$⌈\log_2{N}⌉$的数组来描述一个线性基。特别地,线性基第\(i\)位上的数二进制下最高位也为第\(i\)位。一个线性基满足,对于它所表示的所有数的集合\(S\),\(S\)中任意多个数异或所得的......