文章目录
flow 是啥
按顺序发出值并正常完成或异常完成的冷流异步数据流
flow咋用?flow 是kotlin coroutines 库里面的类,所以使用 flow 之前,要确保添加了协程依赖
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
引入依赖后,我们写一段代码:
flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.collect {
//接收结果
Log.d("flow-", "value $it")
}
如果你这样写就会报错
意思是:collect 方法是 suspend 修饰的挂起函数,只能在协程里,或者其他挂起函数中使用。我们来修改一下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
}
}
输出结果:
D/flow-: value: 1
D/flow-: value: 2
创建flow
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
val flow = flow {
emit(1)
emit(2)
}
val flow2 = flowOf(1, 2.3)
val flow3 = mutableListOf(1, 2, 3, 4, 5).asFlow()
val flow4 = (1..4).asFlow()
}
}
}
map 操作符
例子1
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.map {
"map $it"
}.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
}
}
输出结果:
D/flow-: value: map 1
D/flow-: value: map 2
例子2
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow: Flow<String> = flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.map {
"map $it"
}
GlobalScope.launch {
flow.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
}
}
输出结果:
```java
D/flow-: value: map 1
D/flow-: value: map 2
catch 捕获上游出现的异常在接收数据的时候,我们用了 collect 方法,collect 英文含义就是
收集
的意思
当 flow 流操作中发生异常的情况时,程序会发生崩溃:
例子1:不捕获异常
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow: Flow<String> = flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.map {
0 / 0 //人为制造异常
"map $it"
}
GlobalScope.launch {
flow
.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
}
}
输出结果:程序发生崩溃
例子2:捕获异常
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow: Flow<String> = flow {
emit(1) //发射数字 1
emit(2) //发射数字 2
}.map {
0 / 0 //人为制造异常
"map $it"
}
GlobalScope.launch {
flow
.catch {
//捕获异常
Log.d("flow-", "value: ${it.message}")
}.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
}
}
输出结果:
D/flow-: exception: divide by zero
可以看到程序可以正常运行,我们也正常捕获了异常
取消flowFlow创建后并不返回可以cancel的句柄,但是一个flow的collect是suspend的,所以可以像取消一个suspend方法一样取消flow的collection。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch {
flow {
emit(1)
kotlinx.coroutines.delay(3000)
emit(2)
kotlinx.coroutines.delay(3000)
emit(3)
}
.collect {
//接收结果
Log.d("flow-", "value: $it")
}
}
findViewById<Button>(R.id.cancel).setOnClickListener {
job.cancel()
}
}
}
collectIndexed
输出带有索引的结果
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
flow {
Log.d("flow-", "init")
emit(1)
emit(2)
}.collectIndexed { index, value ->
Log.d("flow-", "onEach $index $value")
}
}
}
}
//输出结果
D/flow-: init
D/flow-: onEach 0 1
D/flow-: onEach 1 2
distinctUntilChanged 过滤重复的值
如果生产的值和上个发送的值相同,值就会被过滤掉
flow {
emit(1)
emit(1)
emit(2)
emit(2)
emit(3)
emit(4)
}.distinctUntilChanged()
// 结果:1 2 3 4
// 解释:
// 第一个1被释放
// 第二个1由于和第一个1相同,被过滤掉
// 第一个2被释放
// 第二个2由于和第一个2相同,被过滤掉
// 第一个3被释放
// 第一个4被释放
可以传参
(old: T, new: T) -> Boolean
,进行自定义的比较
private class Person(val age: Int, val name: String)
flow {
emit(Person(20, "张三"))
emit(Person(21, "李四"))
emit(Person(21, "王五"))
emit(Person(22, "赵六"))
}.distinctUntilChanged{old, new -> old.age == new.age }
.collect{ value -> println(value.name) }
// 结果:张三 李四 赵六
// 解释:本例子定义如果年龄相同就认为是相同的值,所以王五被过滤掉了
可以用
distinctUntilChangedBy
转换成年龄进行对比
flow {
emit(Person(20, "张三"))
emit(Person(21, "李四"))
emit(Person(21, "王五"))
emit(Person(22, "赵六"))
}.distinctUntilChangedBy { person -> person.age }
// 结果:张三 李四 赵六
transform
对每个值进行转换,用一个新的
FlowCollector
发射数据
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
flow {
emit(1)
emit(2)
emit(3)
emit(4)
emit(5)
}
.transform {
if (it % 2 == 0) {
emit("value-$it")
}
}
.collect {
Log.d("flow-", "collect $it")
}
}
}
}
//输出结果
D/flow-: collect value-2
D/flow-: collect value-4
filter 过滤
val list = mutableListOf(1, 2, 3, 4, 5)
val job = GlobalScope.launch {
list.asFlow()
.filter {
it > 3
}
.collect {
Log.d("flow-", "value: $it")
}
}
输出结果:
D/flow-: value: 4
D/flow-: value: 5
flowOn 数据发射的线程类似的还有 filterNot 反向过滤,这里就不举例子了
可以切换CoroutineContext
说明:flowOn只影响该运算符之前的CoroutineContext,对它之后的CoroutineContext没有任何影响
我们先看一下默认情况下的线程问题
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow = flow {
Log.d("flow-", "init->thread:${Thread.currentThread().name}")
emit(1)
emit(2)
}
val job = GlobalScope.launch {
flow
.collect {
Log.d("flow-", "collect->thread:${Thread.currentThread().name} value: $it")
}
}
}
}
输出结果
D/flow-: init->thread:DefaultDispatcher-worker-1
D/flow-: collect->thread:DefaultDispatcher-worker-1 value: 1
D/flow-: collect->thread:DefaultDispatcher-worker-1 value: 2
我们可以看到默认情况下,flow 数据发射的线程就是当前协程所在的线程。
我们可以用 flowOn 方法指定数据发射的线程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow = flow {
Log.d("flow-", "init->thread:${Thread.currentThread().name}")
emit(1)
emit(2)
}
GlobalScope.launch {
flow
.flowOn(Dispatchers.Main) //指定数数据发射的线程为主线程
.collect {
Log.d("flow-", "collect->thread:${Thread.currentThread().name} value: $it")
}
}
}
}
输出结果
D/flow-: init->thread:main
D/flow-: collect->thread:DefaultDispatcher-worker-2 value: 1
D/flow-: collect->thread:DefaultDispatcher-worker-2 value: 2
可以看到数据发射的线程已经被切换到了主线程
多个操作符的情况下,如下
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
flowDemo()
}
}
suspend fun flowDemo() {
flow {
Log.d("flow-", "init ${Thread.currentThread().name}")
emit(1)
}.map {
Log.d("flow-", "map ${Thread.currentThread().name}")
it
}.flowOn(Dispatchers.Main)
.collect {
Log.d("flow-", "collect ${Thread.currentThread().name} value:$it")
}
}
}
D/flow-: init main
D/flow-: map main
D/flow-: collect DefaultDispatcher-worker-1 value:1
launchIn
scope.launch { flow.collect() }
的缩写, 代表在某个协程上下文环境中去接收释放的值
例子:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
val flow = flow {
Log.d("flow-", "init")
emit(1)
emit(2)
}.onEach {
Log.d("flow-", "onEach $it")
}
flow.launchIn(this)
}
}
}
输出结果:
D/flow-: init
D/flow-: onEach 1
D/flow-: onEach 2
conflate()
如果值的生产速度大于值的消耗速度,就忽略掉中间未来得及处理的值,只处理最新的值。
val flow1 = flow {
delay(2000)
emit(1)
delay(2000)
emit(2)
delay(2000)
emit(3)
delay(2000)
emit(4)
}.conflate()
flow1.collect { value ->
println(value)
delay(5000)
}
// 结果: 1 3 4
// 解释:
// 2000毫秒后生产了1这个值,交由collect执行,花费了5000毫秒,当1这个值执行collect完成后已经经过了7000毫秒。
// 这7000毫秒中,生产了2,但是collect还没执行完成又生产了3,所以7000毫秒以后会直接执行3的collect方法,忽略了2这个值
// collect执行完3后,还有一个4,继续执行。
withIndex
将值封装成
IndexedValue
对象
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.withIndex()
// 结果:
// I/System.out: IndexedValue(index=0, value=1)
// I/System.out: IndexedValue(index=1, value=2)
// I/System.out: IndexedValue(index=2, value=3)
// I/System.out: IndexedValue(index=3, value=4)
onEach
每个值释放的时候可以执行的一段代码
onEach 函数,执行一段代码后,再释放值
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.onEach { println("接收到$it") }
// 结果:
I/System.out: 接收到1
I/System.out: 1
I/System.out: 接收到2
I/System.out: 2
I/System.out: 接收到3
I/System.out: 3
I/System.out: 接收到4
I/System.out: 4
onStart 在数据发射之前触发
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow = flow {
Log.d("flow-", "init->thread:${Thread.currentThread().name}")
emit(1)
emit(2)
emit(3)
}
GlobalScope.launch {
flow
.onStart {
Log.d("flow-", "onStart->thread:${Thread.currentThread().name}")
}
.flowOn(Dispatchers.Main) //指定数数据发射的线程为主线程
.collect {
Log.d("flow-", "collect->thread:${Thread.currentThread().name} value: $it")
}
}
}
}
输出结果:
D/flow-: onStart->thread:main
D/flow-: init->thread:main
D/flow-: collect->thread:DefaultDispatcher-worker-1 value: 1
D/flow-: collect->thread:DefaultDispatcher-worker-1 value: 2
D/flow-: collect->thread:DefaultDispatcher-worker-1 value: 3
结论:
onStart 方法在数据发射之前调用,onStart 所在的线程是数据产生的线程。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
val flow = flow {
Log.d("flow-", "init ${Thread.currentThread().name}")
emit(1)
emit(2)
}
flow
.onStart {
Log.d("flow-", "onStart ${Thread.currentThread().name}")
}
.onCompletion {
Log.d("flow-", "onCompletion ${Thread.currentThread().name}")
}.collect {
Log.d("flow-", "collect ${Thread.currentThread().name} value:$it")
}
}
}
}
输出结果:
D/flow-: onStart DefaultDispatcher-worker-1
D/flow-: init DefaultDispatcher-worker-1
D/flow-: collect DefaultDispatcher-worker-1 value:1
D/flow-: collect DefaultDispatcher-worker-1 value:2
D/flow-: onCompletion DefaultDispatcher-worker-1
drop(n) 忽略最开始释放的值
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.drop(2)
// 结果:3 4
// 解释:
// 最开始释放的两个值(1,2)被忽略了
dropWhile
判断第一个值如果满足
(T) -> Boolean
这个条件就忽略
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.dropWhile {
it % 2 == 0
}
// 结果:1 2 3 4
// 解释:
// 第一个值不是偶数,所以1被释放
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.dropWhile {
it % 2 != 0
}
// 结果:2 3 4
// 解释:
// 第一个值是偶数,所以1被忽略
take 取指定数量的数据
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flow = flow {
emit(1)
emit(2)
emit(3)
}
GlobalScope.launch {
flow
.take(2)
.flowOn(Dispatchers.Main) //指定数数据发射的线程为主线程
.collect {
Log.d("flow-", "collect->thread:${Thread.currentThread().name} value: $it")
}
}
}
}
输出结果:
D/flow-: collect->thread:DefaultDispatcher-worker-2 value: 1
D/flow-: collect->thread:DefaultDispatcher-worker-2 value: 2
takeWhile
只会释放第一个值
,但也不是必须释放,要看条件
判断第一个值如果满足
(T) -> Boolean
这个条件就释放
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.takeWhile { it%2 != 0 }
// 结果:1
// 解释:
// 第一个值满足是奇数条件
flow {
emit(1)
emit(2)
emit(3)
emit(4)
}.takeWhile { it%2 == 0 }
// 结果:无
// 解释:
// 第一个值不满足是奇数条件
实现一个定时器功能
GlobalScope.launch {
val flow = flow {
while (true) {
emit(0)
//每隔一秒产生一个数据
delay(1000)
}
}
flow.collect {
UtilLog.d(TAG, "定时任务..")
}
}