Jetpack系列 — LiveData

Posted 网易在职程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack系列 — LiveData相关的知识,希望对你有一定的参考价值。

Jetpack系列(三) — LiveData

LiveData简单介绍

初步印象

LiveData 具有生命周期感知能力,是一种可观察的数据存储器类

LiveData 遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期,因此感知只发生在 LifecycleOwner的活跃生命周期状态

基本概念

MutableLiveDataLiveData 的实现类

MediatorLiveDataMutableLiveData 的实现类

LiveData 基本使用

简单使用

  1. 创建LiveData对象, LiveData 是一种可用于任何数据的封装容器,存放在ViewModel当中

    class HomeViewModel : ViewModel() {
    
        private var tapCount = 0
    
        private var _taps = MutableLiveData<String>("$tapCount taps")
    
        val taps: LiveData<String>
            get() = _taps
    
        fun updateTaps() {
            tapCount++
            _taps.postValue("$tapCount taps")
        }
    }
    
    
  2. 观察LiveData 对象

    // HomeFragment.kt
    private val viewModel: HomeViewModel by viewModels()
    
    // 观察Observer 
    viewModel.taps.observe(viewLifecycleOwner, ::tapsUpdate)
    
    private fun tapsUpdate(s: String?) {
        binding.tvCount.text = s ?: ""
    }
    
    
  3. 更新LiveData 对象

    • MutableLiveData类将公开 setValue(T)postValue(T)方法,
    // 触发
    binding.btnAdd.setOnClickListener {
        viewModel.updateTaps()
    }
    复制代码
    

扩展LiveData

  1. 继承LiveData,自定义LiveData 实现扩展功能,重写onActive()onInactive() 方法

    • LiveData 对象具有活跃观察者时,会调用 onActive() 方法
    • LiveData 对象没有任何活跃观察者时,会调用 onInactive()
    • setValue(T) 方法将更新 LiveData 实例的值,并将更改告知活跃观察者。
    // 官方代码
    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager: StockManager = StockManager(symbol)
    
        private val listener = { price: BigDecimal ->
            value = price
        }
    
        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }
    
        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    
        companion object {
            private lateinit var sInstance: StockLiveData
    
            @MainThread
            fun get(symbol: String): StockLiveData {
                sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
                return sInstance
            }
        }
    }
    
  2. 观察StockLiveData

    StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
    })
    

转换LiveData

  1. Lifecycle软件包会提供 Transformations类,可以根据另一个实例的值返回不同的 LiveData 实例,在将 LiveData 对象分派给观察者之前对存储在其中的值进行更改

    • map() 用于将实际包含数据的LiveData和仅用于观察数据的LiveData进行转换
    //User.kt
    data class User(var firstName: String, var lastName: String, var age: Int)
    
    //HomeViewModel.kt
    class HomeViewModel : ViewModel() {
    
        private val _userLiveData = MutableLiveData<User>()
    
        val userName: LiveData<String> = Transformations.map(_userLiveData) { user ->
            "${user.firstName} ${user.lastName}"
        }
    
        fun updateTaps() { 
            _userLiveData.postValue(User("aa", "bb", 12))
        }
    }
    
    • switchMap()对存储在 LiveData 对象中的值应用函数
    //HomeViewModel.kt
    class HomeViewModel : ViewModel() {
    
        private val _userIdLiveData = MutableLiveData<String>()
    
        val user: LiveData<User> = Transformations.switchMap(_userIdLiveData) { userId ->
            Repository.getUser(userId)
        }
    
        fun updateTaps() {
            _userIdLiveData.postValue("cc")
        }
    }
    
    // 测试
    object Repository {
        fun getUser(userId: String): LiveData<User> {
            val liveData = MutableLiveData<User>()
            liveData.value = User(userId, userId, 0)
            return liveData
        }
    }
    

合并多个 LiveData 源

  1. MediatorLiveData允许合并多个 LiveData 源,适用于一个观察者,多个被观察者

    //HomeViewModel.kt
    class HomeViewModel : ViewModel() {
        private val _user1 = MutableLiveData<User>()
        private val _user2 = MutableLiveData<User>()
    
        var mediatorLiveData: MediatorLiveData<User> = MediatorLiveData()
    
        init {
            mediatorLiveData.addSource(_user1) {
                mediatorLiveData.value = it
            }
            mediatorLiveData.addSource(_user2) {
                mediatorLiveData.value = it
            }
        }
    
        fun updateTaps() { 
            _user1.postValue(User("ll", "nn", 12))
            _user2.postValue(User("xx", "bb", 12))
        }
    }
    
    // HomeFragment.kt
    ...
    private fun subscribeUI() {
        observe(viewModel.mediatorLiveData, ::mediatorLiveDataUpdate)
    }
    
    private fun mediatorLiveDataUpdate(user: User?) {
        Log.e("==========","$user")
    }
    

相关知识点

知识点一: postValuesetValue

  • setValue 需要运行在主线程,postValue不用

     protected void postValue(T value) {
         boolean postTask;
         synchronized (mDataLock) {
             postTask = mPendingData == NOT_SET;
             mPendingData = value;
         }
         if (!postTask) {
             return;
         }
         ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
     }
    
     @MainThread
     protected void setValue(T value) {
         assertMainThread("setValue");
         mVersion++;
         mData = value;
         dispatchingValue(null);
     }
    

如果想进阶学习jetpack又缺少学习资料,而我正好薅到这本阿里十年技术专家联合打造“最新”《Jetpack架构组件入门到精通》和《Jetpack强化实战手册》,是你学习Jetpack的葵花宝典。

点击此处蓝字免费获取

《Jetpack强化实战手册》

一、初识ConstraintLayout之实现登录页面

1.创建项目
2.沉浸式的布局
3.富文本
4.属性动画

二、Navigation实践之实现APP主框架以及Navigation的相关介绍

1.搭建 Bottom Navigation Activity
2.导航界面跳转
3.Navigation传值
4.Navigation跳转动画
5.导航文件拆分
6.Deeplink导航

三、使用 Coroutines, Retrofit, Moshi实现网络数据请求

1.kotlin - Coroutine 协程
2.用协程和Retrofit实现网络请求

四、使用 TabLayout,ViewPager2 ,RecyclerView实现实现歌单广场页面

1.ViewPager2
2.TabLayout
3.RecyclerView
4.网络数据请求和数据填充
5.优化界面

五、歌单页面MVVM架构改造及其ViewModel和LiveData的使用介绍

1.MVC和MVVM介绍
2.修改歌单页面

六、Paging实现加载更多和下拉刷新,错误后重新请求

1.Paging的优势
2.Paging实现分页加载更多
3.Paging和SwipRefreshLayout组合实现下拉刷新
4.给RecyclerView添加加载状态的Footer
5.发生网络错误后重试
6.帧动画

七、vlayout嵌套横向RecyclerView和Banner 实现主页的展示,自定义Moshi的JsonAdapter

1.vlayout架构分析
2.vlayout布局介绍
3.基础工作准备
4.vlayout实现轮播图
5.字段内容类型不一致

八、Room数据库实现增删改查和事务处理

1.添加Room依赖
2.Room详细介绍
3.DataBase创建时插入数据
4.Room实现歌单标签编辑界面的增删改查

九、Room数据库Migration

1.新建 Entity
2.新建 Dao
3.修改 Database
4.修改 HomeViewModel
5.Migration

十、ExoPlayer进行视频播放的实现

1.ExoPlayer介绍
2.ExoPlayer简单的使用方法
3.ExoPlayer简单自定义
4.ExoPlayer高级自定义
5.ExoPlayer在RecyclerView中的复用

十一、MotionLayout让动画如此简单

1.MotionLayout基础
2.关键帧 Keyframes
3.代码启动动画和监听动画
4.MotionLayout案例分析

十二、Kotlin Flow基础知识详解

1.Flow 引入的场景
2.Flow 的特性
3.Flow的构造函数
4.Flow中间运算函数
5.Flow结束函数
6.Flow的错误异常处理
7.Flow的取消
8.StateFlow/MutableStateFlow

十三、Kotlin Flow项目实战-网络、数据库和UI的应用

1.MVVM架构中留给Flow的位置
2.网络数据请求
3.UI相关 - 输入框中输入关键词
4.数据库

十四、View Binding替代ButterKnife和Kotlin synthetics

1.解决findViewById的忧伤
2.View Binding的使用
3.从Kotlin synthetics迁移到View Binding

由于篇幅限制,展示了部分内容截图,需要这些文档资料的,可以点赞支持一下我,然后【点击这里】免费阅读下载哦。

以上是关于Jetpack系列 — LiveData的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack系列理解LiveData的粘性事件,并去除

Jetpack系列:LiveData入门级使用方法

Jetpack系列理解LiveData的粘性事件,并去除

Jetpack系列理解LiveData的粘性事件,并去除

Jetpack系列理解LiveData的粘性事件,并去除

4. Jetpack源码解析—LiveData的使用及工作原理