LiveData源码解析

是什么

是一个可观察的数据存储类,且具备宿主生命周期的感知能力。

优势

  • 页面不可见时不会派发消息
  • 页面可见时,会立刻派发最新的一条消息给所有观察者--保证页面最新状态
  • 不再需要手动处理生命周期,避免NPE
  • 支持黏性事件的分发
  • 共享资源,可以使用单例模式拓展 LiveData,实现全局的,不用反注册,不会内存泄漏的消息分发总线。

各类之间的关系

image.png

注册观察者流程,事件分发原理

// LiveData.class
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        // 判断是否主线程
        assertMainThread("observe");
        
        // 如果宿主已经destroy,则return
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        
        // 用LifecycleBoundObserver包装observer,其中包含LifecycleOwner,且能响应生命周期事, 如果页面destroy了,可以将observer自动从lifecycle移除
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        
        // 将observer和wrapper保存起来,如果之前已经存在则返回之前的wrapper,否则返回null
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        
        // 如果之前已经添加过该observer,且observer不是绑定当前owner,抛出异常
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        // 如果之前已经添加过该observer,则不再添加,return
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
    
// 更新mActiveCount,判断是否触发LiveData的onActive()和onInactive()
@MainThread
void changeActiveCounter(int change) {
        int previousActiveCount = mActiveCount;
        mActiveCount += change;
        if (mChangingActiveState) {
            return;
        }
        mChangingActiveState = true;
        try {
            // 更新mActiveCount
            while (previousActiveCount != mActiveCount) {
                boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
                boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
                previousActiveCount = mActiveCount;
                
                // 触发onActive()或onInactive()
                // 可以充分利用 onActive() 方法被激活的时机,来实现一些数据懒加载的功能。
                if (needToCallActive) {
                    onActive();
                } else if (needToCallInactive) {
                    onInactive();
                }
            }
        } finally {
            mChangingActiveState = false;
        }
}

// 给Observer分发数据
void dispatchingValue(@Nullable ObserverWrapper initiator) {
        // 如果正在分发数据,则将mDispatchInvalidated置为true,后面分发结束后会再分发一次
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            // 如果initiator则只给initiator分发,否则给所有observer分发数据
            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());
                    // 如果正在分发数据,dispatchingValue(observer)又被调用了,则停止本次分发,重新开始分发
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
}

// 真正分发数据的地方
private void considerNotify(ObserverWrapper observer) {
        // 如果observer所在页面不是活跃状态,则不分发数据
        if (!observer.mActive) {
            return;
        }
        
        // 获取observer所在页面的最新状态,有可能页面状态变了,但是还没更新Observer的mActive
        // 我们仍然先判断observer.mActive,如果页面已经变为活跃状态,但是observer.mActive还没更新,即还是不活跃状态,则更新observer.mActive,但仍不分发数据
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        
        // observer的数据版本大于LiveData的版本,即接收数据的次数大于发送数据的次数,说明observer已经收到最新的数据,不再回调observer#onChanged()通知更新
        // observer的mLastVersion=-1,如果之前已经发送过数据了,即mLastVersion<mVersion,则会出现黏性事件,后注册的观察者收到了前面发送的消息
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        
        // 每分发一次消息,则把观察者和LiveData的version对齐,防止重复发送
        observer.mLastVersion = mVersion;

        // 通知更新
        observer.mObserver.onChanged((T) mData);
}
    
    
#########################################################


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

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

        // 当前界面是否活跃,即当前界面状态是否大于STARED
        @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();
            
            // 如果当前界面已经销毁,则移除observer
            if (currentState == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            
            Lifecycle.State prevState = null;
            while (prevState != currentState) {
                prevState = currentState;
                // 给父类OvserverWrapper设置活跃状态,如果是活跃的话分发数据
            activeStateChanged(shouldBeActive());
                currentState = mOwner.getLifecycle().getCurrentState();
            }
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

// ObserverWrapper.class
    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;
            }
            
            mActive = newActive;
            // 更新mActiveCount,判断是否出发LiveData的onActive()和onInactive()
            changeActiveCounter(mActive ? 1 : -1);
            
            // 如果是活跃的话,分发数据
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
image.png

小结

  1. 首先调用LiveData#observe(owner, observer)进行注册,这时会将observer包装成LifecycleBoundObserver
  2. LifecycleBoundObserver继承自ObserverWrapper,保存了Observer和LifecycleOwner,所以可以回调observer#onChanged(data),也可以从LifecycleOwner中获得页面活跃状态;同时它实现了LifecycleEventObserver,所以能够监听lifecycle的生命周期事件,在页面销毁时移除observer,在页面变为活跃状态时,接收最新的数据(如果之前没有接收过的话)。
  3. 如果是在页面onResume是注册的观察者,onStateChanged(owner, event)会回调3次:onCreate、onStart、onResume。但是observer#onChanged(data)只会回调一次,因为onStateChanged(owner, event)会判断如果页面状态和之前一样(mActive没有改变),则return。
  4. 另外即使mActive改变了,但是如果数据没有改变,也不会回调observer#onChanged(data),因为LiveData会根据ObserverWrapper#mLastVersion和自身的mVersion判断数据的接收次数和发送次数是否一致:mLastVersion<mVersion则发送;否则不发送。
  5. LifecycleBoundObserver#onStateChanged(owner, event)回调时,会根据是否有活跃的观察者(mActiveCount)来触发LiveData#onActive()/onInactive()
  6. 之后就会判断页面是否活跃,如果活跃则分发数据,调用considerNotify方法,通过ObserverWrapper#mLastVersion和LiveData#mVersion来判断是否触发Observer#onChanged(data)

发送数据

// LiveData.class

private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            // 加锁,防止主线程更新数据时,mPendingData被改变
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
};
    
    
protected void postValue(T value) {
        boolean postTask;
        // 加锁,防止主线程更新数据时,mPendingData被改变
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        // 如果postTask=false,说明正在等待runnable获取mPendingData更新界面,不再发送runnable
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

@MainThread
protected void setValue(T value) {
        assertMainThread("setValue");
        // 增加发送次数
        mVersion++;
        mData = value;
        // 通知所有的observer更新数据
        dispatchingValue(null);
}
image.png

数据倒灌是什么

页面在再次打开时,收到了旧数据。
比如,有3个fragment:列表、详情、编辑,它们都共享viewmodel中的同一个liveData,在编辑页编辑完后通过livedata保存数据,退出返回详情页时,详情页恢复活跃状态,会从liveData收到最新的数据更新界面。再次回退到列表页,点击另一个item打开详情页,这时详情页调用liveData#addObserver()会从liveData收到之前到旧数据,这就是数据倒灌。

如果解决黏性事件

  1. 反射干涉Version
    不推荐这种方式,网上的方案是在LiveData#observe()方法中利用反射修改LifecycleBoundObserver中的mLastVersion的值=LiveData的mVersion,但是这种方案只适合在页面的onCreate(页面还不是活跃状态)时注册observer,因为这时页面的state是INITIALIZED,LiveData#observe()->LifecycleRegistrty#addObserver()中由于LifecycleRegistrty.mState=LifecycleBoundObserver.mState,导致不会回调ObserverWithState#dispatchEvent()->LifecycleBoundObserver#onStateChanged(),从而不会触发observer.onChanged(),也就达到了取消粘性事件的目的。但是如果在onStart()或之后(页面变为活跃状态)注册observer,由于这时页面的state是大于INITIALIZED的, 所以在LiveData#observe()->LifecycleRegistrty#addObserver()中会触发ObserverWithState#dispatchEvent()->LifecycleBoundObserver#onStateChanged(),也就导致observer.onChanged()被触发,还是发生粘性事件。

  2. 自定义LiveData和Observer,分别在内部维护一个version。当不需要粘性事件时,在用LiveData注册Observer时,将LiveData的version赋值给observer的version;当需要粘性事件时,在用LiveData注册Observer时,设置Observer的version为-1。

参考:https://github.com/KunMinX/UnPeek-LiveData/blob/de958b679b/unpeeklivedata/src/main/java/com/kunminx/architecture/ui/callback/ProtectedUnPeekLiveData.java#L40-L176

public class ProtectedUnPeekLiveData<T> extends LiveData<T> {


  private final static int START_VERSION = -1;


  private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);


  protected boolean isAllowNullValue;


  /**
   * TODO tip:当 liveData 用作 event 用途时,可使用该方法来观察 "生命周期敏感" 的非粘性消息
   * <p>
   * state 是可变且私用的,event 是只读且公用的,
   * state 的倒灌是应景的,event 倒灌是不符预期的,
   * <p>
   * 如果这样说还不理解,详见《LiveData 唯一可信源 读写分离设计》的解析:
   * https://xiaozhuanlan.com/topic/2049857631
   *
   * @param owner    activity 传入 this,fragment 建议传入 getViewLifecycleOwner
   * @param observer observer
   */
  @Override
  public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
  }


  /**
   * TODO tip:当 liveData 用作 event 用途时,可使用该方法来观察 "生命周期不敏感" 的非粘性消息
   *
   * @param observer observer
   */
  @Override
  public void observeForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
  }


  /**
   * TODO tip:当 liveData 用作 state 用途时,可使用该方法来观察 "生命周期敏感" 的粘性消息
   *
   * @param owner    activity 传入 this,fragment 建议传入 getViewLifecycleOwner
   * @param observer observer
   */
  public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    super.observe(owner, createObserverWrapper(observer, START_VERSION));
  }


  /**
   * TODO tip:当 liveData 用作 state 用途时,可使用该方法来观察 "生命周期不敏感" 的粘性消息
   *
   * @param observer observer
   */
  public void observeStickyForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, START_VERSION));
  }


  /**
   * TODO tip:只需重写 setValue
   * postValue 最终还是会经过这里
   *
   * @param value value
   */
  @Override
  protected void setValue(T value) {
    mCurrentVersion.getAndIncrement();
    super.setValue(value);
  }


  /**
   * TODO tip:
   * 1.添加一个包装类,自己维护一个版本号判断,用于无需 map 的帮助也能逐一判断消费情况
   * 2.重写 equals 方法和 hashCode,在用于手动 removeObserver 时,忽略版本号的变化引起的变化
   */
  class ObserverWrapper implements Observer<T> {
    private final Observer<? super T> mObserver;
    private int mVersion = START_VERSION;


    public ObserverWrapper(@NonNull Observer<? super T> observer, int version) {
      this.mObserver = observer;
      this.mVersion = version;
    }


    @Override
    public void onChanged(T t) {
      if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {
        mObserver.onChanged(t);
      }
    }


    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      ObserverWrapper that = (ObserverWrapper) o;
      return Objects.equals(mObserver, that.mObserver);
    }


    @Override
    public int hashCode() {
      return Objects.hash(mObserver);
    }
  }


  /**
   * TODO tip:
   * 通过 ObserveForever 的 Observe,需要记得 remove,不然存在 LiveData 内存泄漏的隐患,
   * 保险的做法是,在页面的 onDestroy 环节安排 removeObserver 代码,
   * 具体可参见 app module 中 ObserveForeverFragment 的案例
   *
   * @param observer observeForever 注册的 observer,或 observe 注册的 observerWrapper
   */
  @Override
  public void removeObserver(@NonNull Observer<? super T> observer) {
    if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {
      super.removeObserver(observer);
    } else {
      super.removeObserver(createObserverWrapper(observer, START_VERSION));
    }
  }


  private ObserverWrapper createObserverWrapper(@NonNull Observer<? super T> observer, int version) {
    return new ObserverWrapper(observer, version);
  }


  /**
   * TODO tip:
   * 手动将消息从内存中清空,
   * 以免无用消息随着 SharedViewModel 的长时间驻留而导致内存溢出的发生。
   */
  public void clear() {
    super.setValue(null);
  }


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

推荐阅读更多精彩内容