天天看点

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

Android LiveDatabus非黏性事件

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

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

目录

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

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

3.怎么解决?

4.LiveDataBus的完整代码为:

5.结语

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

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

LiveDatabus的代码:

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

试验的结果为:

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

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

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件
Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

可以看见,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非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

哦豁,这样子?原来还真是,在调用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 BusMutableLiveData<T> extends MutableLiveData<T> {
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        hookVersion(this);
        super.observe(owner, observer);

    }

}

private <T> 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();
    }
}
      
private synchronized BusMutableLiveData<T> getChenal(String key) {
    if (caches.get(key) == null) {
        caches.put(key, new BusMutableLiveData<T>());
    }
    return caches.get(key);
}      

修改后我们再来看下效果

Android LiveDatabus非黏性事件Android LiveDatabus非黏性事件

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

4.LiveDataBus的完整代码为:

public class Databus<T> {
    private static Databus databus;

    private static String TAG = "DatabusUtil";

    private Map<String, BusMutableLiveData> caches;

    private Databus() {
        caches = new HashMap<>();
    }

    public static Databus getInstance() {
        if (databus == null) {
            databus = new Databus();
        }

        return databus;
    }


    private synchronized BusMutableLiveData<T> getChenal(String key) {
        if (caches.get(key) == null) {
            caches.put(key, new BusMutableLiveData<T>());
        }
        return caches.get(key);
    }


    public void publishSet(String key, T value) {
        MutableLiveData<T> chenal = getChenal(key);
        chenal.setValue(value);
    }


    public void publishPo(String key, T value) {
        synchronized (Databus.class) {
            MutableLiveData<T> chenal = getChenal(key);
            chenal.postValue(value);
        }
    }

    public void regist(String key, LifecycleOwner lifecycle, Observer<T> observer) {
        getChenal(key).observe(lifecycle, observer);

    }


    private class BusMutableLiveData<T> extends MutableLiveData<T> {
        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            hookVersion(this);
            super.observe(owner, observer);

        }

    }

    private <T> 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.结语

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