关于RxBus的Stick特性探究

使用RxBus有一段时间了,记得刚使用的使用我在ActivityB中去注册RxBus,这个时候ActivityB还没有启动,然后我在ActivityA中post事件后启动ActivityB,然而ActivityB并没有接收到任何事件,当时还郁闷了很久。

ActivityB接收不到事件的主要原因在于我们的ActivityB create之前发送了事件这个时候由于我们ActivityB并没有创建,所以也没有进行事件注册,当然收不到任何事件了,我当时的做法是调用handler.postDelay(500)进行延迟一下发送事件,这个时间是我测试ActivityB完全创建后的时间,这个时候由于ActivityB已经创建并且注册了事件,ActivityA发送的事件也可以被接收了,但是这种做法并不优雅靠谱,因为Activity创建时间多少完全不可预料,导致我们没办法去估计一个准确的延迟事件,并且这种做法实在不优雅。。。。
所以我一直在想难道事件只能在Activity创建后才能发送吗?可以不可在它创建前就发送,创建成功后接受到呢?

我们看下面实现代码

/**
 * Created by wubo on 2017/6/2.
 */

public class RxBus<T> {


    private static RxBus mRxBus;


    private PublishProcessor<T> mPublishProcessor;
    private ConcurrentHashMap<Class, T> mConcurrentHashMap;

    private RxBus() {
        mPublishProcessor = PublishProcessor.create();
        mConcurrentHashMap = new ConcurrentHashMap();

    }

    public static RxBus getInstance() {
        if (mRxBus == null) {
            synchronized (RxBus.class) {
                if (mRxBus == null) {
                    mRxBus = new RxBus();
                }
            }
        }
        return mRxBus;
    }

    //register
    public Flowable<T> registerEvent(Class<T> clazz) {
        Flowable <T>tFlowable = mPublishProcessor.ofType(clazz);
        return tFlowable;
    }

    public void post(T event) {
        mPublishProcessor.onNext(event);
    }

    public Flowable registerStickEvent(final Class<T> clazz) {

        final T t = mConcurrentHashMap.get(clazz);
        if (t != null) {
            Flowable<T> tFlowable = mPublishProcessor.ofType(clazz);
            return tFlowable.mergeWith(Flowable.create(new FlowableOnSubscribe<T>() {
                @Override
                public void subscribe(FlowableEmitter<T> e) throws Exception {
                        e.onNext(t);
                }
            }, BackpressureStrategy.BUFFER));
        }

        return mPublishProcessor;

    }

    public void postStick(T event) {
        T t = mConcurrentHashMap.get(event.getClass());
        if (t == null) {
            mConcurrentHashMap.put(event.getClass(), event);
        }
        post(event);
    }

    public void removeAllStickEvent(){
        mConcurrentHashMap.clear();
    }

}

我们可以这么写,为什么Stick事件注册的时候需要合并一个流呢,因为如果我们这里首先需要返回一个Flowable提供订阅的,那么如果我们直接返回mPublishProcessor的话我们怎么接收到Stick事件呢? 所以我们merge一个流当我们注册时就发送我们map中存入的stick事件。

这里还有个小问题,当我们订阅一个被观察者我们销毁的时候需要解除订阅,否则会继续持有引用对象,导致内存泄露.

Flowable<String> flowable = RxBus.getInstance().registerStickEvent(String.class);
        flowable.subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.e("wwwSecondActivity",s);
            }
        });

比如这段代码,我们在SecondActivity中使用RxBus注册一个Stick事件,然后在MainActivity中开开心心的调用

RxBus.getInstance().postStick("MainActivity");

发送一个Stick事件
好我们看看Log打印日志
点击按钮跳到第二个界面

06-03 16:15:06.051 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity

吆西貌似效果不错哦~~等等我们按返回键退出这个界面然后再进来试一试

06-03 16:16:46.714 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:16:46.749 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity

这。。这怎么回事,我只发送一次,怎么收到二个事件抱着大大的❓我返回这个界面再进来一次看看,

06-03 16:18:00.767 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:18:00.767 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:18:00.814 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity

可以看到情况很不妙每次我们退出界面再进来的时候这个事件就会多接收一次一直累加,我们分析一下问题可能出现在哪。

在上一篇关于RxBus实现思考我们分析到当我们使用subject onNext()去发送事件时候我们会发送到我们所有用subject 订阅
的里面,而我们第一次接收一次说明我们订阅了一次,而退出来再进来接收到二次说明我们订阅了二次,可你会疑惑我界面都退出了啊,不错,Rx是不会主动帮你去解除你的订阅关系的,所以当你第一次订阅的时候虽然你退出了但是被订阅者引用还在RxBus的Subject里,每订阅并退出一次,这个引用会增加一个,所以当你发送事件的时候满足类型会接收之前所有订阅过的事件。

哪这多坑啊怎么办呢?没事rx可能也想到一点帮我提供一个CompositeDisposable类,它可以组装我们所有的订阅关系,然后统一进行解绑,我们可以创建一个方法

    public void addDispose(Disposable disposable){
        mCompositeDisposable.add(disposable);
    }

然后在onDesdory()中进行统一解绑

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mCompositeDisposable!=null&&!mCompositeDisposable.isDisposed()){
            mCompositeDisposable.dispose();
        }
        RxBus.getInstance().removeAllStickEvent();
    }

这样不管我们怎么退出界面都没有关系,我们当前注册事件的界面只会一对一的接收发送过来的事件。
isDisposed()是2.0新方法判断CompositeDisposable有没有解绑
如果已经解绑就会返回true如果没有的话就返回false
我们看看源码是不是这样

    @Override
    public void dispose() {
        if (disposed) {
            return;
        }
        OpenHashSet<Disposable> set;
        synchronized (this) {
            if (disposed) {
                return;
            }
            disposed = true;
            set = resources;
            resources = null;
        }

        dispose(set);
    }

    @Override
    public boolean isDisposed() {
        return disposed;
    }

isDisposed()返回disposed变量而当我们调用dispose()丢弃我们绑定关系后 disposed = true;会被赋值为true

    @Override
    public boolean add(Disposable d) {
        ObjectHelper.requireNonNull(d, "d is null");
        if (!disposed) {
            synchronized (this) {
                if (!disposed) {
                    OpenHashSet<Disposable> set = resources;
                    if (set == null) {
                        set = new OpenHashSet<Disposable>();
                        resources = set;
                    }
                    set.add(d);
                    return true;
                }
            }
        }
        d.dispose();
        return false;
    }

add方法也是如此如果disposed是true的话说明已经解绑就不添加到集合里,直接把参数解绑然后返回false说明添加失败.

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 文章转自:http://gank.io/post/560e15be2dca930e00da1083作者:扔物线在正...
    xpengb阅读 7,017评论 9 73
  • 原文地址:http://gank.io/post/560e15be2dca930e00da1083 前言 我从去年...
    AFinalStone阅读 2,184评论 5 23
  • 整个车厢在刹车声后突然陷入一种死寂,前一秒大家还在尖叫着,此时都屏住了呼吸,惊魂未定地互相张望着,都不知道发生了什...
    下一年的秋天阅读 223评论 0 2