Bootstrap

Android开发实战班 - 应用架构 - LiveData/Flow 数据流

在 MVVM 架构中,数据流是连接 ViewModel 和 View 的重要桥梁,用于实现数据的观察和响应。Jetpack 提供了两种主要的数据流机制:LiveDataFlow。本章节将深入讲解 LiveData 和 Flow 的概念、使用方法、区别以及在实际开发中的应用场景,帮助学员掌握数据流的应用。

数据流概述

  • 数据流的作用:

    • 数据流用于在 ViewModel 和 View 之间传递数据,实现数据的观察和响应。
    • 数据流可以感知生命周期变化,避免内存泄漏。
  • 数据流的优势:

    • 生命周期感知: 数据流可以感知 View 的生命周期,自动管理订阅和取消订阅,避免内存泄漏。
    • 响应式编程: 数据流采用响应式编程范式,数据变化时自动通知观察者更新 UI。
    • 线程安全: 数据流支持线程切换,可以在不同线程之间安全地传递数据。

11.2 LiveData

LiveData 是 Jetpack 提供的一种可观察的数据持有者类,具有生命周期感知能力。LiveData 适用于需要在 ViewModel 和 View 之间传递数据的场景。

11.2.1 LiveData 的特点
  • 生命周期感知: LiveData 会自动感知 View 的生命周期变化,避免内存泄漏。
  • 自动解绑: 当 View 处于销毁状态时,LiveData 会自动取消订阅。
  • 线程安全: LiveData 可以在主线程或后台线程中更新数据,观察者会在主线程中接收数据更新。
  • 数据持有: LiveData 可以持有数据,并在数据变化时通知观察者。
11.2.2 LiveData 的使用
  • 创建 LiveData:

    class MyViewModel : ViewModel() {
        private val _data = MutableLiveData<String>()
        val data: LiveData<String> get() = _data
    
        fun updateData(newData: String) {
            _data.value = newData
        }
    }
    
  • 观察 LiveData:

    class MyActivity : AppCompatActivity() {
        private lateinit var viewModel: MyViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
            viewModel.data.observe(this) { data ->
                // 更新 UI
                findViewById<TextView>(R.id.textView).text = data
            }
    
            // 更新数据
            viewModel.updateData("Hello, LiveData!")
        }
    }
    
  • 使用 Transformation:

    • LiveData 提供了 Transformation 类,可以对 LiveData 进行转换,例如 map, switchMap 等。
      val transformedData: LiveData<String> = Transformations.map(data) { originalData ->
          // 转换数据
          originalData.toUpperCase()
      }
      
  • 使用 MediatorLiveData:

    • MediatorLiveData 可以合并多个 LiveData 数据源。
      val mediatorLiveData = MediatorLiveData<String>()
      mediatorLiveData.addSource(liveData1) { value ->
          mediatorLiveData.value = value
      }
      mediatorLiveData.addSource(liveData2) { value ->
          mediatorLiveData.value = value
      }
      
11.2.3 LiveData 的优点和缺点
  • 优点:

    • 简单易用,易于上手。
    • 生命周期感知,避免内存泄漏。
    • 线程安全。
  • 缺点:

    • 功能相对有限,缺乏复杂的操作符。
    • 不支持背压(backpressure)。

11.3 Flow

Flow 是 Kotlin 协程提供的一种异步数据流机制,适用于需要复杂数据处理的场景。Flow 提供了丰富的操作符,可以对数据进行转换、过滤、合并等操作。

11.3.1 Flow 的特点
  • 异步处理: Flow 是异步的,可以在不同的协程中处理数据。
  • 丰富的操作符: Flow 提供了丰富的操作符,例如 map, filter, flatMap, zip 等。
  • 背压支持: Flow 支持背压,可以处理数据流中的数据积压问题。
  • 协程支持: Flow 依赖于 Kotlin 协程,可以与协程的其他功能无缝集成。
11.3.2 Flow 的使用
  • 创建 Flow:

    class MyViewModel : ViewModel() {
        private val _dataFlow = MutableStateFlow<String>("")
        val dataFlow: StateFlow<String> get() = _dataFlow
    
        fun updateData(newData: String) {
            _dataFlow.value = newData
        }
    }
    
  • 观察 Flow:

    class MyActivity : AppCompatActivity() {
        private lateinit var viewModel: MyViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
            lifecycleScope.launch {
                viewModel.dataFlow.collect { data ->
                    // 更新 UI
                    findViewById<TextView>(R.id.textView).text = data
                }
            }
    
            // 更新数据
            viewModel.updateData("Hello, Flow!")
        }
    }
    
  • 使用操作符:

    • Flow 提供了丰富的操作符,可以对数据进行转换、过滤、合并等操作。
      val transformedFlow: Flow<String> = dataFlow.map { originalData ->
          // 转换数据
          originalData.toUpperCase()
      }
      
      val filteredFlow: Flow<String> = dataFlow.filter { data ->
          // 过滤数据
          data.isNotEmpty()
      }
      
  • 使用 CoroutineScope:

    • Flow 的观察需要在协程中进行,可以使用 lifecycleScopeviewModelScope 来管理协程生命周期。
      lifecycleScope.launch {
          viewModel.dataFlow.collect { data ->
              // 更新 UI
          }
      }
      
11.3.3 LiveData 与 Flow 的比较
  • 生命周期感知:

    • LiveData 内置生命周期感知能力,而 Flow 需要手动管理生命周期。
    • 可以使用 lifecycleScopeviewModelScope 来管理 Flow 的生命周期。
  • 数据处理:

    • Flow 提供了丰富的操作符,可以进行复杂的数据处理。
    • LiveData 的操作符相对有限。
  • 线程支持:

    • LiveData 主要在主线程中更新数据。
    • Flow 支持在不同的协程中处理数据,可以更灵活地进行线程切换。
  • 背压支持:

    • Flow 支持背压,可以处理数据流中的数据积压问题。
    • LiveData 不支持背压。
11.3.4 Flow 在 Jetpack Compose 中的应用

在 Jetpack Compose 中,可以使用 collectAsState 函数将 Flow 转换为 Compose 的 State,从而实现数据驱动 UI。

  • 示例:

    @Composable
    fun MyComposable(viewModel: MyViewModel) {
        val data by viewModel.dataFlow.collectAsState()
    
        Text(text = data)
    }
    
  • 完整示例:

    class MyViewModel : ViewModel() {
        private val _dataFlow = MutableStateFlow<String>("")
        val dataFlow: StateFlow<String> get() = _dataFlow
    
        fun updateData(newData: String) {
            _dataFlow.value = newData
        }
    }
    
    @Composable
    fun MyComposable(viewModel: MyViewModel) {
        val data by viewModel.dataFlow.collectAsState()
    
        Column {
            Text(text = data)
            Button(onClick = { viewModel.updateData("Hello, Flow!") }) {
                Text(text = "Update Data")
            }
        }
    }
    

11.4 实战案例

  1. 案例一:使用 LiveData 实现简单的计数器应用

    • 创建一个 ViewModel,使用 LiveData 存储计数器的值。
    • 在 Activity 中观察 LiveData,更新 UI。
    • 创建一个按钮,点击按钮更新计数器的值。
  2. 案例二:使用 Flow 实现图片浏览应用

    • 创建一个 ViewModel,使用 Flow 存储图片列表。
    • 在 Jetpack Compose Composable 中使用 collectAsState 观察 Flow,更新 UI。
    • 创建一个按钮,点击按钮加载图片列表。

11.5 课后作业

  1. 任务一:使用 LiveData 实现用户列表应用

  2. 任务二:使用 Flow 实现图片浏览应用

;