Flow作为Android开发中的重要的效果。尤其在Jetpack Compose里左一个collect,右一个collect。不告知Flow而开发Android是步履维艰。作为一个入门文章,假如你还不是很了解Flow的话,本文可以带你更进一步的了解Flow。
Flow是一个异步数据流,它会宣告数据给收集者,终究带或者不带异常的完成任务。下面我们经过比如来学习。
假定我们正在下载一幅图片。在下载的时分,还要把下载的百分比作为值宣告来,比如:1%,2%,3%,等。收集者(collector)会接收到这些值并在界面上以适宜的方法显现出来。但是假如出现网络问题,任务也会因此中止。
现在我们来看一下Flow里的几个API:
- 流构建器(Flow builder)
- 操作符(Operator)
- 收集器(Collector)
流构建器
简略来说,它会实行一个任务并把值宣告来,有时也会只宣告值而不会实行什么任务。比如简略的宣告一些数字值。你可以把流构建器作为一个发言人。这个发言人会考虑(做任务)和说(宣告值).
操作符
操作符可以帮忙转化数据。
我们可以把操作符作为是一个翻译。一个发言人说了法语,但是听众(收集器)只能听懂英语。这就需要一个翻译来帮忙了。它可以把法语都翻译成英语让听众了解。
当然,操作符可以做的远不止这些。以上的比如只是帮忙了解。
收集器
Flow宣告的值经过操作符的处理之后会被收集器收集。
收集器可以作为是收听者。实践上收集器也是一种操作符,它有时被称作终端操作符。
第一个比如
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
方法把流构建器和收集器连到了一起,这个方法调用之后流就开端实行了。
流构建器的不同类型
流构建器有四种:
-
flowOf()
:从一个给定的数据集结生成流 -
asFlow()
: 一个扩展方法,可以把某个类型转化成流 -
flow{}
: 我们比如中运用的方法 -
channelFlow{}
:运用结构器自带的send
方法发送的元素构建流
例如:
flowOf()
flowOf(4, 2, 5, 1, 7)
.collect {
Log.d(TAG, it.toString())
}
asFlow()
(1..5).asFlow()
.collect {
Log.d(TAG, it.toString())
}
flow{}
flow {
(0..10).forEach {
emit(it)
}
}
.collect {
Log.d(TAG, it.toString())
}
channelFlow{}
channelFlow {
(0..10).forEach {
send(it)
}
}
.collect {
Log.d(TAG, it.toString())
}
flowOn
操作符
flowOn
这个操作符可以控制flow任务实行的线程的类型。在Android里一般是在一个后台线程实行任务,之后在界面上更新结果。
下面的比如里加了一个500毫秒的延迟来模拟实践任务。
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主要有这些类型:IO,Default,Main。flowOn和CoroutineScope都可以运用Dispatchers来实行任务实行的“线程”(暂且这么了解)。
运用流结构器
我们经过几个比如学习。
移动文件
这儿我们用流结构器新建一个流,让流任务在后台线程实行。完成后在UI线程显现状况。
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线程更新下载的百分比。
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
这类的把流的值规约到单个值的操作符。
比如:
val sum = (1..5).asFlow()
.map { it * it } // 数字 1 至 5 的平方
.reduce { a, b -> a + b } // 求和(结尾操作符)
println(sum)
今天份先更新到这了。to be continued…