LiveData粘性事件的問題2
之前在LiveData文章中已經分析過粘性事件的原因了,但是在實際使用中還是遇到了一些問題,
網上的UnstickyLiveData的寫法通常是反射修改LiveData.ObserverWrapper.mLastVersion,
@Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { assertMainThread("observe"); super.observe(owner, observer); HookUtils.alignVersion(this, observer); }
public static void alignVersion(LiveData liveData, Observer observer) { Class<LiveData> liveDataClass = LiveData.class; try { //獲取field private SafeIterableMap<Observer<T>, ObserverWrapper> mObservers Field mObservers = liveDataClass.getDeclaredField("mObservers"); mObservers.setAccessible(true); //獲取SafeIterableMap集合mObservers Object observers = mObservers.get(liveData); //獲取SafeIterableMap的get(Object obj)方法 Class<?> observersClass = observers.getClass(); Method methodGet = observersClass.getDeclaredMethod("get", Object.class); methodGet.setAccessible(true); //獲取到observer在集合中對應的ObserverWrapper對象 Object objectWrapperEntry = methodGet.invoke(observers, observer); Object objectWrapper = null; if (objectWrapperEntry instanceof Map.Entry) { objectWrapper = ((Map.Entry) objectWrapperEntry).getValue(); } if (objectWrapper == null) { throw new NullPointerException("ObserverWrapper can not be null"); } //獲取ObserverWrapper的Class對象 LifecycleBoundObserver extends ObserverWrapper Class<?> wrapperClass = objectWrapper.getClass().getSuperclass(); //獲取ObserverWrapper的field mLastVersion Field mLastVersion = wrapperClass.getDeclaredField("mLastVersion"); mLastVersion.setAccessible(true); //把當前ListData的mVersion賦值給 ObserverWrapper的field mLastVersion mLastVersion.set(objectWrapper, liveData.getVersion()); } catch (Exception e) { if (BuildConfig.DEBUG) { throw new RuntimeException(e); } else { e.printStackTrace(); } } }
但是有個問題,如果在生命周期當前是:
owner.getLifecycle().getCurrentState().isAtLeast(STARTED)
那么里邊會立馬調用observer.onChange,這顯然是有問題的,正確的做法應該是我下邊的代碼
@Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { assertMainThread("observe"); if (getVersion() > LiveData.START_VERSION) {//有新值,會調用onChanged if (owner.getLifecycle().getCurrentState().isAtLeast(STARTED)) {//此時super.observe內會立馬調用onChanged PreventStickyEventObserverWrapper<? super T> myObserverWrapper = myObserverWrapperMap.get(observer); if (myObserverWrapper == null) { myObserverWrapper = new PreventStickyEventObserverWrapper<>(observer); myObserverWrapper.preventStickyEvent = true; myObserverWrapperMap.put(observer, myObserverWrapper); super.observe(owner, myObserverWrapper); } } else { super.observe(owner, observer); HookUtils.alignVersion(this, observer); } } else { super.observe(owner, observer); } }
class PreventStickyEventObserverWrapper<T> implements Observer<T> { @NonNull final Observer<T> observer; boolean preventStickyEvent = false; PreventStickyEventObserverWrapper(@NonNull Observer<T> observer) { this.observer = observer; } @Override public void onChanged(@Nullable T t) { if (preventStickyEvent) { preventStickyEvent = false; return; } observer.onChanged(t); } }
這樣就覆蓋了各種情況。
LiveDataBus
事件總線是一種解耦的編程方式,特別是在頁面深度特別深,需要通知之前的某個頁面狀態時非常有用,
主流的主要是EventBus,RxBus,但這些都有個問題就是生命周期感知的問題,
而LiveData之所以好,就是因為只在頁面真正顯示的時候才去通知觀察者,所以仿照着前者寫了個LiveDataBus,實現起來也很簡單,看起來也很方便,沒有其他的雜七雜八的邏輯。
github:https://github.com/wz147258/LiveDataBus