Android LiveDatabus非黏性事件

Android LiveDatabus非黏性事件

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


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

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


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

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

LiveDatabus的代码:

试验的结果为:

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

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

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

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

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

可以看见,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类里面,发现这是一个抽象方法

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

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

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

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

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

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

哦豁,这样子?原来还真是,在调用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();

    }

}

修改后我们再来看下效果

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350