ViewModel之LiveData

  • 概述

    我们知道,页面中引用的数据放在ViewModel中,那么当ViewModel中的数据改变的时候怎样通知界面呢?我们可以通过持有Activity或者Fragment的实例来实现,但是在Android中组件实例的生命周期通常是易变的,随时可能销毁或者更改,所以按照原则,不能在生命周期长的类中引用生命周期短暂或者不可控的类的实例;并且,ViewModel中通常含有网络请求等异步操作,所以线程间异步通信也要考虑UI线程的问题,这些问题LiveData都做了很好的处理。

  • 注册监听

    通过调用LiveData的observe方法添加回调接口:

    mViewModel.userId.observe(this, {
        ToastUtil.showDefaultToast(this, it)
    })
    
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        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;
        }
          //添加到生命周期组件中
        owner.getLifecycle().addObserver(wrapper);
    }
    

    回调接口owner是带有生命周期的组件实例,默认的ComponentActivity和Fragment都实现了这个接口,LifecycleBoundObserver持有了owner和observer,相当于把他们对应在一起,可见,回调接口实例被添加到了mObservers中。

  • 关于数据变化的通知

    在LiveData中,有两个通知改变数据的方法:setValue和postValue。但其实postValue最后也是调用了setValue:

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(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);
            }
        };
    

    可以看到postValue只不过是通过Handler把回调接口放在队列中,以确保在主线程执行。

    所以主要看一下setValue方法:

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

    注意必须是主线程,保存数据。

    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;
    }
    

    注意此时dispatchingValue传入的是null,所以所有的mObservers中的监听回调都会被尝试执行:

    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.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
          //决定着数据是否已更新
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
    

    上面为什么说尝试呢?因为我这里看到,在开头首先会判断observer的mActive是否为true,不为true则不会执行。注意observer.mLastVersion >= mVersion的判断,这是数据更新的关键:

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

    可以看到只有在带有初始化值构造或者调用setValue方法更新值之后mVersion才会在原有基础上+1,所以每次dispatch的时候都要判断这个版本号,它决定着是否数据已更新,如果更新了才会通知回调方法执行,如果都通过了,最终执行到了我们监听回调接口的onChanged方法,显示Toast。

  • 绑定监听的时候也会触发监听回调

    除了setValue和postValue这种手动的通知监听的方式,当LiveData调用observe方法绑定到组件生命周期时也会触发监听回调,只不过在页面可见的时候(onStart之后)才会执行监听回调代码。

    前面注册监听的时候我们看到,在observe方法中还有一句owner.getLifecycle().addObserver(wrapper)代码,用于把回调接口绑定到生命周期组件的监听回调集合里。

    owner.getLifecycle()方法得到的是当前界面组件的LifecycleRegistry,看一下它的addObserver方法:

    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
    
        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }
    
        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
          pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
          popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }
    
        if (!isReentrance) {
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }
    

    statefulObserver持有observer的状态在当前状态不是DESTROYED的时候是INITIALIZED,不存在的情况下存入mObserverMap中,这个是生命周期组件中持有的所有监听回调的集合,targetState是生命周期组件及其父组件中最新的状态,while是判断当前的observer的状态是否在生命周期最新状态之前,把执行状态在当前状态之前的所有Observer都尝试执行dispatchEvent,并且是已添加到mObserverMap的前提下,会执行statefulObserver.dispatchEvent方法:

    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
    

    upEvent(statefulObserver.mState):

    private static Event upEvent(State state) {
        switch (state) {
            case INITIALIZED:
            case DESTROYED:
                return ON_CREATE;
            case CREATED:
                return ON_START;
            case STARTED:
                return ON_RESUME;
            case RESUMED:
                throw new IllegalArgumentException();
        }
        throw new IllegalArgumentException("Unexpected state value " + state);
    }
    

    所以这里传入的event是ON_CREATE。

    ObserverWithState持有的是前面传入的LifecycleBoundObserver和初始状态INITIALIZED:

    ObserverWithState(LifecycleObserver observer, State initialState) {
        mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
        mState = initialState;
    }
    

    对于LifecycleEventObserver类型的observer来说,Lifecycling.lifecycleEventObserver得到的就是observer本身,所以mLifecycleObserver.onStateChanged也就是LifecycleBoundObserver的onStateChanged方法:

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

    看一下activeStateChanged方法:

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            onInactive();
        }
        if (mActive) {
            dispatchingValue(this);
        }
    }
    

    如果mActive是true的话会执行dispatchingValue方法,mActive通过shouldBeActive()赋值:

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

    可以看到,onStart之后才会触发事件回调。注意这里dispatchingValue传入的是this,即当前的ObserverWrapper,所以只会尝试执行当前Observer的回调。

    我们可以从代码中看到,添加监听的时候会尝试dispatch回调,但是,我们前面的注册监听放在onCreate中,那么按照逻辑,在onStart之前的并不会被执行,但实际上为什么我们的监听在页面显示的时候自动执行了呢?其实自动执行是由Activity的生命周期方法触发的,使用ReportFragment来通知,ComponentActivity在onCreate的时候会调用ReportFragment.injectIfNeededIn(this):

    public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            // On API 29+, we can register for the correct Lifecycle callbacks directly
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }
        // Prior to API 29 and to maintain compatibility with older versions of
        // ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and
        // need to support activities that don't extend from FragmentActivity from support lib),
        // use a framework fragment to get the correct timing of Lifecycle events
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.
            manager.executePendingTransactions();
        }
    }
    

    在sdk29以上会额外添加LifecycleCallbacks来回调,为了兼容以前的版本,下面的代码是添加一个没有任何界面的ReportFragment,只用它来处理生命周期通知的相关工作。

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        dispatchCreate(mProcessListener);
        dispatch(Lifecycle.Event.ON_CREATE);
    }
    
    @Override
    public void onStart() {
        super.onStart();
        dispatchStart(mProcessListener);
        dispatch(Lifecycle.Event.ON_START);
    }
    
    @Override
    public void onResume() {
        super.onResume();
        dispatchResume(mProcessListener);
        dispatch(Lifecycle.Event.ON_RESUME);
    }
    ... ...
    ... ... 
    

    可见是利用Fragment的生命周期来处理回调,看一下dispatch方法:

    private void dispatch(@NonNull Lifecycle.Event event) {
        if (Build.VERSION.SDK_INT < 29) {
            // Only dispatch events from ReportFragment on API levels prior
            // to API 29. On API 29+, this is handled by the ActivityLifecycleCallbacks
            // added in ReportFragment.injectIfNeededIn
            dispatch(getActivity(), event);
        }
    }
    

    可以看到,29以下的通过ReportFragment的dispatch方法发送生命周期回调通知:

    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
        if (activity instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return;
        }
    
        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }
    

    我们看到调用了LifecycleRegistry的handleLifecycleEvent方法,这个方法中又调用了moveToState方法,moveToState又调用了sync方法,在sync中会调用forwardPass或backwardPass方法,这两个方法中都会调用和前面addObserver方法中类似的代码遍历mObserverMap,对符合执行时机的Observer调用dispatchEvent方法。

    所以当界面显示的时候执行onStart回调时就会再次dispatchEvent,所以我们的监听回调此时就符合了“onStart之后执行”这个触发条件。

    那么29以上的生命周期回调怎么处理呢?上面的注释也有提到,是根据ActivityLifecycleCallbacks来处理的。上面我们已经看到在ComponentActivity的onCreate的时候通过ReportFragment的injectIfNeededIn方法已经添加了一个LifecycleCallbacks:

    static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
        @Override
        public void onActivityCreated(@NonNull Activity activity,
                @Nullable Bundle bundle) {
        }
    
        @Override
        public void onActivityPostCreated(@NonNull Activity activity,
                @Nullable Bundle savedInstanceState) {
            dispatch(activity, Lifecycle.Event.ON_CREATE);
        }
          ... ...
    }
    

    然后在Activity的生命周期方法中都会调用相关的通知方法,以onStart来说:

    protected void onStart() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
        mCalled = true;
    
        mFragments.doLoaderStart();
    
        dispatchActivityStarted();
    
        if (mAutoFillResetNeeded) {
            getAutofillManager().onVisibleForAutofill();
        }
    }
    

    其中调用了dispatchActivityStarted方法:

    private void dispatchActivityStarted() {
        getApplication().dispatchActivityStarted(this);
        Object[] callbacks = collectActivityLifecycleCallbacks();
        if (callbacks != null) {
            for (int i = 0; i < callbacks.length; i++) {
                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
            }
        }
    }
    

    可以看到,这里调用了每个ActivityLifecycleCallbacks的onActivityStarted方法,达到了生命周期通知回调的目的。

    不管是ReportFragment中生命周期方法内调用还是LifecycleCallbacks调用,最后调用的都是同一个diapatch方法,都会走到LifecycleRegistry的handleLifecycleEvent中去。

  • 和组件生命周期绑定

    当调用LiveData的observe方法时:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        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;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
    

    LiveData里面的mObservers会保存这个observer,这是为了数据改变的时候会调用这个observer来通知界面组件更新。

    同时owner.getLifecycle().addObserver(wrapper)会在界面组件的LifecycleRegistry里的mObserverMap中也会保存一个observer:

    ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
    ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
    

    而这里保存就是为了在组件销毁的时候自动解除绑定,避免内存泄露。

    Activity通过LifecycleRegistry来通知生命周期变化的,在它的setCurrentState方法流程中,拿前进时的生命周期方法forwardPass方法来说(backwardPass也一样):

    private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
                mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                pushParentState(observer.mState);
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
                popParentState();
            }
        }
    }
    

    这里会读取mObserverMap中所有的Observer来调用dispatchEvent方法,前面提到addObserver的时候mObserverMap保存的是ObserverWithState,所以会调用ObserverWithState的dispatchEvent方法:

    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
    

    mLifecycleObserver就是前面构造ObserverWithState时传入的observer,也就是LifecycleBoundObserver,所以会调用它的onStateChanged方法:

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

    可以看到,如果当前界面组件已经销毁的话,这里会从LiveData的mObserver中移除当前的observer。

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

推荐阅读更多精彩内容