使用 Coroutines(协程) 和 Flow(流) 的 LiveData -第二部分:使用架构组件启动协程

31 篇文章 1 订阅
4 篇文章 0 订阅

本文是我在 2019 年 Android 开发者峰会上与 Yigit Boyar 的谈话总结的第二部分。

使用协程和流的 LiveData (ADS 2019)https://youtu.be/B8ppnjGPAGEhttps://youtu.be/B8ppnjGPAGE

第一部分:响应式 UI

第二部分:使用架构组件启动协程(本文)

第三部分:LiveData 和协程模式

Jetpack 的架构组件提供了许多快捷方式,所以您不必担心任务和取消。你只需要选择你的行动的作用域:

ViewModel 的作用域

这是启动协程最常见的方法之一,因为大多数数据操作都是从ViewModel开始的。使用 viewModelScope 扩展,当 ViewModel 被清除时,任务将被自动取消。使用 viewModelScope.launch 以启动协程。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->

class MainActivityViewModel : ViewModel {

    init {
        viewModelScope.launch {
            // 处理事情!
    
        }    
    }
}

 MainActivityViewModel.kt

Activity 和 Fragment 的作用域

类似地,如果您使用  lifecycleScope.launch,您可以将操作的作用域,限定到某个 view 的特定实例。

如果你使用 launchWhenResumedlaunchWhenStarted 或者 launchWhenCreated 将操作限制在某个生命周期状态,你甚至可以有一个更窄的作用域。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
   
class MyActivity : Activity {
    override fun onCreate(state: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            // 运行
        }

        lifecycleScope.launchWhenResumed {
            // 运行
        }
     }
 }

MyActivity.kt  

Application 的作用域

这儿有对于应用程序范围作用域的优秀案例(在这里阅读有关它的全部内容),但是,首先,如果您的任务最终必须执行,那么您应该考虑使用  WorkManager 。

ViewModel + LiveData

到目前为止,我们已经看到了如何启动协程,但还没有看到如何从协程接收结果。你可以像这样使用 MutableLiveData :

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
   
// 不要这样做。使用 liveData 代替。
class MyViewModel : ViewModel() {
    private val _result = MutableLiveData<String>()
    val result: LiveData<String> = _result

    init {
        viewModelScope.launch {
            val computationResult = doComputation()
            _result.value = computationResult
          }
      }
  }

MyViewModel.kt

但是,因为你将把这个结果暴露给你的 view,你可以通过使用 liveData coroutine builder 来少打点字儿,它会启动协程并让你通过不可变的 LiveData 来暴露结果。使用 emit() 向它发送更新。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
   
class MyViewModel : ViewModel() {
    val result = liveData {
        emit(doComputation())
    }
}

MyViewModel.kt

使用 switchMap 的 LiveData 协程构建器

在某些情况下,您希望在 LiveData 的值发生变化时启动协程。例如,在启动数据加载操作之前需要一个 ID。使用 Transformations.switchMap 有一个方便的模式:

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
   
private val itemId = MutableLiveData<String>()
val result = itemId.switchMap {
    liveData { emit(fetchItem(it)) }
}

MyViewModel.kt

result 是一个不可变的 LiveData,每当 itemId 有一个新值时,它都将使用调用 fetchItem 挂起函数的结果进行更新。

从另一个 LiveData 发出所有事项

这个特性不太常见,但也可以节省一些样板:你可以使用 emitSource 传递一个 LiveData 源。当您希望先发出一个初始值,然后再发出一系列值时,这很有用。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->

liveData(Dispatchers.IO) {
    emit(LOADING_STRING)
    emitSource(dataSource.fetchWeather())
}

MyViewModel.kt 

取消协程

如果您使用上述任何一种模式,就不必显式地取消任务。然而,有一件重要的事儿要记住:协程取消是可协作的

这意味着,如果调用协程被取消,您必须帮助 Kotlin 停止任务。假设您有一个启动无限循环的挂起函数。Kotlin 无法为您停止该循环,因此您需要协作,定期检查任务是否处于活动状态。您可以通过检查 isActive 属性来实现这一点。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
   
suspend fun printPrimes() {
    while(isActive) {
        // 计算
    }
}

PrimeFinder.kt 

顺便说一下,如果你使用 kotlinx 中的任何函数。协程(比如 delay),你应该知道它们都是可取消的,这意味着它们会为你进行检查。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
suspend fun printPrimes() {
    while(true) { // 大概没问题,因为我们再里面调用了delay
        // 计算
        delay(1000)
    }
}

PrimeFinder.kt 

尽管如此,我还是建议您添加检查,因为可能会有人在将来删除延迟调用,从而在您的代码中引入一个微妙的错误。

一次性 vs 多个值

为了理解协程(以及与此相关的响应式 UI),我们需要区分以下两种情况:

  • 一次性操作:它们只运行一次并可以返回一个结果
  • 返回多个值的操作:对可以随时间发出多个值的数据源的订阅。

Twitter app 显示的部分 UI 需要不同类型的操作。转发和点赞会随着时间的推移而更新。

使用了协程的一次性操作

使用挂起函数并使用 viewModelScope 或 liveData{} 调用它们是一种非常方便的运行非阻塞操作的方法。

<!-- Copyright 2020 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 -->
class MyViewModel {
    val result = liveData {
        emit(repository.fetchData())
    }
}

MyViewModel.kt 

然而,当我们监听变化时,事情会变得更加复杂。

使用 LiveData 接收多个值

我在 ViewModel 之外的 LiveData (2018)中讨论了这个主题,在这个文章里我讨论了可以用来解决 LiveData 从未被设计为全功能 streams 构建器这一事实的模式。

App 的 表示层(绿色) 和 数据层(蓝色) 使用 LiveData 进行通信

现在,更好的方法是使用 Kotlin 的 Flow(警告:某些部分仍处于实验阶段)。Flow 类似于 RxJava 中的响应式 streams 特性。

然而,虽然协程使非阻塞的一次性操作变得更容易,但对于 Flow 来说却不是这样。streams 仍然难以把握。但是,如果你想要创建快速且可靠的响应式 UI,我认为这是值得投入时间的。由于它是语言的一部分,并且是一个小的依赖项,许多库开始添加 Flow 支持(比如 Room)。

因此,我们可以公开来自 Data Source 和 Repository 的 Flow,而不是 LiveData,但是 ViewModel 仍然公开 LiveData,因为它是可以感知生命周期的。

在数据层使用 Flow 代替 LiveData 进行通信

接下来是 第三部分:LiveData 和协程模式

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值