LiveData数据倒灌?你真的用对了吗?源码解析

livedata/lifecycle

在看livedata之前,还得先看看AndroidX自带的lifecycle,这也是livedata感知UI容器生命周期的保证。

androidx-lifecycle

基本使用

定义一个lifecycle watcher,用于观察activity的生命周期:

class LifecycleWatcher : LifecycleEventObserver, DefaultLifecycleObserver, LifecycleObserver {
    override fun onStateChanged(lifecycleOwner: LifecycleOwner, event: Lifecycle.Event) {
        Lifecycle.Event.ON_RESUME
        Log.e(TAG, lifecycleOwner.javaClass.name + " = " + event.toString())
    }

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        Log.e(TAG, "onCreate")
    }

    override fun onStart(owner: LifecycleOwner) {
        super.onStart(owner)
        Log.e(TAG, "onStart")
    }
}

然后将这个watcher与对应的activity关联起来形成观察者-被观察者联系:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        lifecycle.addObserver(LifecycleWatcher())
        ...
    }

这样咱们就可以在logcat中看到相关的打印信息了。

需要注意的是,因为onRestart生命周期因为会产生歧义或使用场景有限,就不支持了,这在文末会有说明。

uml-关键角色关系

AndroidX_Lifecycle_uml

观察者模式

androidx的activity种有一个get方法:getLifecycle获取到当前activity的生命周期。

然后lifecycle.addObserver(watcher)

所以,这是一个观察者模式,其中主要四个角色:

  • activity作为lifeCycleOwner,是被观察者持有者(生命周期持有者)。
  • 而activity里面的lifecycle才是被观察者
  • addObserver方法的参数是观察者,如presenter、viewmodel等。
  • LifecycleRegistry作为纽带,连接观察者和被观察者。

关键源码分析

  1. 简单使用:

    我们拿到MainActivty的onCreate方法中,拿到它的生命周期(被观察者)并添加一个观察者实现:

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            ...
            lifecycle.addObserver(LifecycleWatcher())
        }
    

    观察者实现,实现于LifecycleEventObserver或DefaultLifecycleObserver或LifecycleObserver(注解方式已废弃)。

    其中LifecycleEventObserver为event形式观察,DefaultLifecycleObserver为callback形式观察全生命周期。

    可以看到,它的父类是:public interface DefaultLifecycleObserver extends FullLifecycleObserver

    class LifecycleWatcher : LifecycleEventObserver, DefaultLifecycleObserver, LifecycleObserver {
        override fun onStateChanged(lifecycleOwner: LifecycleOwner, event: Lifecycle.Event) {
            Lifecycle.Event.ON_RESUME
            Log.e(TAG, lifecycleOwner.javaClass.name + " = " + event.toString())
        }
    
        override fun onCreate(owner: LifecycleOwner) {
            super.onCreate(owner)
            Log.e(TAG, "onCreate")
        }
    
        override fun onStart(owner: LifecycleOwner) {
            super.onStart(owner)
            Log.e(TAG, "onStart")
        }
        ...
    }
    

    这样我们就可以通过自定义的观察者来观察MainActivity的lifecycle了。

    我们使用起来非常简单,那它内部是怎么做到的呢?

  2. 从addObserver()开始

    addObserver的具体实现是LifecycleRegistry类:

    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        // 以状态模式对activity生命周期进行封装
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        // 观察者集合中没有当前观察者,就将当前观察者加入到集合中去
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
        ...
        // 同步当前生命周期的状态
    }
    

    可以看到,这个方法有两个要点:

    1. 状态模式对activity生命周期进行封装:
      static class ObserverWithState {
          State mState;
          LifecycleEventObserver mLifecycleObserver;
      
          ObserverWithState(LifecycleObserver observer, State initialState) {
              mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
              mState = initialState;
          }
      
          void dispatchEvent(LifecycleOwner owner, Event event) {
              State newState = event.getTargetState();
              mState = min(mState, newState);
              mLifecycleObserver.onStateChanged(owner, event);
              mState = newState;
          }
      }
      
      这是LifecycleRegistry中的一个静态内部类,它不仅封装了activity的状态,LifecycleRegistry也是通过它下发状态变更的事件给观察者的,它是纽带中的纽带
    2. 观察者集合:
      FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap
      观察者为键、当前状态为值的map集合。
  3. 如何观察activity生命周期改变并通知观察者的?

    下面来到了关键部分,我们再分析一下RegistryLifecycle的具体实现发现:

    handleLifecycleEvent这个public方法被触发后会执行一系列私有方法来同步观察者集合中的生命周期状态。

    handleLifecycleEvent -> _moveToState -> _sync() -> _forwardPass() or _backwardPass()下划线开头的方法都是私有的。

    通过枚举状态定义的顺序进行比较来向前或向后更新生命周期状态。

    再看这个方法的名字,handleLifecycleEvent,明显就是来处理生命周期事件的,显然它就是activity生命周期变动的回调处理。

    我们通过getLifecycle()可以直接获取MainActivity的生命周期,点进去发现是在ComponentActivty中实现了LifecycleOwner:

    public interface LifecycleOwner {
        /**
         * @return The lifecycle of the provider.
         */
        @NonNull
        Lifecycle getLifecycle();
    }
    

    并且也找到了ComponentActivity的实现和成员变量mLifecycleRegistry(纽带):

    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    ...
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
    

    一切都如预期般衔接上了。但随之而来的是噩梦。

    ComponentActivity中mLifecycleRegistry竟然只有getLifecycle()这个方法引用了,那ComponentActivity是如何把生命周期变动给到这个纽带的呢?

    我又去ComponentActivity的生命周期相关方法中挨个查看,发现都没有。。

    甚至于找到了隔壁FragmentActivity的LifecycleRegistry实现,但二者纽带的引用地址、变量名都不一样,肯定不是成员变量的覆写,说明ComponentActivity生命周期通知给纽带的逻辑在别处。

    于是我查了下资料,发现ComponentActivity的onCreate()方法中注册了一个ReportFragment

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        super.onCreate(savedInstanceState);
        // 注册了个用于报告的空Fragment
        ReportFragment.injectIfNeededIn(this);
        ...
    }
    

    看这个名字起的很有意思,那究竟是要report什么呢?点进去看一看:

    public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            // On API 29+, we can register for the correct Lifecycle callbacks directly
            LifecycleCallbacks.registerIn(activity);
        }
        // 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();
        }
    }
    

    映入眼帘的全是lifecycle相关,原来逻辑在这里!专门用来报告生命周期状态的空fragment

    • 可以看到SDK>=29时可以直接注册,activity.registerActivityLifecycleCallbacksactivity直接支持生命周期的回调。

    • 其他情况需要持有一个空ReportFragment来进行fragment生命周期的手动监听:

      @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);
      }
      
      @Override
      public void onPause() {
          super.onPause();
          dispatch(Lifecycle.Event.ON_PAUSE);
      }
      ...
      

    最终二者都是通过一个static方法进行分发的:

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

    至此,终于找到了handleLifecycleEvent调用的地方,当纽带收到这个event后,经过一系列私有方法处理成state,下发给我们定义的观察者,这样就完成了观察者模式。

  4. 状态的封装

    public State getTargetState() {
        switch (this) {
            case ON_CREATE:
            case ON_STOP:
                return State.CREATED;
            case ON_START:
            case ON_PAUSE:
                return State.STARTED;
            case ON_RESUME:
                return State.RESUMED;
            case ON_DESTROY:
                return State.DESTROYED;
            case ON_ANY:
                break;
        }
    }
    

    这样就完成了lifecycle到state的映射。

livedata

基本使用

基本使用,定义一个viewmodel,view model中有一个String类型的livedata:

class SimpleViewModel : ViewModel() {
    private val text: MutableLiveData<String> = MutableLiveData()

    init {
        text.value = "Hello, World!"
    }

    fun getText(): LiveData<String> {
        return text
    }

    fun changeText(newText: String) {
        text.value = newText
    }
}

初始化viewmodel,并在ui中监听livedata的String内容变化:

    private fun initViewModel() {
        binding.buttonChangeText.setOnClickListener {
            mViewModel?.changeText("new text")
        }
        mViewModel = ViewModelProvider(this)[SimpleViewModel::class.java]
        mViewModel?.getText()?.observe(this, Observer {
            // val newValue = it + timeOfChange
            val newValue = it
            timeOfChange++
            binding.tvText.text = newValue
        })
    }

以上就是livedata的基本使用了,当点击按钮的时候会触发livedata的value改变,然后通知观察者响应。

源码分析

通过基本使用,我们发现不论是viewmodel还是livedata,都把lifecycle owner传进去了。

而lifecycle owner一般是由activity或fragment实现的,所以通过这个传递不难猜出,是通过lifecycle owner建立的生命周期感知。

还是从observe开始

从基本使用上来看,livedata也是基于观察者模式,通过观察livedata的value值改变而做出响应。

既然是观察者模式,我们就直接进入livedata的observe方法看看,它是如何实现观察者的监听的,不出意外,所有观察者会被放进一个list或者map中去,以便被观察者改变后对它进行回调。

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        ...
        // 这里是个封装类,封装并绑定了两个观察者,生命周期观察者和livedata的观察者。
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        // 这里就是所有观察者的集合了
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        ...
        // 观察生命周期
        owner.getLifecycle().addObserver(wrapper);
    }

可以看到,关键逻辑就这三行。

观察者集合是以livedata的观察者接口为键,LifecycleBoundObserver这个封装类为值的集合。

前面lifecycle我们知道也是基于观察者模式,而LifecycleBoundObserver这个类的名字就极其明显:是将生命周期和livedata的观察者(接口)进行绑定,进入这个类看看:

// 实现了LifecycleEventObserver,观察lifecycle owner,并且继承ObserverWrapper
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() {
        // 枚举值大于等于start表示可激活,即start || resume状态
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    // 对lifecycle owner的监听响应
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            // destroy状态直接移除观察者
            // 这个mObserver是来自父类,父类是对livedata观察者回调的包装
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        // 更新当前状态,并根据状态判断是否分发value
        while (prevState != currentState) {
            prevState = currentState;
            // 这行代码是关键,对生命周期变化的处理
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

    ...
    
    @Override
    boolean shouldBeActive() {
        // 这里可以看到枚举值 >= STARTED是激活态,枚举的定义顺序是:
        // DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED
        // 所以STARTED||RESUMED就表示activity是激活态
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    ...
    public boolean isAtLeast(@NonNull State state) {
        // 比较,大于等于即可STARTED || RESUMED
        return compareTo(state) >= 0;
    }
}

可以看到,LifecycleBoundObserver不仅包装了livedata的观察者,还包装了对生命周期的观察者,承担着两个观察者的作用,并将二者绑定:如果是lifecycle是destroy状态就不再观察livedata,如果生命周期发生改变,且由非激活状态变为激活状态:activeStateChanged(shouldBeActive());

    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) {
            // 可以看到,这里如果是激活态,会进行了value的分发,经过上面过滤,必须是由非激活态转为激活态才会分发,这样的场景就是后台更新操作
            dispatchingValue(this);
        }
    }

通过观察源码我们发现,假如我们点击按钮触发livedata的更新操作,但是延迟3秒才postValue,或者runOnUiThread的setValue,但三秒还没到,我们就打开了另一个activity,使得当前activity进入非激活态。

而非激活态是不会将变化通知给观察者的,只有当状态是非激活太变为激活态(与上一个状态不同&&当前状态是激活态&&livedata版本大于原版本发生了变化),才会通知观察者。

这样就将生命周期和livedata绑定了起来,实现了只在激活状态下才分发value的更新。

下面再看看value的变化是如何分发给观察者的。

livedata变化通知观察者

上面我们看到了lifecycle观察者和livedata观察者进行了绑定,但是还是不知道livedata的内容发生改变后,是如何通知多有livedata的观察者的。

找到livedata的setValue()方法,进去一看便知:

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 *
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

先看看注释:

  1. 设置一个新的value,这回通知多有已激活的观察者,并将新值分发给他们。
  2. 这个方法必须在主线程被调用,子线程调用用postValue

再看看里面的逻辑:

  • 版本号加1
  • 更新value值
  • 分发

首先是版本号,初始值是-1,在livedata的构造函数中,如果没有给初始值,默认就用-1,如果给了初始值,则初始版本为-1 + 1 = 0,具体可以看源码,很清楚明白。

将value值更新无需多言,我们直接看分发value的逻辑:

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            // 是否是初始化操作
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                // 不是初始化操作,是setValue操作,遍历观察者集合进行通知
                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进行的通知,主要区别在于initiator观察者是否为null,不为null直接通知该观察者,为null遍历通知所有观察者

但他们最终都调用了considerNotify,这个方法才是通知逻辑,通知某个具体的观察者:

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

livedata的版本号当前观察者的版本号进行对比,如果版本号大于原版本号则执行观察者的onChange回调方法,这样就通知了观察者。

所以observer.mLastVersion每一个观察者都有一个版本号需要和livedata的版本号进行同步。

注意这两个版本号的定义位置是不同的,当前观察者的版本号定义的位置有个坑会导致数据倒灌,后面会细说。

再来看看最终的时序图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sviTsZOl-1681211413877)(https://img.yameko.top/livedata%E6%97%B6%E5%BA%8F%E5%9B%BE.png)]

viewmodel源码分析

FAQ

参考:

  • https://juejin.cn/post/6991168529454088228#heading-4
  • https://juejin.cn/post/6991497263457501221
  • https://xiaozhuanlan.com/topic/6719328450

lifecycle用到的设计模式

  • 观察者模式
  • 状态模式

lifecycle是如何监听aty生命周期的

在ComponentActivity中使用一个空的ReportFragment进行监听,然后通过LifecycleRegestry作为纽带回调给观察者。

一些废弃和原因

为什么废弃注解形式的event

提问chatGPT:

问:https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=zh-cn#2.4.0

这篇文章中,为什么要废弃@OnLifecycleEvent

答:AndroidX Lifecycle 2.4.0 版本废弃了 @OnLifecycleEvent 注解,主要原因是该注解具有以下问题:

  1. 该注解容易与混淆和代码可读性相关的其他注解混淆,导致使用者难以理解。
  2. 该注解的使用场景较为有限,通常需要在 Activity 或 Fragment 中使用,而其他组件则很难使用。
  3. 该注解需要使用者手动处理生命周期事件,导致使用者难以进行统一管理和维护。

因此,为了提高代码的可读性、灵活性和易维护性,AndroidX Lifecycle 废弃了该注解,并推荐使用 LifecycleEventObserver 和 DefaultLifecycleObserver 接口来监听生命周期事件。

为什么不再支持onRestart

chatGPT:

根据我们之前的讨论,主要有两个可能的原因导致 AndroidX Lifecycle 不再支持 onRestart 方法:

  1. onRestart 方法的语义模糊,不太容易理解。这也是 AndroidX 官方文档中提到的原因。由于 onRestart 的执行时机比较特殊,它会在 Activity 从后台返回到前台时执行。但是它并不会在 Activity 首次启动时执行,这就容易让人感到困惑,不太好理解。
  2. onRestart 方法的使用场景比较有限。由于 onRestart 方法只有在 Activity 从后台返回到前台时才会执行,而这种情况并不是非常常见,因此 onRestart 方法的使用场景比较有限。考虑到 AndroidX Lifecycle 的设计目标是提供一种更加简单、可靠的方式来管理 Activity 和 Fragment 的生命周期,因此它们决定不再支持 onRestart 方法,以便更好地满足设计目标。

综上所述,我们可以得出结论:AndroidX Lifecycle 不再支持 onRestart 方法的主要原因是它的语义模糊,使用场景比较有限,同时为了更好地满足设计目标,AndroidX Lifecycle 决定放弃对该方法的支持。

总结:

  • 语意模糊冗余,与onResume语义重叠,容易混淆、可读性差。
  • 使用场景有限,不经常使用且可替代
  • Google官方可能有意简化生命周期流程

livedata是如何和lifecycle绑定的

lifecycle的监听是观察者模式,前面讲过,livedata的也是观察者模式。

通过LifecycleBoundObserver类,将生命周期的观察者和livedata的观察者绑定封装在了一起。

激活状态 = start || resume

通过监听lifecycle,当生命周期发生变化时开始过滤:

  • 只有当生命周期状态发生改变 &&
  • 当前状态为激活状态 &&
  • livedata的version大于上一次的(值发生改变)

同时满足上面三种情况,则生命周期的变化会通知观察者。

如果一直是激活态,只要value值更新,哪怕更新的值和原来的值一样,版本号也会+1,只要和原版本不同就会通知观察者(即使新值和旧值是一样的)。

设置相同的值,订阅的观察者们会收到同样的值吗

会,只和版本号有关

哪些情况才会分发值通知更新

  1. 调用 setValue 和 postValue 并且 LifecycleOwner 处于活跃状态时,其实这种情况就是第三种情况
  2. LiveData 有值,并且处于活跃状态时,调用 LiveData#observe 订阅观察者
  3. LiveData 有新值,也就是 ObserverWrapper 的 mLastVersion 小于 LiveData 的 mVersion,LifecycleOwner 从不活跃状态转为活跃状态时

作者:那根笔
链接:https://juejin.cn/post/6991497263457501221
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

livedata是如何感知生命周期的,需要取消注册吗?

在调用livedata的observe方法时,会调用 owner.getLifecycle().addObserver 已达到感知生命周期的目的。

而在LifecycleBoundObserver的生命周期回调方法onStateChanged中如果是destroy状态会移除观察者,所以不需要我们手动取消注册,是自动感知的。

setValue 和 postValue 有什么区别

    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };
    
    ...
    
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

在子线程中调用setValue会抛出异常,不允许在子线程中调用该方法:throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread");

再看postValue源码,发现postValue最终还是通过handler跳回主线程调用setValue.

但是由于是允许在子线程调用,需要考虑多线程安全问题,所以在设置新值的过程pendingData或者newData都进行了synchronized加锁处理。

非激活态会收到事件吗,激活后最多收到几次事件?

GPT:

不会收到,当 Activity 不处于活动状态时,LiveData会将数据更改缓存起来,等到 Activity再次处于活动状态时,会将缓存的最新数据发送给观察者,只会通知一次

这种方式可以避免数据更新过程中多次通知观察者导致性能问题。

粘性事件和数据倒灌

粘性事件

先说下什么是粘性事件:当livedata在观察者开始观察之前就发生改变,当观察者开始观察后,这个改变仍然会通知到观察者,这就是粘性事件。

那么livedata是如何实现粘性事件的呢?

可以看到前面livedata源码的分析,通知观察者的情况一种可能是:activity生命状态发生改变&&由非激活态转为激活态&&版本号更新了。

那么再看这样一种场景:在activity初始化viewModel和livedata后,这时还没有调用observe开始观察livedata,我们先对livedata调用setValue,然后三秒后再观察livedata,发现仍然可以收到通知。

这样就复现了一个粘性事件。

其最根本的原因是,lastState默认初始化为null,这样lastActive就默认是false,而onsStart后,当前状态变为激活态,currentActive为true,与之前状态一比较发现生命周期发生改变,于是下发通知,这时又发现当前livedata版本号也比观察者的版本号(默认初始值为-1)大,刚好就会下发成功,则粘性事件到达。

数据倒灌

参考:

  • https://www.jianshu.com/p/cb6a80865805

首先先明确这样一个定义:

被观察者数据发生变化,如何确定这次变化需要通知到观察者?在livedata源码中,有俩版本号用作该控制。

第一个版本号是livedata的版本号,属于被观察者的,第二个版本号是该livedata的每一个观察者都有一个lastVersion,每次分发成功后会同步这两个版本号的值,将每个观察者的lastVersion更新位livedata的版本号。

基于上面livedata的粘性事件,通知观察者的情况一种可能是:activity生命状态发生改变&&由非激活态转为激活态&&版本号更新了。

这种情况会通知观察者。

那么再看现实中的一个场景:购买成功跳评论页。

购买成功用一个bool的livedata来存储分发,在主页购买成功后,livedata被setValue为true然后通知每一位观察者。

观察者收到通知后打开评论页,正常流程就是这么简单,似乎没有什么问题吧。

  1. 但是,假设现在我们已经购买成功且去到评论页,然后再返回到主页。
  2. 这时突然屏幕旋转,activity被迫重建但viewmodel没有销毁,livedata存储的也是上次的值购买成功true。
  3. 这时在onCreate方法中进行对livedata的observe()观察,突然发现收到一个事件,这个事件被消费过已经打开了一次评论页,现在又会再打开一次,情况变得扑朔迷离了起来。

这是为什么呢?

答案就在observe()方法里,会创建一个新的LifecycleBoundObserver观察者,但创建新的观察者意味着版本号默认初始化为-1,且由于生命周期发生变动,由onCreate -> onStart转为激活态,刚好满足分发事件给这个新观察者的条件,那livedata就把它目前存储的最新值分发出去,所以还会再打开评论页。

如何解决数据倒灌

  • 解决方案参考:https://juejin.cn/post/7148049769057746952#heading-6

  • 反射侵入:重写livedata,在observe()中反射同步两个版本号,将这个观察者的版本号在初始化的时候同步为livedata的版本号。这样的话事件就是非粘性事件了。

  • 使用google大神的SingleLiveEvent包装类:仍然是粘性事件,只是粘性事件没有分发,但可解决数据倒灌,但是生命周期变化的事件只消费一次,setValue引起的事件分发才会被消费多次,具体实现通过在onChange回调中先套一层原子类+CAS

  • 使用事件包装器:在观察的model中除了content,再加一个isHandled属性,默认为false,第一次得到该数据内容后将isHandled设置为true,这样每次setValue,set的是包装后的event model,与SingleLiveEvent包装类有异曲同工之妙,只是一个在业务层一个更接近于框架层。但仍然是粘性事件,只是解决了数据倒灌。

  • UnPeekLiveData:KunMinX大神实现的livedata覆写类,其实原理和google大神的SingleLiveEvent有异曲同工之妙,都是observe()方法阻塞,setValue()再放开。它是通过map存储每一个观察者以其内存地址生产的唯一ID作为键,是否消费bool类型作为值,在setValue和observe方法中控制。仍然是粘性事件,只是在回调给开发者的onChange方法包了一层过滤掉了而已。

所以,只要没有入侵更改版本号的,基本上都还是粘性事件,只是通过某种手段进行过滤。

得出livedata适用场景

那么为什么会出现数据倒灌,我们的使用场景是否正确?

livedata其实是不适合处理业务逻辑的,因为它的观察者模式并非单一仅仅受到被观察者影响,同时也会受到activity的生命周期影响。

所以如果处理后台业务逻辑时,我们就需要考虑如果被当前activity的生命周期限制依然能正常工作,并保证结果一致性,这样才能放心使用。

那么哪种情况刚好可以放心使用呢?

UI的更新,activity作为UI的容器,viewmodel、livedata作为感知activity并提供数据,可以将数据缓存在内存中,以便activity激活时快速响应

UI的一致性比较容易得到保证,即使得到的是上一次最新的数据,虽然对于现在来说可能是旧数据,但总比没有好,而且我们在onCreate中可以重新进行远端请求并通过setValue更新livedata,进而更新界面。

所以得出结论:livedata的适用场景是UI变化的响应式编程,而不是业务逻辑上变化的观察和响应,否则业务逻辑的结果一致性受到activity生命周期影响而无法保证。

如果是业务侧的观察者模式可以考虑其他观察者比如:observeForever或者RX、Bus等。

observeForever有啥用

AlwaysActiveObserver与生命周期无感知

如果你想无论页面处于何种生命周期,setValue/postValue之后立刻回到数据。那么可以使用observerForever()方法,使用起来与observer()没有太大差别. 因为AlwaysActiveObserver没有实现GenericLifecycleObserver 接口,不能感应生命周期。

但是需要注意的是,在用完之后,一定要记得在onDestroy()方法中调用removeObserver()方法来停止对LiveData的观察,否则LiveData会一直处于激活状态,Activity则永远不会被系统自动回收,会造成内存泄露。

用它可以实现一个事件总线来代替event bus。

livedata作为fragment之间通信

我们已经知道,ViewModel能够将数据从Activity中剥离出来。只要Activity不被销毁,ViewModel会一直存储,并且独立于Activity的配置变化。

Fragment可以被看作Activty的子页面,即一个Activity中可以包含多个Fragment.这些Fragment彼此独立,但是又都属于同一个Activity.

基于ViewModel和Fragment组件的这些特性,我们可以利用LiveData,实现同一个Activity中的不同Fragment间的通信,因为不同的Fragment得到的都是同一个LiveData;

参考:https://blog.csdn.net/qq_43404873/article/details/109556209

在`RecyclerView`中结合`LiveData`实现数据更新通常涉及以下几个步骤: 1. 创建LiveData:首先,创建一个`LiveData<List<DataModel>>`,其中`DataModel`是您的数据模型类。这通常是从服务端获取数据的地方,或者处理本地缓存的刷新。 ```java private MutableLiveData<List<DataModel>> data MutableLiveData = new MutableLiveData<>(); ``` 2. 更新LiveData:当数据有新的变化时,比如从网络拉取新数据,或者本地数据发生更新,调用`data.postValue(newDataList)`来设置新的值。 ```java // 示例:网络请求成功后更新数据 networkRepository.getData().observe(this, new Observer<List<DataModel>>() { @Override public void onChanged(List<DataModel> newData) { data.postValue(newData); } }); ``` 3. 设置Adapter:在`onCreate`或`onResume`等适当的地方初始化`RecyclerView`,并将`LiveData`绑定给`Adapter`,通常会使用ViewModel或者LifecycleOwner来持有`LiveData`。 ```java recyclerView.setAdapter(new MyAdapter(data)); ``` 4. 适应 LiveData 的生命周期:确保当`ViewModel`或`Activity/Fragment`销毁时,取消对`LiveData`的观察,以避免内存泄漏。 ```java @Override protected void onDestroy() { super.onDestroy(); data.removeObservers(this); } ``` 5. 通知Adapter:当`LiveData`发生变化时,`Adapter`会自动收到通知并调用`notifyDataSetChanged()`来更新视图。 由于`LiveData`的设计使得数据更新过程更为简洁,只需要关注数据源,而无需手动维护数据同步的状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值