当前位置: 首页 > article >正文

Android Livedata源码解析

文章目录

  • 前言
  • 一、LiveData 简介
    • 1. 什么是 LiveData?
    • 2. LiveData 的特点和优势
      • 特点
      • 优势 ( 适用场景 )
  • 二、LiveData 的架构设计
  • 三、源码解析
    • 1. 基本使用
    • 2. observe() 的内部逻辑:观察者绑定
    • 3. 数据更新:setValue() 和 postValue() 的区别
    • 4. 数据分发机制:dispatchingValue() 和 considerNotify()
    • 5. 生命周期感知:LifecycleBoundObserver 的实现
  • 四、LiveData"粘包"
    • 1. 多线程并发更新
    • 2. 重复发送数据事件
    • 3. 高频数据更新
    • 4. 生命周期管理不当
    • 解决方案
      • 1. 使用 Mutex 锁来保证线程安全:
      • 2. 使用 SingleLiveEvent 来避免重复发送:
      • 3. 使用 Flow 来控制更新频率:
  • 总结


前言

LiveData 的设计初衷,强调其生命周期感知、简化UI组件的数据更新和内存管理,适用于ViewModel数据共享的场景。
下面首先我会先介绍LiveData是什么,然后再介绍源码部分。 想看源码的可以直接跳到源码解析部分

一、LiveData 简介

1. 什么是 LiveData?

LiveData 是一个具有生命周期感知的数据持有类,可帮助 Android UI 组件在数据变化时自动更新。说明它是如何依赖 Android 架构组件库中的 ViewModel 和 Lifecycle,实现数据共享和观察者管理。

2. LiveData 的特点和优势

特点

  • 生命周期感知:LiveData 自动根据观察者(如 Activity 或
    Fragment)的生命周期进行管理,仅在界面活跃时通知数据更新,避免不必要的 UI 操作和内存泄漏。

  • 数据持有类:LiveData 本身是一个数据持有类,能够存储数据。可以在 ViewModel 中创建一个 LiveData 实例来持有数据,使得数据能在配置变更时保持不变。

  • 自动更新 UI(与DataBinding 配合使用):当 LiveData 数据发生变化时,它会自动通知注册的观察者更新 UI,减少手动调用 UI 更新逻辑的需求。

  • 线程安全:LiveData 内部处理了线程同步,确保在主线程中更新 UI 时不会遇到线程安全问题。

优势 ( 适用场景 )

  • 减少内存泄漏:LiveData 能够观察 UI 组件的生命周期,仅在组件活跃状态下更新数据,降低了因生命周期不当导致的内存泄漏风险。

  • 简化 UI 控制逻辑:LiveData 自动管理数据状态变化,使得 UI 与数据同步变得轻松,代码逻辑更清晰,易于维护。

  • 应对配置变更:与 ViewModel 配合使用,LiveData 能在配置变更时(如旋转屏幕)保存数据状态,避免重新加载和数据丢失的问题。

  • 实时数据响应:LiveData 可以直接连接到数据库(例如 Room),实现数据实时响应,数据库更新时自动刷新UI,不需手动监听数据库变化。

  • 线程支持:LiveData 提供 postValue 和setValue,可以分别在子线程和主线程中更新数据,帮助在异步任务完成后切换到主线程更新 UI。

二、LiveData 的架构设计

LiveData 的架构设计围绕着生命周期感知数据订阅发布机制以及数据持有进行设计,旨在简化 UI 层的数据管理,确保数据在不同生命周期阶段的合理更新。
分析源码前 我们先看看LiveData的UML类图:

注:如果不清楚UML关系类图的可以看看下面链接,欢迎大佬们莅临指导
链接: UML类图绘制规则
在这里插入图片描述
类图详细说明

  • LifecycleOwner
    关系:与 Lifecycle 是关联关系,通过 getLifecycle() 方法获取 Lifecycle 对象。
    功能:表示具备生命周期的组件,提供 getLifecycle() 方法以获取其生命周期状态,允许 LiveData 自动管理观察者的活跃状态。

  • Lifecycle
    关系:LifecycleOwner 拥有 Lifecycle;Lifecycle 聚合 LifecycleObserver。
    功能:管理生命周期事件并通知观察者,通过 addObserver() 和 removeObserver() 方法添加或移除观察者。getCurrentState() 方法提供当前的生命周期状态。

  • LifecycleObserver
    关系:LiveData 通过 LifecycleBoundObserver 实现 LifecycleObserver,用来监听 Lifecycle 的状态变化。
    功能:用于观察 Lifecycle 的状态,在 LiveData 中自动管理观察者的活跃性。LifecycleObserver 可以跟踪 LifecycleOwner 的生命周期事件,自动激活和暂停观察者。

  • LiveData
    关系:与 LifecycleObserver 是组合关系:LiveData 使用内部类 LifecycleBoundObserver 管理 LifecycleOwner 的状态。与 Observer 是依赖关系:LiveData 依赖 Observer,通过 observe() 注册观察者。
    功能:提供观察数据的功能,当数据变化时通知观察者。包括 observe() 方法绑定带有 LifecycleOwner 的观察者,observeForever() 方法绑定无生命周期的观察者。

  • LifecycleBoundObserver
    关系:LifecycleBoundObserver 是 LiveData 的内部类,继承自 LifecycleObserver。
    功能:LifecycleBoundObserver 将 Observer 与特定的 LifecycleOwner 绑定,使得 LiveData 根据 Lifecycle 状态控制观察者的激活或暂停。

  • Observer
    关系:与 LiveData 是依赖关系,通过 onChanged() 方法响应数据的变化。
    功能:Observer 定义了 onChanged() 方法,当 LiveData 中的数据更新时被调用,用于响应数据的变化。

  • MutableLiveData
    关系:继承自 LiveData,是一种可变的 LiveData。
    功能:通过 setValue() 和 postValue() 方法,允许外部更新数据。

  • MediatorLiveData
    关系:继承自 LiveData,并且组合多个 LiveData 数据源。
    功能:通过 addSource() 和 removeSource() 方法,可以动态添加或移除 LiveData 数据源。MediatorLiveData 会监听所有数据源的变化,并在指定条件下触发观察者的更新。

UML类图看完了,那我们看看他们大致的时序图
在这里插入图片描述
看不懂图没关系 我们看一下文字描述,图文结合就能懂了

注:在 LiveData 的上下文中,“if in active state” 表示观察者(Observer)处于“活跃状态”时,LiveData 会将数据通知给该观察者。这个活跃状态(active state)是由 LifecycleOwner 的生命周期状态决定的。
活跃状态的定义
跃状态(Active State): 在 LiveData 中,观察者会被标记为活跃,当它的 LifecycleOwner 处于 STARTED 或 RESUMED 状态时(比如 Activity 正在前台运行,或 Fragment 正在显示)。只有当观察者处于活跃状态时,它才会接收到 LiveData 的数据更新。
非活跃状态(Inactive State): 如果 LifecycleOwner 的状态低于 STARTED(如 CREATED 或 DESTROYED),则此时的观察者被视为“非活跃状态”。非活跃状态下,LiveData 不会通知观察者数据更新,以节省资源。

时序图的详细流程

1. 观察者注册(调用 observe() 方法)

  • Activity/Fragment(或其他 LifecycleOwner)调用 LiveData.observe(),传入
    LifecycleOwner 和 Observer。

  • LiveData 检查 LifecycleOwner 的生命周期状态
    1、如果状态为 STARTED 或 RESUMED,则标记 Observer 为活跃状态,并立即触发通知,将当前数据推送给该观察者。
    如果状态低于 STARTED,则不会立即通知数据,等待状态更新到活跃时再通知。
    2、LiveData 将该 Observer 添加到观察者列表中,监听其生命周期变化。

2. 生命周期状态改变

  • 当 LifecycleOwner 的生命周期状态变化(如从 CREATED 到 STARTED),LiveData 会监听到该事件。
  • LiveData 再次检查 LifecycleOwner 的状态:
    1、如果新的状态为 STARTED 或 RESUMED,LiveData 将此观察者标记为活跃,并触发数据通知(如果有新数据)。
    2、如果状态低于 STARTED(例如 DESTROYED),LiveData 将停止对该观察者的更新,可能会从观察者列表中移除该观察者。

3. 数据更新(调用 setValue() 或 postValue())

  • 当调用 setValue() 或 postValue() 更新 LiveData 的数据时:
    1、 LiveData 将数据标记为“已更新”状态,准备通知所有活跃的观察者。
    2、 如果当前没有活跃的观察者,LiveData 将暂存新数据,等待后续观察者变为活跃时再通知。
    3、如果有活跃的观察者,则将最新数据立即推送给所有活跃的观察者。

4. 数据通知
1、LiveData 会遍历所有活跃状态的观察者,将最新数据推送给每个观察者。
2、Observer 的 onChanged() 方法被调用,以接收最新的数据并触发 UI 更新。

5. 观察者移除
当 LifecycleOwner 的生命周期状态到达 DESTROYED 状态时,LiveData 自动从观察者列表中移除该观察者。
这一步确保不再对已销毁的 LifecycleOwner 进行数据通知,防止内存泄漏。

三、源码解析

了解了LiveData大致工作原理后 下面我们从源码角度开始分析

1. 基本使用

在实际项目中,LiveData 通常配合 ViewModel 使用。下面是 LiveData 的基本使用步骤:

在 ViewModel 中定义 LiveData,并通过 setValue() 或 postValue() 更新数据。
在 Activity 或 Fragment 中通过 observe() 注册观察者,接收 LiveData 的数据更新。

// MyViewModel.kt
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data

    fun updateData(newData: String) {
        _data.value = newData
    }
}

// MyActivity.kt
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, Observer { data ->
            // 数据更新时调用
            println("Updated data: $data")
        })
    }
}

从上述代码可以看出,observe() 方法会将一个 Observer 附加到 LiveData 上,从而监听数据的变化。接下来,我们深入探究 LiveData 是如何管理这些观察者,并如何实现数据分发的。

2. observe() 的内部逻辑:观察者绑定

从源码角度看,observe() 方法在接收 LifecycleOwner 和 Observer 后,主要执行以下几个步骤:

1、检查当前调用是否在主线程。
2、将 Observer 包装成 ObserverWrapper,并将其与 LifecycleOwner 关联。
判断 LifecycleOwner 的当前状态是否为活跃状态,如果是则立即接收数据更新。

observe() 方法源码

// LiveData.java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");  // 确保在主线程中调用
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

    if (existing != null && existing.isAttachedTo(owner)) {
        return; // 如果已经在该 LifecycleOwner 上注册,直接返回
    }
    
 	// 确保不能重复注册相同的 observer
    if (existing != null) {
        throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
    }

	// 将 wrapper 添加为 LifecycleOwner 的生命周期观察者
    owner.getLifecycle().addObserver(wrapper);
}

LifecycleBoundObserver 是 LiveData 内部的包装类,用于管理 Observer 的生命周期。
LifecycleOwner 会监听其生命周期的变化,当状态处于活跃(STARTED 或 RESUMED)时,Observer 会被激活,从而能够接收数据更新。

3. 数据更新:setValue() 和 postValue() 的区别

在实际开发中,数据更新有同步和异步两种方式:
1、setValue():在主线程直接更新数据。
2、postValue():在非主线程更新数据,利用 Handler 切换到主线程分发。

setValue() 源码解析
当我们在主线程调用 setValue() 时,LiveData 会立即更新 mData,并触发数据分发。

protected void setValue(T value) {
    assertMainThread("setValue"); // 确保在主线程中调用
    mVersion++; // 增加版本号,表示数据已更新
    mData = value; // 更新数据
    dispatchingValue(null); // 开始数据分发
}

mVersion 表示数据的版本号,每次更新数据时递增。
dispatchingValue(null) 会检查当前的 Observer 是否处于活跃状态,并将最新数据推送给所有活跃的观察者。

postValue() 源码解析
对于非主线程的调用,我们会使用 postValue() 来更新数据:

public void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {  //加锁 确保线程安全
        postTask = mPendingData == NOT_SET; // 检查是否已经有待更新的数据
        mPendingData = value; // 设置待更新的数据
    }
    
    // 如果没有待更新的数据,直接返回
    if (!postTask) {
        return;
    }
    // 切换到主线程执行更新
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

postValue() 将新数据暂存到 mPendingData,并通过 ArchTaskExecutor 切换到主线程,最终由 mPostValueRunnable 调用 setValue() 完成数据更新。

4. 数据分发机制:dispatchingValue() 和 considerNotify()

当调用 setValue() 或 postValue() 时,LiveData 会进入数据分发流程。dispatchingValue() 是数据分发的核心方法。

dispatchingValue() 方法源码

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) { // 检查是否正在分发
        mDispatchInvalidated = true; // 如果正在分发,则标记为无效
        return;
    }
    mDispatchingValue = true; // 标记为正在分发
    do {
        mDispatchInvalidated = false;  // 重置无效标记
        if (initiator != null) {
        	// 如果有 initiator,优先通知该 observer
            considerNotify(initiator);
            initiator = null; // 清空 initiator
        } else {
       		// 遍历所有观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions();
                 iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue()); // 通知每个活跃的 observer
                if (mDispatchInvalidated) { // 如果分发状态变为无效,则中断
                    break;
                    break;
                }
            }
        }
    } while (mDispatchInvalidated); // 如果有无效标记,继续分发
    mDispatchingValue = false; // 标记为不再分发
}

该方法会遍历 mObservers,调用 considerNotify() 向每个符合条件的观察者分发数据。
mDispatchingValue 和 mDispatchInvalidated 用于控制分发状态,避免重复分发。

considerNotify() 方法源码
considerNotify() 判断当前 Observer 是否活跃且需要更新,并调用 onChanged() 向其推送数据。

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) { // 检查 observer 是否处于活跃状态
        return; // 如果不活跃,直接返回
    }
    // 检查 observer 是否应该活跃
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false); // 如果不应活跃,则更新状态
        return;
    }
    // 检查 observer 是否已经接收到最新版本的数据
    if (observer.mLastVersion >= mVersion) {
        return; // 如果是最新数据,直接返回
    }
    observer.mLastVersion = mVersion; // 更新上次版本号
    observer.mObserver.onChanged((T) mData); // 触发 observer 的数据更新
}
  • shouldBeActive() 判断 Observer 是否符合分发条件。
  • 若 Observer 活跃且未接收到最新版本的数据,则调用 onChanged() 传递数据更新。

5. 生命周期感知:LifecycleBoundObserver 的实现

LifecycleBoundObserver 是 LiveData 的内部包装类,它实现了 LifecycleEventObserver,从而使 Observer 能够感知生命周期的变化并自动调整活跃状态。

private class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull final LifecycleOwner mOwner; // 绑定的 LifecycleOwner

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer); // 调用父类构造器
        mOwner = owner; // 记录 LifecycleOwner
    }

    @Override
    boolean shouldBeActive() {
        // 判断当前 LifecycleOwner 的状态是否处于活跃状态
        return mOwner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
    	// 监听 LifecycleOwner 状态变化
        if (mOwner.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
            removeObserver(mObserver); // 如果已销毁,移除 observer
            return;
        }
        // 根据当前状态更新活跃状态
        activeStateChanged(shouldBeActive());
    }
}

shouldBeActive() 方法会根据 LifecycleOwner 的当前状态判断 Observer 是否应保持活跃。
onStateChanged() 方法在生命周期状态改变时调用,用于移除已销毁的 Observer,或在 Observer 变为活跃时分发数据。


四、LiveData"粘包"

什么是LiveData“粘包”问题?
粘包指的是数据流在传输过程中被多个数据块粘合在一起,导致原本独立的数据包无法正常解析。这种现象在网络请求、并发数据处理时常见,而在使用 LiveData 时,由于线程不安全或者数据合并不当,也可能导致类似的“粘连”现象。

1. 多线程并发更新

比喻: 服务员使用托盘承载多个订单,但如果多个顾客同时下单,新的订单会覆盖旧的。
示例代码

val liveData = MutableLiveData<String>()

// 模拟两个线程同时更新
Thread {
    liveData.postValue("Order from Thread 1")
}.start()

Thread {
    liveData.postValue("Order from Thread 2")
}.start()

服务员:postValue 方法。
托盘:mPendingData。
咖啡杯:更新的数据。
由于两个线程几乎同时更新,最终可能只接收到 “Order from Thread 2” 的数据,而 “Order from Thread 1” 被覆盖掉了。

2. 重复发送数据事件

比喻: 广播员在每次广告结束后重复播放上一段音乐。
示例代码

val liveData = MutableLiveData<String>().apply { value = "Initial Data" }

liveData.observe(this) { data ->
    println("Received: $data")
}

// 模拟 Activity 重建
liveData.value = "Updated Data"

形象解释
广播员:LiveData 的观察者。
音乐:数据更新。
在重建时,观察者再次接收到 “Updated Data”,造成多次重复通知。

3. 高频数据更新

比喻: 传送带上的产品不断变化,但你只能抓到最新的一个。
示例代码

fun fetchDataStream(): LiveData<String> = liveData {
    for (i in 1..10) {
        emit("Data $i") // 数据流快速更新
        delay(100) // 模拟快速更新
    }
}

// 观察数据
fetchDataStream().observe(this) { data ->
    println("Received: $data")
}

形象解释
传送带:数据流。
产品:数据更新。
如果 fetchDataStream 运行速度很快,观察者可能只会接收到最后几次的数据,而中间的数据可能会被“丢掉”。

4. 生命周期管理不当

比喻: 图书馆重启时,借书系统导致重复借到同一本书。
示例代码

val liveData = MutableLiveData<String>()

// 观察者
liveData.observe(this) { data ->
    println("Received: $data")
}

// 模拟 Activity 重建
liveData.value = "New Data"

形象解释
图书馆:LiveData。
书籍:数据。
在 Activity 重建时,观察者接收到了相同的数据更新,造成冗余的通知。

解决方案

1. 使用 Mutex 锁来保证线程安全:

val mutex = Mutex()

fun safePostValue(data: String) {
    CoroutineScope(Dispatchers.IO).launch {
        mutex.withLock {
            liveData.postValue(data)
        }
    }
}

2. 使用 SingleLiveEvent 来避免重复发送:

class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner) { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        }
    }

    override fun setValue(value: T?) {
        pending.set(true)
        super.setValue(value)
    }
}

3. 使用 Flow 来控制更新频率:

fun fetchDataFlow(): LiveData<String> = liveData {
    flow {
        for (i in 1..10) {
            emit("Data $i")
            delay(100) // 控制更新频率
        }
    }.collect { data ->
        emit(data)
    }
}

总结

通过源码解析可以看出,LiveData 的核心设计基于观察者模式,结合 Lifecycle 实现生命周期感知的数据观察与分发。observe()、setValue() 和 dispatchingValue() 各司其职,确保数据在适当的生命周期内分发给活跃的观察者,提升了 Android 应用的数据管理能力和开发体验。


http://www.kler.cn/a/379059.html

相关文章:

  • 【解决办法】无法使用右键“通过VSCode打开文件夹”
  • 儿童安全座椅行业全面深入分析
  • 物联网赋能的人工智能图像检测系统
  • rnn/lstm 项目实战
  • 个人对Numpy中transpose()函数的理解
  • 视频去水印怎么办?两种方法教会你
  • JVM问题排查分析
  • 探索开源语音识别的未来:高效利用先进的自动语音识别技术20241030
  • 刘艳兵-DBA016-在您的数据库中,SALES表存在于SH用户中,并且启用了统一审计。作为DBA,您成功执行了以下指令:
  • 《Python网络安全项目实战》项目2 Python基础练习_总复习(1)
  • Vscode使用launch.json进行传参调试代码
  • 进程间通信Linux
  • 如何更新已经发布的 NPM 组件库
  • 青春的海洋:海滨学院班级回忆录项目
  • Rust 力扣 - 54. 螺旋矩阵
  • Python小游戏19——滑雪小游戏
  • 引领数字时代:万码优才如何变革IT人才招聘新体验(这里有更精准的推荐)
  • 电赛入门之软件stm32keil+cubemx
  • hadoop面试题
  • ios Framework版本号的问题。
  • 统信UOS设备驱动开发-调试优化
  • 【LeetCode:3226. 使两个整数相等的位更改次数 + 模拟 + 位运算】
  • 牛客网最新Java高频面试题汇总(2024最新含答案)
  • android h5页面获取不到定位数据的问题
  • WPS 中使用PPT导出视频
  • 面试题:JVM(三)