Kotlin 协程Flow 操作符 ① ( 过渡操作符 | map 操作符 | transform 操作符 | 限长操作符 | take 操作符 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程Flow 操作符 ① ( 过渡操作符 | map 操作符 | transform 操作符 | 限长操作符 | take 操作符 )相关的知识,希望对你有一定的参考价值。

文章目录





一、过渡操作符



过渡操作符 相关概念 :

  • 转换流 : 使用 过渡操作符 转换 Flow 流 ;
  • 作用位置 : 过渡操作符作用 于 流的上游 , 返回 流的下游 ;
  • 非挂起函数 : 过渡操作符 不是挂起函数 , 属于冷操作符 ;
  • 运行速度 : 过渡操作符 可以 快速返回 新的 转换流 ;

1、map 操作符


通过 map 操作符 , 可以操作每个元素 , 将元素转为另外一种类型的元素 ;

map 操作符原型 :

/**
 * 返回一个流,其中包含对原始流的每个值应用给定[transform]函数的结果。
 */
public inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R> = transform  value ->
   return@transform emit(transform(value))

代码示例 : 将 Flow 中发射的 Int 元素 转为 字符串 ; 通过 map 操作符 , 将 Int 类型的元素 转为 字符串类型 元素 ;

package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            (0..3).asFlow()
                        // 通过 map 操作符将 Int 转为字符串元素
                        .map 
                            stringConvert(it)
                        
                        .collect 
                            println("collect : $it")
                        
        
    

    // 将 Int 转为 字符串
    suspend fun stringConvert(num: Int): String 
        delay(1000)
        return "convert $num"
    

执行结果 :

2022-12-26 11:28:07.370 23972-23972/kim.hsl.coroutine I/System.out: collect : convert 0
2022-12-26 11:28:08.371 23972-23972/kim.hsl.coroutine I/System.out: collect : convert 1
2022-12-26 11:28:09.412 23972-23972/kim.hsl.coroutine I/System.out: collect : convert 2
2022-12-26 11:28:10.452 23972-23972/kim.hsl.coroutine I/System.out: collect : convert 3


2、transform 操作符


通过 transform 操作符 , 可以操作每个元素 , 可以在单个元素处理时 , 发射多次元素 ;

transform 操作符原型 :

/**
 * 将[transform]函数应用到给定流的每个值。
 *
 * ' transform '的接收者是[FlowCollector],因此' transform '是一个
 * 灵活的函数,可以转换发出的元素,跳过它或多次发出它。
 *
 * 该操作符泛化了[filter]和[map]操作符和
 * 可以用作其他操作符的构建块,例如:
 *
 * ```
 * fun Flow<Int>.skipOddAndDuplicateEven(): Flow<Int> = transform  value ->
 *     if (value % 2 == 0)  // Emit only even values, but twice
 *         emit(value)
 *         emit(value)
 *      // Do nothing if odd
 * 
 * ```
 */
public inline fun <T, R> Flow<T>.transform(
    @BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow  // 注意:这里使用的是安全流,因为收集器对每个操作的转换都是公开的
    collect  value ->
        // 没有它,单元将被退回,TCE将不会生效,KT-28938
        return@collect transform(value)
    


代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            (0..3).asFlow()
                        .transform  it ->
                            // 在 transform 操作符中发射 2 个元素
                            emit(it)
                            emit(stringConvert(it))
                        
                        .collect 
                            println("collect : $it")
                        
        
    

    // 将 Int 转为 字符串
    suspend fun stringConvert(num: Int): String 
        delay(1000)
        return "convert $num"
    

执行结果 :

2022-12-26 11:38:14.091 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : 0
2022-12-26 11:38:14.091 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : 0
2022-12-26 11:38:15.146 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : convert 0
2022-12-26 11:38:16.189 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : convert 0
2022-12-26 11:38:16.189 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : 1
2022-12-26 11:38:16.189 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : 1
2022-12-26 11:38:17.229 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : convert 1
2022-12-26 11:38:18.269 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : convert 1
2022-12-26 11:38:18.270 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : 2
2022-12-26 11:38:18.270 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : 2
2022-12-26 11:38:19.309 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : convert 2
2022-12-26 11:38:20.349 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : convert 2
2022-12-26 11:38:20.349 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : 3
2022-12-26 11:38:20.349 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : 3
2022-12-26 11:38:21.389 25902-25902/kim.hsl.coroutine I/System.out: 接收元素 : convert 3
2022-12-26 11:38:22.429 25902-25902/kim.hsl.coroutine I/System.out: 发射元素 : convert 3





二、限长操作符 ( take 操作符 )



通过 take 操作符 , 可以选择选取指定个数的发射元素 ;

如 : 在 Flow 流中发射了 4 个元素 , 但是调用了 Flow#take(2) , 只收集其中 2 个元素 ;

take 操作符原型 :

/**
 * 返回包含第一个[count]元素的流。
 * 当[count]元素被消耗时,原始流将被取消。
 * 如果[count]不是正数,抛出[IllegalArgumentException]。
 */
public fun <T> Flow<T>.take(count: Int): Flow<T> 
    require(count > 0)  "Requested element count $count should be positive" 
    return unsafeFlow 
        var consumed = 0
        try 
            collect  value ->
                // 注意:这个for take不是故意用collectWhile写的。
                // 它首先检查条件,然后对emit或emitAbort进行尾部调用。
                // 这样,正常的执行不需要状态机,只需要终止(emitAbort)。
                // 有关不同方法的比较,请参阅“TakeBenchmark”。
                if (++consumed < count) 
                    return@collect emit(value)
                 else 
                    return@collect emitAbort(value)
                
            
         catch (e: AbortFlowException) 
            e.checkOwnership(owner = this)
        
    


代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            (0..3).asFlow()
                        .take(2)
                        .collect 
                            println("接收元素 : $it")
                        
        
    

执行结果 :

2022-12-26 11:42:25.560 27701-27701/kim.hsl.coroutine I/System.out: 接收元素 : 0
2022-12-26 11:42:25.560 27701-27701/kim.hsl.coroutine I/System.out: 接收元素 : 1

以上是关于Kotlin 协程Flow 操作符 ① ( 过渡操作符 | map 操作符 | transform 操作符 | 限长操作符 | take 操作符 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程Flow 流异常处理 ( 收集元素异常处理 | 使用 try...catch 代码块捕获处理异常 | 发射元素时异常处理 | 使用 Flow#catch 函数捕获处理异常 )

Kotlin 协程Flow 流异常处理 ( 收集元素异常处理 | 使用 try...catch 代码块捕获处理异常 | 发射元素时异常处理 | 使用 Flow#catch 函数捕获处理异常 )

Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )

Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )

Kotlin协程之flow工作原理

Kotlin协程之flow工作原理