美文网首页
Android Jetpack LiveData原理分析

Android Jetpack LiveData原理分析

作者: 雷涛赛文 | 来源:发表于2020-11-16 10:16 被阅读0次

          网上关于DataBinding,ViewModel,LiveData文章很多,最近结合源码及相关实例分析了一下,本文结合LiveData的使用来对LiveData进行源码分析。
          关于DataBinding的使用,可以参考之前的文章:
          Android JetPack DataBinding分析

    一.LiveData简介

          LiveData 是一个可观察的数据持有者类。与常规 observable 不同,LiveData 是生命周期感知的,这意味着它尊重其他应用程序组件的生命周期,例如 activity,fragment。此感知确保 LiveData仅更新处于活动状态的应用组件观察者。
          如果 Observer 类表示的观察者生命周期处于 STARTEDRESUMED 状态,则 LiveData 会将其视为活动状态。LiveData 仅通知处于活动状态的观察者更新信息。非活动状态的观察者不会收到有关数据更改的通知。
          你可以注册与实现了 LifecycleOwner 接口的对象配对的观察者。此关系允许在相应 Lifecycle 对象的状态更改为 DESTROYED 时删除观察者。这对于 activity 和 fragment 特别有用,因为它们可以安全地观察 LiveData 对象而不用担心泄漏 - activity 和 fragment 在其生命周期被销毁时立即取消订阅。

    二.LiveData使用

          LiveData是一个抽象类,不能直接实例话,google为我们定义好了实现类MutableLiveData,可以通过MutableLiveData来创建不同类型的对象,结合代码来看一下:

    a.基础功能
    private MutableLiveData<String> mLiveData = new MutableLiveData<>();
    mLiveData.observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            Toast.makeText(HookActivity.this, "receive message is " + s,Toast.LENGTH_SHORT).show();
        }
    });
    mLiveData.setValue("test lifecycle observe");
    

          通过以上可以看到,使用起来还是比较简单的,先创建一个MutableLiveData对象,执行observe()方法,传入当前UI实现类(A/F)的对象引用和observer(监听data变化),执行setValue()来更新MutableLiveData的值。

    b.进阶功能

          UI内实现了对某个LiveData的监听,当UI处于后台后,此时LiveData的值进行了更新,那应该如何处理呢?结合代码来看一下:

    private void testDataObserverLifecycle() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    mLiveData.postValue("test lifecycle observe");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    

          以上方法可以看到,是启动一个线程,然后在3s后对LiveData进行了值更新,注意一下,postValue()是在非UI线程执行的值更新调用方法,当我们在执行以上方法后,将当前界面切换到后台,3s之后再切换回来,发现会弹出toast提示。
          结论:LiveData会监听UI的生命周期变化,当处于前台后,会将值变化通知到UI

    c.全局功能

          通过以上可以看到,当LiveData发生变化后,UI处于后台时,是不会收到值更新的,但是当返回前台后,会立刻收到值变化通知UI,那如果LiveData作为一个全局的变量,新的UI(A/F)启动后监听该LiveData会怎样呢?结合代码来看一下:
    ①.定义全局变量
          定义一个单例模式的类,里面定义一个HashMap来存储MutableLiveData,通过key来获取,如果map中存在,就返回;否则创建,然后存入map。

    public final class LiveDataBus {
    
        private final Map<String, MutableLiveData<Object>> bus;
    
        private static class InstanceHolder {
            public static LiveDataBus sInstance = new LiveDataBus();
        }
    
        private LiveDataBus() {
            bus = new HashMap<>();
        }
    
        public static synchronized LiveDataBus getInstance() {
            return InstanceHolder.sInstance;
        }
    
        public <T> MutableLiveData<T> with(String target, Class<T> type) {
            if (!bus.containsKey(target)) {
                bus.put(target, new MutableLiveData<>());
            }
            return (MutableLiveData<T>) bus.get(target);
        }
    }
    

    ②.创建LiveData
          先在Activity1内使用对应key的变量,并更新值:

    private void testData() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    LiveDataBus.getInstance().with("key_test", String.class).postValue("LiveDataBus test");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    

    ③.使用LiveData
          后在类Activity2内使用对应key的变量:

    LiveDataBus.getInstance().with("key_test", String.class).observe(this,
            new Observer<String>() {
                @Override
                public void onChanged(String s) {
                    Toast.makeText(Activity2.this, "receive message is " + s,Toast.LENGTH_SHORT).show();
                }
            });
    

          我们发现,会弹出Toast提示,也就是说后面创建新的UI内部使用该LiveData,也会收到回调,跟stick broadcast类似,后面注册该broadcast时,会立刻收到广播。
          在对LiveData进行使用后,有了一定的了解,带着问题去看一下LiveData的源码。

    三.LiveData源码分析

    a.observe()
    image.png
          LiveData注册了LifecycleOwner(Activity/Fragment)生命周期的观察者,当Activity/Fragment生命周期发生变化后,LiveData会对生命周期状态进行判断,来确定是否需要通知LifecycleOwner进行逻辑更新,详情可参考Android Jetpack Lifecycle详解,注册逻辑如下:
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            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);
    }
    

          从上面可以看到:在进行observe()时,首先确保是主线程,如果LifeCycleOwner的生命周期是destroyed时,直接返回;然后将owner及observer封装成LifecycleBoundObserver,添加到mObservers里面,后续值发生变化时,会遍历回调;最后将wrapper添加到owner的lifecycle生命周期的观察者。


    image.png

          接下来看一下LifecycleBoundObserver的实现:

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        .......
    
        @Override
        //判断owner是否处于active状态
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
    
        @Override
        //LifecycleEventObserver实现了LifecycleObserver,owner生命周期状态发生变化后的回调
        public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //当owner生命周期状态发生变化后,看是否需要将数据更新到owner,如果从后台切换到前台后,会将最新数据更新到UI[每次执行setValue()后,都会保存最新的mData = value]。
            activeStateChanged(shouldBeActive());
        }
        ......
    }
    

          LifecycleBoundObserver实现了LifecycleEventObserver,owner有生命周期变化后,会回调onStateChanged()方法,当其生命周期变为DESTROYED后,会移除observer,这也就是为什么我们不需要手动 remove observer 的原因,此后LiveData发生变化后,不会更新到Activity/Fragment,即不会一直持有其引用,从而不会造成内存泄露;
          当Activity/Fragment处于前台后,会将最新的数据更新到UI,逻辑如下:

    void activeStateChanged(boolean newActive) {
        ......
        if (mActive) {
            dispatchingValue(this);
        }
    }
    

          感知数据变化如下:


    image.png
    b.setValue()/postValue()
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    
    protected void postValue(T value) {
        ......
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    

          执行setValue()及postValue()后,会先更新mData为最新的value,然后执行dispatchingValue()。

    c.dispatchingValue()
    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()后,会对参数进行判断,如果不为空,会单独调用considerNotify();如果为空,则会遍历调用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);
    }
    

          通过以上可以看到,在considerNotify()内部会最终调用observer.mObserver的onChanged()方法。

    d.通知途径

          以上可以看到,有两种途径会最终调用dispatchingValue():
          1.setValue():调用dispatchingValue(null),会通知所有的observer,进而再调用considerNotify()进行更新。
          2.A/F(LifeCycleOwner)的activeStateChanged():调用dispatchingValue(this),由于此处参数不为null,所以会单独回调监听该owner对应的observer。LiveData内部关联了LifecycleOwner(Activity/Fragment)生命周期相关的方法,当LifecycleOwner生命周期变化的时候都会回调onStateChanged()方法,然后会去调用activeStateChanged(),最后调用considerNotify()方法去执行数据变化回调。
          接收事件流程:


    image.png

          以上就是对LiveData源码的分析,包括observe()及其内部封装、setValue()后执行dispatchValue()、considerNotify()最终执行observer的onChanged()进行通知及数据更新。

    四.LiveData粘性去除

          以上实例可以看到,LiveData可以作为全局变量来使用,具有粘性的功能,如果不需要粘性功能,需要怎么处理呢?先看一下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);
    }
    

          如果修改内部执行逻辑的话,可以通过hook来实现,内部有3处可以执行return,但是唯一可以hook的就是来使mLastVersion==mVersion,来使条件满足执行return,看一下实现方式:

    public final class LiveDataBus {
        ......
        public <T> MutableLiveData<T> with(String target, Class<T> type) {
            if (!bus.containsKey(target)) {
                bus.put(target, new BusMutableLiveData<>());
            }
            return (MutableLiveData<T>) bus.get(target);
        }
    
        private static class BusMutableLiveData<T> extends MutableLiveData<T> {
    
            @Override
            public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
                super.observe(owner, observer);
                try {
                    //通过设置mLastVersion = mVersion在执行considerNotify()时返回,消除粘性
                    hook(observer);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            private void hook(@NonNull Observer<?> observer) {
                Class<?> liveDataClass = LiveData.class;
                try {
                    Field mObserversField = liveDataClass.getDeclaredField("mObservers");
                    mObserversField.setAccessible(true);
                    Object mObservers = mObserversField.get(this);
                    Class<?> mObserversClass = mObservers.getClass();
    
                    Method getMethod = mObserversClass.getDeclaredMethod("get", Object.class);
                    getMethod.setAccessible(true);
                    Object entry = getMethod.invoke(mObservers, observer);
                    Object observerWrapper = ((Map.Entry) entry).getValue();
                    Class<?> observerClass = observerWrapper.getClass().getSuperclass();
    
                    Field mLastVersionField = observerClass.getDeclaredField("mLastVersion");
                    mLastVersionField.setAccessible(true);
                    Field mVersionField = liveDataClass.getDeclaredField("mVersion");
                    mVersionField.setAccessible(true);
                    Object mVersionValue = mVersionField.get(this);
                    mLastVersionField.set(observerWrapper, mVersionValue);
                } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

          以上可以看到,自定义类继承MutableLiveData类,在observe()后,执行hook(),将observer的mLastVersion的值设成跟mVersion一致就可以了。

    五.LiveData 的优点:

          a.确保你的 UI 符合你的数据状态
          LiveData遵循观察者模式,状态更改时LiveData会通知 Observer对象,观察者可以在每次数据变化时更新UI。
          b.没有内存泄漏
          观察者绑定到 Lifecycle 对象,并在其相关生命周期被销毁后自行清理。
          c.不会因为 activity 停止而发生崩溃
          如果观察者的生命周期处于非活动状态(例如,activity 在后台堆栈中),则它不会接收任何 LiveData 事件。
          d.不再需要手动处理生命周期
          UI 组件只是观察相关数据,不会停止或恢复观察。LiveData自动管理所有这些,因为它在观察时意识到相关的生命周期状态变化。
          e.始终保持最新数据
          如果生命周期变为非活动状态,它将在再次变为活动状态时接收最新数据。例如,后台 activity 在返回前台后立即接收最新数据。
          f.适当的配置更改
          如果由于配置更改(例如设备旋转)而重新创建 activity 或 fragment,则会立即接收最新的可用数据。
          g.共享资源
          可以使用单例模式扩展 LiveData 对象以包装系统服务,以便可以在应用程序中共享它们。LiveData 对象连接到系统服务一次,然后任何需要该资源的观察者只需观察 LiveData 对象。
          以上是对LiveData的使用及源码分析,详细理解需要重点关注LiveData这个类

    相关文章

      网友评论

          本文标题:Android Jetpack LiveData原理分析

          本文链接:https://www.haomeiwen.com/subject/cgxbbktx.html