Jetpack——LiveData源码解析

本文深入解析Android Jetpack中的LiveData,探讨其作为具有生命周期感知能力的数据存储器的工作原理。通过源码分析,展示了LiveData如何与ViewModel和Lifecycle配合,以及如何处理数据粘性问题。文中提供多个使用示例,并详细解释了数据更新和事件绑定的流程。
摘要由CSDN通过智能技术生成

一、LiveData 是什么?

官方也给了介绍说明,传送门:LiveData

LiveData 通常配合 ViewModel 一起用,一半的功能依靠 Lifecycle 实现,是一种可观察的数据存储器。与常规的可观察类不同,LiveData 具有生命周期感知能力,遵循其他应用组件的生命周期,这种感知力确保其处于活跃生命周期装填的应用组件观察者,通俗的讲就是 LiveData 自己充当两个角色,既是观察者,也是被观察者,可以实现双向绑定,既可以存也可以修改,数据驱动UI,STARTEDRESUMED 状态才会工作。

所以要掌握 LiveData 一定要明白 Lifecycle 的内容,可以参考上一篇 Lifecycle 的分析

二、使用

示例1

该示例演示最简单的使用,先定义一个懒加载的 LiveData 数据单例类

object MyLiveData {
   
    val info1: MutableLiveData<String> by lazy {
    MutableLiveData() }
}

在 Activity 里面使用非常简单:

/**
 * 触发与改变
 */
class MainActivity : AppCompatActivity() {
   

    override fun onCreate(savedInstanceState: Bundle?) {
   
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_livedata)

        // 实际项目中一般使用DataBinding
        val textView: TextView = findViewById(R.id.tv_textview)

        // 1.观察者 kotlin特性lambda简写
        MyLiveData.info1.observe(this, {
   
            textView.text = it// 数据驱动UI
        })
        // 完整写法
        /*MyLiveData.info1.observe(this, object : Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t
            }
        })*/

        // 2. 触发数据改变
        
        // 主线程修改数据setValue 
        MyLiveData.info1.value = "default"

        // 子线程异步操作postValue
        thread {
   
            Thread.sleep(3000)
            MyLiveData.info1.postValue("3秒钟修改数据")
        }
        thread {
   
            Thread.sleep(6000)
            MyLiveData.info1.postValue("6秒钟修改数据")
        }
    }
}

示例2

这个示例演示只在界面可见的情况刷新UI数据。

同样的,先定义一个懒加载的 LiveData 数据单例类

object MyLiveData {
   
    val data1: MutableLiveData<String> by lazy {
    MutableLiveData() }

    init {
   
        data1.postValue("init data")
        //data1.value = "default"
    }
    /*
    如果是主线程中初始化,用setvalue就会崩溃:
    2021-11-16 16:32:41.651 12633-12780/sun.geoffery.myjetpack E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: sun.geoffery.myjetpack, PID: 12633
    java.lang.ExceptionInInitializerError
        at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:18)
        at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:15)
        at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
     Caused by: java.lang.IllegalStateException: Cannot invoke setValue on a background thread
        at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
        at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
        at sun.geoffery.myjetpack.livedata.simple2.MyLiveData.<clinit>(MyLiveData.kt:10)
        at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:18) 
        at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:15) 
        at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30) 
        LiveData:assertMainThread("setValue");
    */
}

设计一个后台服务,模拟服务端连续不断地推送过来消息:

/**
 * Service服务是主线程
 */
class MyService : Service() {
   

    override fun onBind(p0: Intent?): IBinder? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
   
        thread {
   
            for (str in 1..100000) {
   
                Log.d("server", "服务器push消息(模拟滴滴提示音),内容是:${
     str}")
                MyLiveData.data1.postValue("服务器推来了消息,内容是:${
     str}")
                Thread.sleep(2000)
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

最后在界面应用:

/**
 * 只在界面可见的情况刷新UI数据
 */
class MainActivity : AppCompatActivity() {
   

    override fun onCreate(savedInstanceState: Bundle?) {
   
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_livedata2)

        val button: Button = findViewById(R.id.button)
        button.setOnClickListener {
   // 启动服务
            startService(Intent(this, MyService::class.java))
            Toast.makeText(MainLivedata2Activity@ this, "", Toast.LENGTH_SHORT).show()
        }

		// 如果在这里初始化了,LiveData 的init必须异步线程操作数据
        MyLiveData.data1.observe(this, {
   
            Log.d("server", "界面可见说明用户再鼓捣手机,更新消息界面:${
     it}")
            Toast.makeText(MainLivedata2Activity@ this, "更新界面UI成功:${
     it}", Toast.LENGTH_SHORT).show()
        })
    }
}

示例3

该示例展示 LiveData 的数据粘性问题,先来一个数据源:

object MyLiveData {
   
    val value1: MutableLiveData<String> by lazy {
    MutableLiveData() }
}

主界面很简单,就是一个页面跳转,跳转前做了一个数据更新:


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小山研磨代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值