LiveData不一样的使用方式

    xiaoxiao2022-07-12  198

            在正文开始需要读者首先了解过LiveData,如果还不了解的话,请先移步到官方文档。

            本篇文章主要内容:

            1.LiveData原理。

            2.对LiveData封装使用。

    LiveData原理

            既然说原理,那么肯定就要先知道如何使用了,

    public class UserModel extends ViewModel { public MutableLiveData<User> userMutableLiveData = new MutableLiveData<>(); public UserModel(){ userMutableLiveData.postValue(new User(18,"lml")); } public void doSomeThing(){ User user = userMutableLiveData.getValue(); if(user != null){ user.age = 15; user.name = "xx"; userMutableLiveData.setValue(user); } } }

    在该例中,使用的数据模型是Uer,里面包含age与name字段,我们再看看Activity中的使用:

    public class MainActivity extends FragmentActivity { private static final String TAG = "LiveDataBus"; private TextView tvContent; private Button btnTest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvContent = findViewById(R.id.tv_content); btnTest = findViewById(R.id.btn_test); final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class); // final UserModel userModel = new UserModel(); userModel.userMutableLiveData.observe(this, new Observer<User>() { @Override public void onChanged(User user) { tvContent.setText(user.toString()); } }); btnTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { userModel.doSomeThing(); } }); } }

    他的简单使用就这么多了,我们在点击按钮之后,tvContent的内容就会发生改变了。是不是感觉很熟悉,但又陌生,熟悉的是这这种机制很熟悉,陌生的是为啥他可以达到这个效果。那么现在我们就来看看他的内部原理吧。

           我们就从UI改变的地方看起吧,发现tvContent的ui改变是在onChanged的方法里面的,那么也就是说在我们点击按钮的时候会触发这个onChanged的动作。而按钮中就执行了

    userModel.doSomeThing();

    这一行代码了,我们再看看这个doSomeThing吧,发现内部主要做的事情就是获取User,并且修改User的值,然后调用

    userMutableLiveData.setValue(user);

    那我们就继续找下去吧,我们看看setValue方法,

    public class MutableLiveData<T> extends LiveData<T> { @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); } }

    发现调用了LiveData的setValue方法了,再跟踪到LiveData的该方法中去

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

    在这里我们发现两个有用的点,第一个是@MainThread注解,那么也就是说该方法是在主线程中执行的,第二个是执行了

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

    根据传入的参数,我们知道会进入到else中去,然后去执行这个for循环,在这里为什么要一个for循环,我们待会再说,我们先看看for循环中会执行什么东西,发现他会执行considerNotify方法,我们再次跟着进去看看

    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; //noinspection unchecked observer.mObserver.onChanged((T) mData); }

    在这里是不是发现一个很熟悉的东西,原来onChanged方法在这里被执行了。到目前为止,我们已经知道为什么onChanged方法会执行了。但是还不知道他从哪里来的呢。说到这里,不知道大家还记不记得在开头使用的时候,我们有这么一块代码:

    userModel.userMutableLiveData.observe(this, new Observer<User>() { @Override public void onChanged(User user) { tvContent.setText(user.toString()); } });

    也就是我们时间的起源,那么也就是说在considerNotify中的mObserver是这里的匿名Observer了,他是怎样去到那里的呢,在这里发现userModel.userMutableLiveData调用的oberve方法将它传入进去了,我们在看看这个userMutableLiveData是个啥。

    发现他是一个MutableLiveData,那么我们就要去看看他的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); }

    那么在这里似乎就明白了,原来那个匿名的Observer在这里被add进去了,在这里我们还看到涉及到了Lifecycle,不过本篇文章不会讲这个了,需要了解的童鞋可以自行去Lifecycle官网了。在这里其实也看出来为啥开始会用for循环去取Observer了,因为在这里可以add多个不同的Observer。那么在消息分发的时候肯定就要对多个观察者进行回调了。

    LiveData封装使用

            在直接使用LiveData的时候不知道发没发现一个问题,比如出现这样一个情景:MutableLiveData在ActivityA界面进行消息发送,也就是setValue,如果ActivityA界面此时注册观察者(也就是调用observe方法),那么在ActivityA界面响应这个消息肯定是正常的,但是,如果此时MutableLiveData向ActivityB界面发送消息(此时ActivityB界面还未启动),这个时候我们再去启动ActivityB界面,然后在ActivityB界面注册观察者,然而这个时候,onChanged方法会被触发(先发送消息,后订阅)。这样一个机制合理么?OK,估计有的童鞋不是很明白这段话的意思,我就说一个现实中的例子,在美国,邮局发送报纸是发给已经订阅了的人的,此时邮局相当于MutableLiveData,订阅的人相当于各个Activity中的Observer,在邮局有新的报纸的时候,也就是说MutableLiveData会发执行一个setValue方法,通知第一个订阅者(ActivityA的Observer)有新的报纸,并且把新的报纸送到订阅者的邮箱,然后订阅者阅读报纸(onChanged),这么一个过程是合理的,但是,这个时候有个第二个订阅者(ActivityB的Observer),但是第二个订阅者是在第一个订阅者阅读完报纸之后订阅的,也就是说此时第一个订阅者的报纸是属于旧报纸,但是这个时候MutableLiveData依然会将这个旧报纸送给了第二个订阅者。这种机制很明显在正常情况下是不合理的,因为订阅的人一般是想看新的报纸,也就是想看在订阅之后的新的内容,而不是要阅读在订阅之前的旧的内容。

           对于这样的机制,相信大家想到了EventBus的post方法,那么在上述情景中,如果第二个阅读这也想要旧的报纸,那么就相当于EventBus的postSticky机制。

           基于这样的思想,我这里也封装了一个简易的LiveDataBus。我们先来看看这个简易的代码简易到什么样子:

    public class LiveDataBus { private static volatile LiveDataBus liveDataBus; private Map<String, HookMutableLiveData> cacheMutableLiveDatas; public static LiveDataBus get(){ if(liveDataBus == null){ synchronized (LiveDataBus.class){ if(liveDataBus == null){ liveDataBus = new LiveDataBus(); } } } return liveDataBus; } private LiveDataBus(){ cacheMutableLiveDatas = new HashMap<>(); } public <T> MutableLiveData<T> with(String key,Class<T> clazz){ HookMutableLiveData<T> mutableLiveData = cacheMutableLiveDatas.get(key); if(mutableLiveData == null){ mutableLiveData = new HookMutableLiveData<T>(); cacheMutableLiveDatas.put(key,mutableLiveData); } return mutableLiveData; } public class HookMutableLiveData<T> extends MutableLiveData<T>{ @Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { super.observe(owner, observer); try { //其实在这里hook的目的主要是为了在每次进行Observe的时候,都让他先不接受任何消息,也就是说必须是先订阅,然后发送的消息才能被接受(在内部原理中是因为每次 // setValue或者postValue的时候,都可以进行onChange方法的调用,因为在源码中如果想让他不执行,那么就可以让mLastVersion=mVersion即可,这样的话,在订阅的时候 // 不管是否发送过消息,都不会处理,但是此时如果通过postValue或者是setValue的话,那么就可以进行mVersion++,这个时候mVersion的值是大于mLastVersion的,此时就会执行 // onChange方法,也就可以进行消息分发了。) hookOnChange(observer); } catch (Exception e) { e.printStackTrace(); } } public void stickyObserve(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer){ super.observe(owner,observer); } private <T> void hookOnChange(Observer<? super T> observer) throws Exception{ Class<LiveData> liveDataClass = LiveData.class; Field mObserversField = liveDataClass.getDeclaredField("mObservers"); mObserversField.setAccessible(true); //获取mObservers的值,也就是SafeIterableMap对象 Object objectObservers = mObserversField.get(this); //获取SafeIterableMap 的class对象 Class<?> classObjectObservers = objectObservers.getClass(); //获取SafeIterableMap的get方法 Method safeIterableMapGetMethod = classObjectObservers.getDeclaredMethod("get", Object.class); //因为原本的权限是protected safeIterableMapGetMethod.setAccessible(true); //执行get方法,并且获取返回值是map.entry的 Object objectWrapperEntry = safeIterableMapGetMethod.invoke(objectObservers, observer); Object objectWrapper = null; if(objectWrapperEntry instanceof Map.Entry){ //getValue方法返回的值其实是ObserverWrapper的子类 objectWrapper = ((Map.Entry)(objectWrapperEntry)).getValue(); } if (objectWrapper == null) { throw new NullPointerException("Wrapper can not be bull!"); } //获取objectWrapper的class的父类class Class<?> superclass = objectWrapper.getClass().getSuperclass(); //mLastVersion属性是在objectWrapper中的 Field mLastVersion = superclass.getDeclaredField("mLastVersion"); mLastVersion.setAccessible(true); //获取LiveData的mVersion属性 Field mVersion = liveDataClass.getDeclaredField("mVersion"); mVersion.setAccessible(true); //获取mVersion的值 Object mVersionValue = mVersion.get(this); //设置mLastVersion的值 mLastVersion.set(objectWrapper,mVersionValue); } } }

    在这个封装的LiveDataBus中,就是针对上述情景中的两种情况的处理,首先来说说不经过处理的observe的方式,在我们封装的类中就是stickyObserve方法,调用的就是父类的observe方法了,在这种情况下,我们的订阅者是可以接受历史消息的。这里我们主要说说上述情景中的第二种情况,也就是订阅者不处理历史消息。说到处理消息,我们还要回到

    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; //noinspection unchecked observer.mObserver.onChanged((T) mData); }

    在这里,我们发现如果想要消息不被处理,那么就要让onChanged方法不被调用,在上面代码中,我们可以处理的地方似乎就是第三个if语句了,如果让这个if语句的条件返回true,那么就不会执行onChanged方法了,这个时候也就是需要observer.mLastVersion = mVersion这种情况最方便了,如何让他们相等呢?这就需要我们的hook技术了,在上述封装的代码中已经体现出来了,而且给出了相应的注释,相信都能看懂。

    那么这个时候可能又有读者会问了,如果让他们相等,那这样其他的订阅者会不会有影响呢?答案是 不会有影响,我们回过头来看看这里,

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

    发现没有,这个mVersion会在发送消息的时候自动加一,那么这个时候observer.mLastVersion的值是必然小于mVersion的,也就是说第三个if的条件是false的,这个时候就回去调用onChanged方法了。

    总结

           好了,说到这里,文章开头说的两个问题也都讲完了,不知道大家都懂了没。不过在这里这个封装还是太过简单,很多地方还没注意,比如进程通信等等都还没加进去,不过我看到有人在这里面用了广播来作为进程间通信去处理这个消息,不过,就我个人而言,在这里用广播的话效果不会那么好,所以大家不知道有没有新的思路去开发新的特性呢。

          文章如有错误,请大家指正,谢谢!!!

    最新回复(0)