在 Kotlin 协程 Coroutine 中 , 使用 suspend 挂起函数 以异步的方式 返回单个返回值肯定可以实现 , 参考 【Kotlin 协程】协程的挂起和恢复 ① ( 协程的挂起和恢复概念 | 协程的 suspend 挂起函数 ) 博客 ;
如果要 以异步的方式 返回多个元素的返回值 , 可以使用如下方案 :
同步调用返回集合和序列代码示例 : 同步调用函数时 , 如果函数耗时太长或者中途有休眠 , 则会阻塞主线程导致 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
尝试使用 挂起函数 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
参数 , 该参数是一个函数 , 该函数 () -> Unit
是 SequenceScope
类型的扩展函数 ;
任意传入一个匿名函数 , 该函数被自动设置为 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