📜  Android 中的 Kotlin Flow 示例(1)

📅  最后修改于: 2023-12-03 14:59:16.005000             🧑  作者: Mango

Android 中的 Kotlin Flow 示例

在 Android 开发中,Kotlin 的 Flow API 提供了一种异步数据流处理的方式。它类似于 RxJava,但更加简洁易用,并且在 Kotlin 的协程框架中得到了天然的支持。

在本文中,我们将介绍 Kotlin Flow API 的基本用法,并提供一个示例来演示如何使用 Flow 处理数据流。

Kotlin Flow 的基本概念

Kotlin Flow 是一种异步数据流处理框架,它的核心概念是流(Flow)、发射器(Emitter)和收集器(Collector)。

  • 流(Flow):表示一个异步的数据流,可以有任意多个发射器和收集器连接到这个数据流上。
  • 发射器(Emitter):表示一个可以发射数据的对象,在 Flow 中通常使用 emit() 方法发射一些数据。
  • 收集器(Collector):表示一个可以收集数据的对象,在 Flow 中通常使用 collect() 方法接收发射器发射的数据。

使用 Kotlin Flow API 可以避免手动管理线程和避免回调地狱的问题。

Kotlin Flow 的基本用法
fun foo(): Flow<Int> = flow { 
    for (i in 1..3) {
        delay(100) // 假装我们在这里做了一些异步的事情
        emit(i) // 发射器发射数据
    }
}

上述代码中,我们定义了一个函数 foo(),该函数返回一个 Flow 对象。在该 Flow 对象中,我们通过 flow 函数使用一个流构建器(FlowBuilder)来定义流的生成方式。在这个例子中,我们简单地使用 emit() 方法发射了 1、2、3 三个数字。

接下来,我们可以使用 collect() 方法来收集这些发射的数据。

fun main() = runBlocking<Unit> {
    // 启动主协程
    println("Start")
    foo().collect { value -> println(value) }
    println("End")
}

上述代码中,我们使用了 runBlocking 函数来创建一个协程,在这个协程中,我们使用 collect() 方法来接收 foo() 函数中通过 emit() 方法发射的数据。运行上述代码,可以得到如下结果:

Start
1
2
3
End
Kotlin Flow 的特性

Kotlin Flow 有以下几个特性:

  • 可取消性(Cancellation)
  • 多次执行性(Multicasting)
  • 联结(Combining)
可取消性

Flow 在被订阅的时候会返回一个 Job 对象,可以用来取消 Flow。例如:

val job = launch {
    foo().collect { value -> 
        if (value == 2) job.cancel()
        println(value) 
    }
}

上述代码中,我们遍历 foo() 函数发射的所有数据,并且如果当前遍历的值是 2,就取消整个 launch 协程。这种可取消性是 Kotlin Flow 比较出色的一个特点。

多次执行性

Flow 每次收集数据时都会重新执行,这就保证了它的每次执行都是异步的,例如:

val flow = flow { 
    emit(1)
    delay(100)
    emit(2)
}

runBlocking {
    val job = launch {
        repeat(2) {
            println("Receiving")
            flow.collect { value -> println("Received: $value") }
            println("Done")
        }
    }
    delay(1000)
    job.cancel()
}

上述代码展示了在 Flow 中多次执行的情况,我们会收到两个 1 和两个 2

联结

Flow 可以使用 combine() 函数实现两个不同的流的联结。在流联结的过程中,将所有流中的最新值以 Function3 的形式进行操作,举例如下:

fun main() = runBlocking {
    val nums = (1..3).asFlow().onEach { delay(100) } 
    val strs = flowOf("one", "two", "three").onEach { delay(200) } 

    nums.combine(strs) { a, b -> "$a -> $b" }
        .collect { println(it) }
}

上述代码中,我们通过 combine() 函数将 numsstrs 两个流联结起来,使用 Function2 的方式将它们进行操作。运行上述代码,可以得到如下结果:

1 -> one
2 -> one
2 -> two
3 -> two
3 -> three
示例:使用 Kotlin Flow 更新 MVVM 架构

Kotlin Flow 可以很容易地和 MVVM 架构一起使用,我们可以定义一个 View Model,利用 Flow 去不断地更新 View 中的数据。

class MyViewModel : ViewModel() {
    private val _users = MutableStateFlow(emptyList<User>()) // 状态 Flow,用于发送用户数据
    val users: StateFlow<List<User>> = _users // 非可变只读状态 flow,用于观察用户数据

    init {
        viewModelScope.launch {
            // 使用 Repository 加载用户数据并发射
            userRepository.getUsers().collect { users ->
                _users.value = users
            }
        }
    }
}

class MyFragment : Fragment() {
    private val viewModel: MyViewModel by viewModels() // 获取 ViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.users.collect { users ->
            // 更新 View 显示用户数据
            adapter.submitList(users)
        }
    }
}

interface UserRepository {
    suspend fun getUsers(): Flow<List<User>>
}

上述代码中,我们定义了一个 MyViewModel 类,利用 StateFlowMutableStateFlow 发射和接收用户数据,viewModels() 函数使用了 Kotlin 的协程生命周期管理技术,在 ViewModel 销毁时自动取消协程。

在 MyFragment 中,我们通过获取 MyViewModel,观察其 users 类型的 Flow 来实时显示用户数据。

结论

Kotlin Flow API 是一个非常强大且易于使用的异步数据流框架,它可以很方便地使用协程的特性,避免回调地狱等问题。在实际开发中,我们可以使用 Kotlin Flow 和 MVVM 架构一起使用,进一步解耦程序逻辑,使开发更加简洁易用。