Android LiveDatabus非黏性事件

Android LiveDatabus非黏性事件

原文链接:https://blog.csdn.net/luotianyi_yi/article/details/103301763


Android LiveDataBus的使用这里不再赘述,网上有很多关于这个的文章。

网上大部分篇幅采用的hook方式经亲自验证不生效,所以,经过分析,自己使用了另一种方式


1.网上取消黏性事件的方法及实验结果

在Activity中 发射(set)数据,然后 在onResume中延迟3秒后在订阅。NonStickActivityDemo的部分代码为:

Android LiveDatabus非黏性事件_第1张图片

Android LiveDatabus非黏性事件_第2张图片

Android LiveDatabus非黏性事件_第3张图片

LiveDatabus的代码:

Android LiveDatabus非黏性事件_第4张图片
Android LiveDatabus非黏性事件_第5张图片

试验的结果为:

Android LiveDatabus非黏性事件_第6张图片

可以看见 ,即使在发射之后注册观察者,仍然是可以接收上次发送的数据 也就是黏性

2.分析网上方式仍然能收到黏性事件的原因

仍然能收到黏性事件的原因,关键在于这种方式,在修改Observer的mLastVersion之前调用了super.observe(owner, observer);方法。但是如果不调用super.observe(owner, observer);那么由于此时还没有注册观察者,执行get方法拿出当前观察者对应的对象Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);会返回空。那么,我们来分析下,为什么调用了super.observe(owner, observer);就会接收到黏性数据。来跟踪下源码:

Android LiveDatabus非黏性事件_第7张图片

进入super.Observe(owner,observer);也就是LiveData的observe(owner,observer)方法

Android LiveDatabus非黏性事件_第8张图片

继续跟进 ,这个方法最后会执行owner.getLifecycle().addObserver(wrapper);这个方法,其中wrapper这个参数,它是一个LifecycleBoundObserver 的实例,来看下LifecycleBoundObserver 的源码:

Android LiveDatabus非黏性事件_第9张图片
Android LiveDatabus非黏性事件_第10张图片
Android LiveDatabus非黏性事件_第11张图片
Android LiveDatabus非黏性事件_第12张图片

可以看见,LifecycleBoundObserver 是ObserverWrapper的子类,关于ObserverWrapper这里不做分析,之前的网上分析黏性事件产生原因的文章已经做了介绍。其实就是一个观察者Observer(androidx.lifecycle.Observer)的封装。

当LifecycleBoundObserver 的onStateChanged方法被回调时,如果LiveData的mVersion>observer(其实就是wrapper ObserverWrapper的实例,这里是LifecycleBoundObserver 的实例)的mLastVersion时就会调用其内部observer对象通知观察者。

那么,这个onStateChanged方法是什么时候被调用呢?为什么hook不生效?猜想一下,难道是调用supper.observe(owner,observer);之后,有了观察者,就会立即把之前的数据发送给observer?

我们来验证一下。继续跟进owner.getLifeCycle().addObserver(wrapper);方法,看看这个方法究竟干了啥。进入到了LifeCycle类里面,发现这是一个抽象方法

Android LiveDatabus非黏性事件_第13张图片

那么,就去找它的子类。还记得我们在Demo里面,NonStickDemo里面调用注册观察者方法

Android LiveDatabus非黏性事件_第14张图片

可以看见,传入了this,因此,我们可以进入AppCompatActivity中去看下getLifeCycle()方法

Android LiveDatabus非黏性事件_第15张图片

什么情况,没有?那去父类里面找,继续跟进,进入FragmentActivity里面,还是没有找到,那就继续跟进,ComponentActivity

Android LiveDatabus非黏性事件_第16张图片

原来是ComponentActivity实现了这个方法,看下这个方法,返回了mLifecycleRegistry,这个mLifecycleRegistry是LifecycleRegistry的实现类

那么,我们要查看的addObserver(wrapper)方法应该就是在LifecycleRegistry类或者它的父类里面实现的,来看下LifecycleRegistry是啥玩意儿。进去LifecycleRegistry里面看看

Android LiveDatabus非黏性事件_第17张图片

看关键地方,先不管这个statefulObserver是啥,也不管dispatchEvent方法里面传入的参数是啥,先进去看看。

Android LiveDatabus非黏性事件_第18张图片

哦豁,这样子?原来还真是,在调用owner.getLifecycle().addObserver(wrapper);这个方法添加观察者后,就会立即将之前发送的数据发送给Observer。所以,无论你后面怎么hook,观察者已经接收了数据,更改mLastVersion也没用。

3.怎么解决?

由于发送(就是调用liveData的setValue()或者postValue())事件在注册观察者之前,而且每次setValue都会使liveData的mVersion加1,由于LiveData的mVersion初始值和ObserverWrapper的mLastVersion的初始值都是-1(START_VERSION = -1)所以只要先发送数据,当注册观察者时,livaData的mVersion一定大于ObserverWrapper的初始值且大于其自身的初始值-1。

既然是因为LiveData的mVersion>Observer的mLastVersion造成黏性事件,那么,我们要想实现非粘性事件,可以在注册观察者之前,利用反射,将之前已经调用了setValue或者postValue的LiveData的mVersion设置为-1就可以了。

新建BusMutableLiveData类继承MutableLiveData,并重写其observe方法,然后在LiveDataBus类的getChanel()方法中使用,代码如下:

private class BusMutableLiveDataextends MutableLiveData {

@Override

    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {

hookVersion(this);

        super.observe(owner, observer);

    }

}

private void hookVersion(BusMutableLiveData data) {

Class liveDataClass = LiveData.class;

    Field mVersion =null;

    try {

mVersion = liveDataClass.getDeclaredField("mVersion");

        mVersion.setAccessible(true);

        int version = ((int) mVersion.get(data));

        Log.e(TAG, "hookVersion:LiveData.mVersion =  " + version);

        if (version != -1) {

mVersion.set(data, -1);

        }

}catch (NoSuchFieldException e) {

e.printStackTrace();

    }catch (IllegalAccessException e) {

e.printStackTrace();

    }

}

修改后我们再来看下效果

Android LiveDatabus非黏性事件_第19张图片

可以看见,没有黏性数据了,也不影响正常数据的接收。

4.LiveDataBus的完整代码

                public class Databus {

private static Databusdatabus;

    private static StringTAG ="DatabusUtil";

    private Mapcaches;

    private Databus() {

caches =new HashMap<>();

    }

public static DatabusgetInstance() {

if (databus ==null) {

databus =new Databus();

        }

return databus;

    }

private synchronized BusMutableLiveDatagetChenal(String key) {

if (caches.get(key) ==null) {

caches.put(key, new BusMutableLiveData());

        }

return caches.get(key);

    }

public void publishSet(String key, T value) {

MutableLiveData chenal = getChenal(key);

        chenal.setValue(value);

    }

public void publishPo(String key, T value) {

synchronized (Databus.class) {

MutableLiveData chenal = getChenal(key);

            chenal.postValue(value);

        }

}

public void regist(String key, LifecycleOwner lifecycle, Observer observer) {

getChenal(key).observe(lifecycle, observer);

    }

private class BusMutableLiveDataextends MutableLiveData {

@Override

        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {

hookVersion(this);

            super.observe(owner, observer);

        }

}

private void hookVersion(BusMutableLiveData data) {

Class liveDataClass = LiveData.class;

        Field mVersion =null;

        try {

mVersion = liveDataClass.getDeclaredField("mVersion");

            mVersion.setAccessible(true);

            int version = ((int) mVersion.get(data));

            Log.e(TAG, "hookVersion:LiveData.mVersion =  " + version);

            if (version != -1) {

mVersion.set(data, -1);

            }

}catch (NoSuchFieldException e) {

e.printStackTrace();

        }catch (IllegalAccessException e) {

e.printStackTrace();

        }

}

}

5.结语

本人是初学者,在使用过程中发现了这个问题,如果有理解不恰当之处,欢迎大家提出宝贵的意见。

你可能感兴趣的:(Android LiveDatabus非黏性事件)