Android LiveData 超详细解读

LiveData 可以说是 Android 中前几年比较新的框架了,配合 ViewModel 使用效果极佳,也是 Android 推荐架构模式中用到的框架之一。

LiveData 是一种可观察的数据存储类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。它有以下的优势:

  • 确保界面符合数据状态

    LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。

  • 不会发生内存泄露

    观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

  • 不会因 Activity 停止而导致崩溃

    如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

  • 不再需要手动处理生命周期

    界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

  • 数据始终保持最新状态

    如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

  • 适当的配置修改

    如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

  • 共享资源

    您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData。

但是它也存在一些缺点:

  • 异步更新数据时,可能会导致数据丢失的问题。
  • 组件在非活跃状态,同步调用无法触发观察者回调。

无论好坏,总结就是 LiveData 是一个自带生命周期管理、自带观察者模式,用来保存数据的对象,但它又不是持久化存储的。

使用

使用 LiveData 的步骤如下:

  1. 创建 LiveData 对象

    val currentName: MutableLiveData = MutableLiveData<String>()
  2. 创建 Observer 对象

    val nameObserver = Observer<String> { newName ->
      // 当数据变化时,更新 TextView 文案.
        nameTextView.text = newName
    }
  3. 使用 LiveData 的 observe() 方法将 Observer 作为参数绑定到 LiveData 对象。

    currentName.observe(this@NameActivity, nameObserver)
  4. 更新 LiveData 的值,触发 nameObserver 回调 block 中的逻辑:

    currentName.value = "新名字"
  5. 或者,在其他线程更新 LiveData 的数据:

    currentName.postValue("新名字")

基本上这就是 LiveData 的完整使用了。

唯一需要注意的是,如果是在主线程更新数据,使用 setValue 方法;在其他线程更新数据,使用 postValue 方法。

原理

LiveData 的核心逻辑就是它表现出来的特性,即自动处理生命周期、通过观察中模式回调通知观察者和数据更新的两个方法。

核心逻辑

相关类和接口

LiveData 用到了一些类和接口,这些类和接口贯穿整个 LiveData 的核心逻辑,所以需要先介绍这些类和接口。

Observer

Observer 是一个简单的可以接收来自 LiveData 的回调的接口,它只有一个 onChanged 方法,代表具有接收数据后进行更新的能力。

public interface Observer<T{
    void onChanged(T t);
}
LifecycleOwner

LifecycleOwner 定义了生命周期的能力,凡是实现这个接口的方法都具有 Android 的生命周期能力,例如 Activity 、Fragment 都实现了这个接口。

public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
}
LifecycleEventObserver

LifecycleEventObserver 可以接收任何生命周期更改并将其分派给接收者的类。

public interface LifecycleEventObserver extends LifecycleObserver {
    void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
ObserverWrapper

LiveData 中的内部类,对 Observer 进行了封装,拓展了 LiveData 关心的活跃状态和一些其他能力。

private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive; // 活跃状态
    int mLastVersion = START_VERSION; // 更新版本

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false
    }

    void detachObserver() {
    }

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        changeActiveCounter(mActive ? 1 : -1);
        if (mActive) {
            dispatchingValue(this);
        }
    }
}

而 ObserverWrapper 的实现有两个:

  • AlwaysActiveObserver,用于 observeForever 方法。
  • LifecycleBoundObserver,用于 observe 方法。

观察者注册

observe

LiveData 的核心方法是 observe(LifecycleOwner, Observer) :

LiveData 的绑定生命周期和添加监听的方法是 observe :

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // 生命周期已进入 Destroy ,无需在进行任何处理 
        return;
    }
    // 将 owner 和 observer 包装成 LifecycleBoundObserver
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 保存到 SafeIterableMap 中,SafeIterableMap 是一个 LinkedList,它伪装成一个 Map 并支持在迭代期间进行修改。 它不是线程安全的。
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 观察者已存在或无法绑定到 owner
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper); // 添加一个 LifecycleObserver,当 LifecycleOwner 更改状态时将通知该 LifecycleObserver。
}

在这个方法中,处理了生命周期对象的封装和观察者注册,最后一行代码可以在外部生命周期变化时,通知 Observer 。逻辑是:

  1. 检查 owner 的生命周期状态,如果 DESTROYED 直接 return 。
  2. 将 owner 和 observer 封装成 LifecycleBoundObserver 对象。
  3. 将 observer 保存到 SafeIterableMap 中。
  4. 如果 observer 已存在,或无法绑定到 owner ,抛出异常。
  5. 再次检查 existing 是否为 null ,为空直接 return 。
  6. owner 的生命周期添加一个观察者,即封装好的  LifecycleBoundObserver 对象,它实现了 ObserverWrapper ,可以作为 ObserverWrapper 使用。

在 observe 方法中,有一个比较核心的类 LifecycleBoundObserver,它的继承关系是:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver 

ObserverWrapper 在上文中提到过,是 Observer 的封装,增加了活跃状态的相关逻辑和更新版本管理能力。

LifecycleEventObserver 也在上文提及过,可以接收任何生命周期更改并将其分派给接收者的类。

LifecycleBoundObserver 实现了 LifecycleEventObserver ,具有了当生命周期变化时感知到的能力,通过 onStateChanged 方法来在感知到变化后做逻辑处理:

@Override
public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event)
 
{
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    // 当生命周期为 DESTROYED 时,移除监听。
    if (currentState == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    Lifecycle.State prevState = null;
   // 执行一个 while 循环,检查当前后状态不一致时
    while (prevState != currentState) {
       // 更新活跃状态和当前生命周期
        prevState = currentState;
        activeStateChanged(shouldBeActive());
        currentState = mOwner.getLifecycle().getCurrentState();
    }
}

在 while 循环,当前后状态不一致时,说明生命周期发生了变化,这个时候就通过 activeStateChanged 方法来处理分发逻辑:

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // 立即设置活跃状态,因此我们永远不会向非活跃的 owner 发送任何内容
    mActive = newActive;
    changeActiveCounter(mActive ? 1 : -1); // 更改计数
    if (mActive) {
        dispatchingValue(this);
    }
}

如果处于活跃状态,通过 dispatchingValue 方法分发观察者:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // 正在分发 value ,此次分发无效
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    // 更新正在分发状态
    mDispatchingValue = true;
    // 不断循环,直到分发无效
    do {
        // 成功开始分发,更新分发失效状态为 false
        mDispatchInvalidated = false;
        if (initiator != null) { 
            considerNotify(initiator); // 通过这里尝试通知观察者
            initiator = null;
        } else {
            // initiator = null ,向 mObservers 中的所有观察者尝试发送通知
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

considerNotify 方法是真正触发 observer 的 block 逻辑的方法:

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }

    // 如果 observer 不应该变成活跃状态,通过 activeStateChanged 方法将状态更新为非活跃状态。
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
  // 数据更新的版本,防止老数据延迟回调导致新数据被老数据覆盖
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 回调 observer 的 onChanged block 
    observer.mObserver.onChanged((T) mData);
}

这部分逻辑主要实现的逻辑是在生命周期发生变化时,尝试更新数据。是一种被动地尝试回调观察者。

而主动的方式是通过 setValue 和 postValue 方法。

observeForever

LiveData 还提供了另一个方法,不绑定生命周期,一个简单的观察者模式。这意味着给定的观察者将接收所有事件并且永远不会被自动删除,需要手动调用 remove 方法来移除。

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    wrapper.activeStateChanged(true);
}

这里保证无需关心生命周期的核心逻辑在 AlwaysActiveObserver 对象中,AlwaysActiveObserver 中没有处理绑定生命周期相关的对象,而是直接将整个对象的活跃状态默认返回为 true ,从而保证了一直活跃。

private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        return true;
    }
}

数据更新

setValue
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

通过 setValue 方法更新数据要求在主线程中调用,每次更新会将 mVersion 进行 + 1 ,这个属性会用来做数据更新的版本校验。然后将新的值更新给 mData 。最后,通过 dispatchingValue(null) ,一个 null 参数,通知到所有 mObservers 中的数据。

setValue 不回调问题

在 considerNotify 方法中,存在一些条件:

private void considerNotify(ObserverWrapper observer) {
    // 【1】
    if (!observer.mActive) {
        return;
    }

    // 【2】如果 observer 不应该变成活跃状态,通过 activeStateChanged 方法将状态更新为非活跃状态。
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
  // 【3】数据更新的版本,防止老数据延迟回调导致新数据被老数据覆盖
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 回调 observer 的 onChanged block 
    observer.mObserver.onChanged((T) mData);
}

【1】处很好理解,根据 observer 自身是否处理活跃状态来判断,mActive 的更新是通过 activeStateChanged 方法设置的。

条件【3】 也很好理解,通过增加了一个版本号防止老数据覆盖了新数据。

比较核心的是问题【2】,根据 ObserverWrapper 的实现不同,会有不同的逻辑。

在 observe 方法中,使用的是 LifecycleBoundObserver 对象,它的 shouldBeActive 方法实现是:

@Override
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

当生命周期在 STARTED 后,才变为活跃状态。

STARTED 代表的是 onStart 之后,onPause 之前

        /**
         * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * <ul>
         *     <li>after {@link android.app.Activity#onStart() onStart} call;
         *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
         * </ul>
         */
        STARTED,
postValue
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

postValue 要求在非主线程中发送消息给主线程,如果您在主线程执行 post 的任务之前多次调用此方法,则只会分派最后一个值。

postValue 中,首先加了个锁来处理数据更新逻辑。通过 postTask 局部变量,检查等待中的数据 mPendingData == NOT_SET ,这句代码的含义就是没有排队的数据。

如果存在等待中的数据,直接 return 而不是分发到主线程。这里就很容易产生一个疑问,为什么如果存在排队等待的数据,不发送到主线程而只是更新 mPendingData 呢?接着往下看。

而如果在 mPendingData 赋新的值之前是 NOT_SET  , 就可以直接发送一个 Runnable 到主线程。

mPostValueRunnable:

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

在这个 mPostValueRunnable 中,对于上面的问题有了一个答案。mPostValueRunnable 的 run 分发中也是先加锁再更新数据:

  1. 加锁
  2. 取出 mPendingData 保存到临时变量 newValue
  3. 将 mPendingData 设置为 NOT_SET
  4. 解锁
  5. 调用 setValue 方法更新数据为临时变量 newValue

这里的第三步,将 mPendingData 设置为 NOT_SET 能够解答 mPendingData 存在值时,为什么不发送到主线程的问题。

一旦发送成功,就会将 mPendingData 设置为 NOT_SET ,而如果因为生命周期处于非活跃状态,此时不会立即更新数据,mPendingData 此时就不能直接发送到主线程更新,而是变成待处理的数据。

数据丢失问题

在 postValue 方法,我们分析出,如果 mPendingData 一直不等于 NOT_SET ,就不会将新的值发送到主线程,也就是不会发送通知给观察者。而 mPendingData 却一直会被覆盖新的值,这样就会出现数据丢失的问题。

总结

LiveData 的特点就是生命周期感知、观察者模式和数据更新的两种方式。

生命周期感知

对于生命周期的自动感知和处理是通过 observe 方法实现的,它的第一个参数是一个 LifecycleOwner 对象,代表具有生命周期的对象。

当生命周期发生变化时,通过回调 LifecycleEventObserver.onStateChanged 方法触发响应逻辑。而在 LiveData 中 LifecycleBoundObserver 实现了这个接口,并在感知到变化时,触发被动的触发观察者回调。

而如果无需关心生命周期,则可以使用 observeForever 方法来注册监听。

观察者模式

LiveData 的观察者是 Observer ,在 LiveData 内部进一步封装成 ObserverWrapper 。ObserverWrapper 中增加了活跃状态和版本,前者配合 LifecycleBoundObserver 实现了对于生命周期变化的逻辑。而版本则是为了防止异步更新导致老数据覆盖新数据的问题。

数据更新

数据更新分为主动更新和被动更新。

被动更新:每次生命周期从非活跃状态重新进入活跃状态时,会触发回调

主动更新:通过 setValue 和 postValue 方法进行主动更新,在主线程更新使用 setValue;在其他工作线程使用 postValue 。


原文始发于微信公众号(八千里路山与海):Android LiveData 超详细解读

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/85071.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!