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

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )相关的知识,希望对你有一定的参考价值。

文章目录





一、以异步返回返回多个返回值



在 Kotlin 协程 Coroutine 中 , 使用 suspend 挂起函数 以异步的方式 返回单个返回值肯定可以实现 , 参考 【Kotlin 协程】协程的挂起和恢复 ① ( 协程的挂起和恢复概念 | 协程的 suspend 挂起函数 ) 博客 ;

如果要 以异步的方式 返回多个元素的返回值 , 可以使用如下方案 :

  • 集合
  • 序列
  • Suspend 挂起函数
  • Flow 异步流




二、同步调用返回多个值的弊端



同步调用返回集合和序列代码示例 : 同步调用函数时 , 如果函数耗时太长或者中途有休眠 , 则会阻塞主线程导致 ANR 异常 ;

package kim.hsl.coroutine

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

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

        // 同步方法返回多个值
        // 调用 " 返回 List 集合的函数 " , 并遍历返回值
        listFunction().forEach 
            // 遍历打印集合中的内容
            println(it)
        

        // 同步调用 " 返回 Sequence 序列 " 时 , 线程会阻塞
        sequenceFunction().forEach 
            // 遍历打印序列中的内容
            println(it)
        

    

    /**
     * 返回 List 集合的函数
     */
    fun listFunction(): List<Int> = listOf(0, 1, 2)

    /**
     * 返回 Sequence 序列
     */
    fun sequenceFunction(): Sequence<Int> = sequence 
        for (i in 3..5) 
            // 每隔 0.5 秒向序列中存入一个值
            Thread.sleep(500)
            yield(i)
        
    

执行结果 :

2022-12-22 12:33:03.122 15427-15427/kim.hsl.coroutine I/System.out: 0
2022-12-22 12:33:03.123 15427-15427/kim.hsl.coroutine I/System.out: 1
2022-12-22 12:33:03.123 15427-15427/kim.hsl.coroutine I/System.out: 2
2022-12-22 12:33:03.661 15427-15427/kim.hsl.coroutine I/System.out: 3
2022-12-22 12:33:04.177 15427-15427/kim.hsl.coroutine I/System.out: 4
2022-12-22 12:33:04.703 15427-15427/kim.hsl.coroutine I/System.out: 5





三、尝试在 sequence 中调用挂起函数返回多个返回值



尝试使用 挂起函数 kotlinx.coroutines.delay 进行休眠 , 这样在挂起时 , 不影响主线程的其它操作 , 此时会报如下错误 ;

Restricted suspending functions can only invoke member or extension suspending functions 
on their restricted coroutine scope

受限挂起函数只能在其受限的协程范围上调用成员或扩展挂起函数

下面分析上述报错原因 :

sequence 函数中 , 传入的是 @BuilderInference block: suspend SequenceScope<T>.() -> Unit 参数 , 该参数是一个函数 , 该函数 () -> UnitSequenceScope 类型的扩展函数 ;

任意传入一个匿名函数 , 该函数被自动设置为 SequenceScope 类的扩展函数 , 在其中的任何调用都默认调用的是 SequenceScope 对象的方法 ;

在该匿名函数中 , 不能调用 SequenceScope 之外定义的挂起函数 , 这样做是为了保证该类的执行性能 ;

/**
 * 构建一个[Sequence],一个接一个地懒惰地产生值。
 *
 * @see kotlin.sequences.generateSequence
 *
 * @sample samples.collections.Sequences.Building.buildSequenceYieldAll
 * @sample samples.collections.Sequences.Building.buildFibonacciSequence
 */
@SinceKotlin("1.3")
public fun <T> sequence(@BuilderInference block: suspend SequenceScope<T>.() -> Unit): Sequence<T> = Sequence  iterator(block) 

SequenceScope 类上 , 有一个 @RestrictsSuspension 注解 , RestrictsSuspension 注解的作用是 限制挂起 , 在该类中不能调用其它的挂起函数 , 这样可以保证序列的执行性能 ;

@RestrictsSuspension
@SinceKotlin("1.3")
public abstract class SequenceScope<in T> internal constructor() 
    public abstract suspend fun yield(value: T)

    public abstract suspend fun yieldAll(iterator: Iterator<T>)

    public suspend fun yieldAll(elements: Iterable<T>) 
        if (elements is Collection && elements.isEmpty()) return
        return yieldAll(elements.iterator())
    

    public suspend fun yieldAll(sequence: Sequence<T>) = yieldAll(sequence.iterator())

sequence 方法中传入一个函数 , 该函数就会变成 SequenceScope 的扩展函数 , SequenceScope 类中的扩展函数是限制挂起的 , 只要是 SequenceScope 中 , 如果要调用挂起函数 , 只能调用其已有的挂起函数 , 如 : yield , yieldAll , 函数等 , 不能调用其它挂起函数 ;

RestrictsSuspension 注解的作用是 限制挂起 ;

/**
 * 当用作扩展挂起函数的接收器时,标记有此注释的类和接口受到限制。
 * 这些挂起扩展只能调用该特定接收器上的其他成员或扩展挂起函数,并且不能调用任意挂起函数。
 */
@SinceKotlin("1.3")
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension




四、协程中调用挂起函数返回集合



如果要 以异步方式 返回多个返回值 , 可以在协程中调用挂起函数返回集合 , 但是该方案只能一次性返回多个返回值 , 不能持续不断的 先后 返回 多个 返回值 ;

代码示例 :

package kim.hsl.coroutine

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

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

        // 携程中调用挂起函数返回多个值
        // 调用 " 返回 List 集合的挂起函数 " , 并遍历返回值
        runBlocking 
            listFunction().forEach 
                // 遍历打印集合中的内容
                println(it)
            
        
    

    /**
     * 返回 List 集合的函数
     */
    suspend fun listFunction(): List<Int> 
        delay(500)
        return listOf(0, 1, 2)
    

执行结果 :

2022-12-22 13:37:00.191 26649-26649/kim.hsl.coroutine I/System.out: 0
2022-12-22 13:37:00.191 26649-26649/kim.hsl.coroutine I/System.out: 1
2022-12-22 13:37:00.191 26649-26649/kim.hsl.coroutine I/System.out: 2

以上是关于Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )

Kotlin 协程Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )

Kotlin 协程Flow 异步流 ④ ( 流的构建器函数 | flow 构建器函数 | flowOf 构建器函数 | asFlow 构建器函数 )

Kotlin 协程Flow 异步流 ④ ( 流的构建器函数 | flow 构建器函数 | flowOf 构建器函数 | asFlow 构建器函数 )

Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )

Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )