Kotlin Flow入门

Flow作为Android开发中的重要的作用。尤其在Jetpack Compose里左一个collect,右一个collect。不交接Flow而开发Android是寸步难行。作为一个入门文章,如果你还不是很了解Flow的话,本文可以带你更进一步的了解Flow。

Flow是一个异步数据流,它会发出数据给收集者,最终带或者不带异常的完成任务。下面我们通过例子来学习。

假设我们正在下载一幅图片。在下载的时候,还要把下载的百分比作为值发出来,比如:1%,2%,3%,等。收集者(collector)会接收到这些值并在界面上以合适的方式显示出来。但是如果出现网络问题,任务也会因此终止。

现在我们来看一下Flow里的几个API:

  • 流构建器(Flow builder)
  • 操作符(Operator)
  • 收集器(Collector)

流构建器

简单来说,它会执行一个任务并把值发出来,有时也会只发出值而不会执行什么任务。比如简单的发出一些数字值。你可以把流构建器当做一个发言人。这个发言人会思考(做任务)和说(发出值).

操作符

操作符可以帮助转化数据。

我们可以把操作符当做是一个翻译。一个发言人说了法语,但是听众(收集器)只能听懂英语。这就需要一个翻译来帮忙了。它可以把法语都翻译成英语让听众理解。

当然,操作符可以做的远不止这些。以上的例子只是帮助理解。

收集器

Flow发出的值经过操作符的处理之后会被收集器收集。

收集器可以当做是收听者 。实际上收集器也是一种操作符,它有时被称作终端操作符

第一个例子

kotlin 复制代码
flow { 
    (0..10).forEach { 
        emit(it) 
    } 
}.map { 
    it * it 
}.collect { 
    Log.d(TAG, it.toString()) 
}
flow {} -> 流构建器
map {} -> 操作符
collect {} -> 收集器

我们来过一下上面的代码:

  • 首先,流构建器会发出从0到10的值
  • 之后,一个map操作符会把每个值计算(it * it)
  • 之后,收集器收集这些发出来的值并打印出来:0,1,4,9,16,25,36,49,64,81,100.

注意:collect方法把流构建器和收集器连到了一起,这个方法调用之后流就开始执行了。

流构建器的不同类型

流构建器有四种:

  1. flowOf():从一个给定的数据集合生成流
  2. asFlow(): 一个扩展方法,可以把某个类型转化成流
  3. flow{}: 我们例子中使用的方法
  4. channelFlow{}:使用构造器自带的send方法发送的元素构建流

例如:

flowOf()

kotlin 复制代码
flowOf(4, 2, 5, 1, 7) 
.collect { 
    Log.d(TAG, it.toString()) 
}

asFlow()

kotlin 复制代码
(1..5).asFlow()
.collect {
    Log.d(TAG, it.toString())
}

flow{}

kotlin 复制代码
flow {
    (0..10).forEach {
        emit(it)
    }
}
.collect {
    Log.d(TAG, it.toString())
}

channelFlow{}

kotlin 复制代码
channelFlow {
    (0..10).forEach {
        send(it)
    }
}
.collect {
    Log.d(TAG, it.toString())
}

flowOn操作符

flowOn这个操作符可以控制flow任务执行的线程的类型。在Android里一般是在一个后台线程执行任务,之后在界面上更新结果。

下面的例子里加了一个500毫秒的延迟来模拟实际任务。

kotlin 复制代码
val flow = flow {
    // Run on Background Thread (Dispatchers.Default)
    (0..10).forEach {
        // emit items with 500 milliseconds delay
        delay(500)
        emit(it)
    }
}
.flowOn(Dispatchers.Default)

CoroutineScope(Dispatchers.Main).launch {
    flow.collect {
        // Run on Main Thread (Dispatchers.Main)
        Log.d(TAG, it.toString())
    }
}

本例,流的任务就会在Dispatchers.Default这个"线程"里执行。接下来就是要在UI线程里更新UI了。为了做到这一点就需要在UI线程里collect

flowOn操作符就是用来控制任务执行的线程的。它的作用和RxJava的subscribeOn类似。

Dispatchers 主要有这些类型:IODefaultMain。flowOn和CoroutineScope都可以使用Dispatchers来执行任务执行的"线程"(暂且这么理解)。

使用流构造器

我们通过几个例子学习。

移动文件

这里我们用流构造器新建一个流,让流任务在后台线程执行。完成后在UI线程显示状态。

kotlin 复制代码
val moveFileflow = flow {
        // move file on background thread
        FileUtils.move(source, destination)
        emit("Done")
}
.flowOn(Dispatchers.IO)

CoroutineScope(Dispatchers.Main).launch {
    moveFileflow.collect {
        // when it is done
    }
}

下载图片

这个例子构造一个流在后台线程下载图片,并且不断的在UI线程更新下载的百分比。

kotlin 复制代码
val downloadImageflow = flow {
        // start downloading
        // send progress
        emit(10)
        // downloading...
        // ......
        // send progress
        emit(75)
        // downloading...
        // ......
        // send progress
        emit(100)
}
.flowOn(Dispatchers.IO)

CoroutineScope(Dispatchers.Main).launch {
    downloadImageflow.collect {
        // we will get the progress here
    }
}

现在你对kotlin的流也有初步的了解了,在项目中可以使用简单的流来处理异步任务。

什么是终端操作符

上文已经提到过collect()方法是一个终端操作符。所谓的终端操作符就是让流跑起来的挂起方法(suspend function)。在以上的例子中,流构造器构造出来的流是不动的,让这个流动起来的操作符就是终端操作符。比如collect

还有:

  • 转化为各种集合的,toList, toSet
  • 获取第一个first,与确保流发射单个值的操作符single
  • 使用reduce, fold这类的把流的值规约到单个值的操作符。

比如:

kotlin 复制代码
val sum = (1..5).asFlow()
    .map { it * it } // 数字 1 至 5 的平方                        
    .reduce { a, b -> a + b } // 求和(末端操作符)
println(sum)

今日份先更新到这了。to be continued...

相关推荐
装杯让你飞起来啊2 小时前
第 2 周 Day 5-6:综合小游戏 —— 学生成绩管理系统
windows·microsoft·kotlin
装杯让你飞起来啊4 小时前
Kotlin List / Array 与 for 循环
开发语言·kotlin·list
装杯让你飞起来啊6 小时前
混合练习 —— 猜数字游戏
windows·游戏·kotlin
装杯让你飞起来啊6 小时前
Kotlin 条件判断 if / when 与智能转换 smart cast
开发语言·python·kotlin
pengyu7 小时前
【Kotlin 协程修仙录 · 金丹境 · 初阶】 | 并发艺术:async/await 与并发组合的优雅之道
android·kotlin
黄林晴9 小时前
重磅发布!KMP 双端订阅支付彻底封神,一套代码搞定 iOS+Android
android·kotlin
alexhilton1 天前
揭密:Compose应用如何做到启动提升34%
android·kotlin·android jetpack
jason.zeng@15022072 天前
Androidr入门环境搭建
java·kotlin
jinanwuhuaguo3 天前
(第二十七篇)OpenClaw四月的演化风暴:OpenClaw 2026年4月全版本更新的文明级解读
大数据·人工智能·架构·kotlin·openclaw
我命由我123453 天前
Kotlin 开发 - lateinit 关键字
android·java·开发语言·kotlin·android studio·android-studio·android runtime