手撕Jetpack组件之LiveData

LiveData是什么?

在官网以及网上有很多文章都会介绍LiveData是什么、能够满足什么场景以及优势。假若用一句话概括,它就是与EventBus有着类似功能的一个库--事件发布订阅机制。那么今天我们就来扒一扒它是如何实现的。本文将会以4个方面进行源码分析:

  • LiveData是如何感知生命周期的?
  • 发布者发送数据后,订阅者是如何收到的?
  • 粘性数据是如何产生的以及如何避免?
  • 它与EventBus有什么区别?

简单使用

  • 添加依赖
implementation "androidx.lifecycle:lifecycle-livedata:2.3.0"
  • 简单demo
public class MainActivity extends AppCompatActivity {
    private MutableLiveData<String> mLiveData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 第一步
        mLiveData = new MutableLiveData<>();
        // 第二步
        mLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
            // 第四步
                Log.e("MainActivity", s);
            }
        });

    }

    public void jump(View view) {
    // 第三步
        mLiveData.setValue("Hello");
    }
}

LiveData的使用非常简单,执行代码中的三个步聚,第四步就可以收到发送过来的信息。接下来分析每一步都做了一些什么事。

源码分析

  • 构造LiveData对象
// MutableLiveData.java
public MutableLiveData() {
    super();
}

// LiveData.java
public LiveData() {
    mData = NOT_SET;
    mVersion = START_VERSION;
}

LiveData是一个抽象类,我们创建的MutableLiveData是它的实现类。在父类的构造方法中,初始化了两个属性,一个是Object类型的mData字段,它的作用是用来保存我们通过setValue传过来的值。另一个是int类型的mVersion字段,它的初始值为0。它有什么用呢?这里暂且先埋个坑。

  • observe方法
// LiveData.java
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    // owner就是我们传过来的MainActivity,很显然State不可能为DESTROYED
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 注释1
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 注释2
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 注释3
    owner.getLifecycle().addObserver(wrapper);
}

先看注释1部分

LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

将我们的生命周期拥有者,也就是我们的MainActivity和我们在MainActivity创建出来的观察者Observer包装成了LifecycleBoundObserver,我们来看看其构造方法做了什么事。

LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
    super(observer);
    mOwner = owner;
}

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

自己保存着我们的MainActivity的引用,调用父类ObserverWrapper构造方法,保存我们创建的Observer对象。在父类里面还有一个比较重要的int类型的属性: mLastVersion。上面有提到过mVersion字段,现在又来一个mLastVerison,猜测一下这两个字段是用来判断当前和上一次的版本,那具体是用来判断什么的呢?

再来看注释2部分

ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

以我们创建的Observer作为key,包装对象作为value添加到mObservers集合中,被保存到Map集合中的value还会被维护成双向链表。

public V putIfAbsent(@NonNull K key, @NonNull V v) {
    Entry<K, V> entry = get(key);
    if (entry != null) {
        return entry.mValue;
    }
    put(key, v);
    return null;
}

protected Entry<K, V> put(@NonNull K key, @NonNull V v) {
    Entry<K, V> newEntry = new Entry<>(key, v);
    mSize++;
    if (mEnd == null) {
        mStart = newEntry;
        mEnd = mStart;
        return newEntry;
    }

    mEnd.mNext = newEntry;
    newEntry.mPrevious = mEnd;
    mEnd = newEntry;
    return newEntry;

}

再来看注释3部分

// 这行代码就使得LiveData可以感知Activity的生命周期
owner.getLifecycle().addObserver(wrapper);

看到这里就明白了LiveData是如何感知Activity的生命周期了。将LifecycleBoundObserver作为生命周期的观察者,这样在生命周其变化时就会回调到onStateChanged方法。addObserver方法源码分析可以看手撕Jetpack组件之Lifecycle

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

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

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    ...
}
  • setValue
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

记得在上文中有提到过mVersion这个字段,初始值为0,此时变成了1,我们传的数据被mData引用。继续跟进:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

因在setValue方法中传入的ObserverWrappernull,所以会进入到else条件里面去。上文有提到过生命周期持有者与我们创建的Observer被包装成LifecycleBoundObserver对象,然后又以Observer为key,这个包装对象为value添加到mObservers集合中。继续跟进considerNotify方法。

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    // 注释4
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    // 注释5
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // 注释6
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

先看注释6部分,observer.mObserver指的就是我们在MainActivity里面创建的Observer,所以订阅者就收到了数据。

注释4和注释5是用来阻止数据回调的判断条件。先看注释4

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

LifecycleOwner的状态决定返回值,因为这里的mOwner就是我们的MainActivity,此时我们是在setValue的操作,状态已经变成RESUMED,所以这里返回true。注释4的if语句内不会执行。

再看注释5部分,这两个属性在上文中多次提到,原来它们的作用是用来判断是否要回调观察察者的回调方法。mLastVersion初始值为-1,mVersion的值在setValue时已经变成1了,所以注释5的条件也不成立。

到这里,整个源码分析就已经完毕。我们再来看看其它API的调用。

  • postValue
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

源码看起来比较简单。就是往主线程提交了一个Runnable

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

最终还是调到了setValue。这两者的唯一区别就是setValue只能在主线程,postValue不受线程局限。

  • observeForever

该方法添加的Observer并不受Activity的生命周期所约束。也就是说,当我们在Activity的onCreate方法注册一个观察者时,若没有在onDestroy方法中手动移除这个观察者,当Activity被销毁后,这个观察者仍然可以收到消息的回调。它是如何做到的?来看看其源码实现。

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    // 这里与observe方法一样,将我们传进来的observer包装成一个wrapper对象
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    // 将其放入到mObservers这个集合中
    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);
}

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    mActive = newActive;
    changeActiveCounter(mActive ? 1 : -1);
    if (mActive) {
    // 看到我们上面分析过了的方法。不过这里的参数为上面的AlwaysActiveObserver
        dispatchingValue(this);
    }
}

由于dispatchingValue方法的参数不为空,所以会进入这个方法的if语句中,最终就会执行到上面提到的注释4部分。由于我们的观察者是被包装了AlwaysActiveObserver对象,注释4部分的if条件判断语句由这个类实现。

@Override
boolean shouldBeActive() {
    return true;
}

这里它是直接返回了true,这也就使得它在没有被手动移除时,即使Activity销毁了仍然可以收到消息的原因。而我们前面提到LifecycleBoundObserver,它实现这个方法是由当前Activity的生命周期的State来决定的。

粘性事件

在开发中共用同一个LiveData是非常常见的,但随之也会产生一个副作用,数据粘性。也就是说,新注册一个观察者的时候,仍然可以收到上个观察者订阅的数据。比如我们在A界面跳转到B界面,在跳转之前,先用LiveData#setValue一个值,此时在B界面的onCreate方法用同一个LiveData对象注册一个观察者,居然收到了上个界面发送来的数据。一般情况下,我们是不希望收到上个界面的数据的,那这产生的原因是什么呢?

在上文有多次提到过,LiveData有两个比较重要的属性:Object类型的mDataint类型的mVersion,分别记录setValue发过来的数据和类似某种计数机制数据发送的次数,每调用一次setValue,这个次数都会++

界面AsetValue之后,此时LiveData中的mData保存着数据,mVersion为1。打开界面B,因为是在界面B的onCreate方法内用LiveData注册了一个新Observer,根据上文提到LiveData感知生命周期部分,会执行到LifecycleBoundObserver#onStateChanged方法

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

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

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    ...
}

最终会执行到上文提到的注释5部分代码。因为在创建一个新LifecycleBoundObserver时,它的mLastVersion属性值-1,所以注释5部分的if条件语句不成立,所以就会执行到注释6部分。这就是收到了上一个订阅者的数据原因。

如何避免这个问题了?

从理论上来分析,每个界面都使用自己的LiveData,每次都创建一个新LiveData对象。但由于在实际开发中,数据共享是非常常见的需求,所以这种方式并不是很完美。

通过上文的分析,在注释4和注释5这两个条件语句任意满足的话都可拦截订阅者收到发布者发布的数据。根据比较,注释5这个点是相对比较容易hook的。通过反射,在新注册Observer时,把其包装对象LifecycleBoundObserver中的mLastVersion改成与当前LiveData中的mVersion一样的值就可以了。

思路分析:

  • 想要修改mLastVersion字段,就需要先获取LifecycleBoundObserver字节码对象
  • 由于LifecycleBoundObserver是被保存在集合中,所以需要先获取mObservers字节码对象
  • 获取mObservers就需要先获取LiveData的字节码对象
public class CustomLiveData<T> extends MutableLiveData<T> {


    @Override
    public void observe(@NonNull @NotNull LifecycleOwner owner, @NonNull @NotNull Observer<? super T> observer) {
        super.observe(owner, observer);
        try {
            hook((Observer<T>) observer);
        } catch (Exception e) {
            Log.e("CustomLiveData", "observe exception: " + e.getMessage());
        }

    }

    private void hook(Observer<T> observer) throws Exception {
        Class<LiveData> liveDataClass = LiveData.class;
        Field mObserversField = liveDataClass.getDeclaredField("mObservers");
        mObserversField.setAccessible(true);
        Object mObservers = mObserversField.get(this);
        // 因为在Map中获取value时是通过get方法获取,所以还需要拿到这个get方法的字节码对象
        Method getMethod = mObservers.getClass().getDeclaredMethod("get", Object.class);
        getMethod.setAccessible(true);
        // 我们传进来的Observer被作为key,与LifecycleBoundObserver存入到mObservers中
        Object entry = getMethod.invoke(mObservers, observer);
        // LifecycleBoundObserver
        Object lbo = null;
        if (entry instanceof Map.Entry) {
            lbo = ((Map.Entry) entry).getValue();
        }
        if (lbo == null) {
            throw new NullPointerException("observerWrapper为空");
        }
        // mLastVersion是在父类中,先获取父类ObserverWrapper字节码对象
        Class<?> observerWrapperClass = lbo.getClass().getSuperclass();
        Field mLastVersionField = observerWrapperClass.getDeclaredField("mLastVersion");
        mLastVersionField.setAccessible(true);

        Field mVersionField = liveDataClass.getDeclaredField("mVersion");
        mVersionField.setAccessible(true);
        Object mVersionValue = mVersionField.get(this);

        mLastVersionField.set(lbo, mVersionValue);
    }
}

该解决方案参考Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus

之前有用过美团的这个LiveDataBus这个库,但发现一个问题就是短时间内发送多个事件给同一个Observer会出现只收到一个。

上面的方案使用的是反射,我们还可以使用一个包装类来实现。

public class ObserverWrapper<T> implements Observer<T> {
    private final StickyLiveData<T> mLiveData;
    private final Observer<T> mObserver;
    private final boolean isSticky;
    private int mLastVersion;

    public ObserverWrapper(StickyLiveData<T> stickyLiveData, Observer<T> observer, boolean isSticky) {
        mLiveData = stickyLiveData;
        mObserver = observer;
        this.isSticky = isSticky;
        mLastVersion = mLiveData.getVersion();
    }

    @Override
    public void onChanged(T t) {
        if (mLastVersion >= mLiveData.getVersion()) {
            // //但如果当前observer它是关心 黏性事件的,则给他。
            if (isSticky && mLiveData.getStickyData() != null) {
                mObserver.onChanged(mLiveData.getStickyData());
            }
            return;
        }
        mLastVersion = mLiveData.getVersion();
        mObserver.onChanged(t);
    }
}
public class StickyLiveData<T> extends LiveData<T> {
    private T mStickyData;
    private int mVersion = 0;
    private final OnDestroyCallback mCallback;

    public interface OnDestroyCallback {
        void onDestroy();
    }

    public StickyLiveData(@NonNull OnDestroyCallback callback) {
        this.mCallback = callback;
    }

    @Override
    protected void setValue(T value) {
        mVersion++;
        super.setValue(value);
    }

    @Override
    protected void postValue(T value) {
        mVersion++;
        super.postValue(value);
    }

    public void setStickyData(T stickyData) {
        this.mStickyData = stickyData;
        setValue(stickyData);
    }

    public void postStickyData(T stickyData) {
        this.mStickyData = stickyData;
        postValue(stickyData);
    }

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        observeSticky(owner, observer, false);
    }

    public void observeSticky(LifecycleOwner owner, Observer<? super T> observer, boolean isSticky) {
        super.observe(owner, new ObserverWrapper(this, observer, isSticky));
        owner.getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    mCallback.onDestroy();
                }
            }
        });
    }

    public T getStickyData() {
        return mStickyData;
    }

    public int getVersion() {
        return mVersion;
    }
}
public class LiveDataBus {
    private final ConcurrentHashMap<String, StickyLiveData> mHashMap = new ConcurrentHashMap<>();
    private static final SingletonHelper<LiveDataBus> INSTANCE =
            new SingletonHelper<LiveDataBus>() {
                @Override
                protected LiveDataBus create() {
                    return new LiveDataBus();
                }
            };

    public static LiveDataBus get() {
        return INSTANCE.get();
    }


    public <T> StickyLiveData<T> with(String eventName) {
        StickyLiveData liveData = mHashMap.get(eventName);
        if (liveData == null) {
            liveData = new StickyLiveData<>(new StickyLiveData.OnDestroyCallback() {

                @Override
                public void onDestroy() {
                    mHashMap.remove(eventName);
                }
            });
            mHashMap.put(eventName, liveData);
        }
        return liveData;
    }
}
public abstract class SingletonHelper<T> {

    public SingletonHelper() {
    }


    private T mInstance;

    protected abstract T create();


    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

这个库 UnPeek-LiveData也是处理LiveData粘性事件的,有兴趣的也可以去查看。

与EventBus的区别

  • LiveData可以自动感知Activity/Fragment的生命周期,EventBus做不到。
  • EventBus需要手动取消事件的注册,LiveData不需要。
  • LiveData是通过接口回调的方式通知订阅者,EventBus通过反射找到标有其注解的方法通知订阅者。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容