手写RxBus

参考:用RxJava实现事件总线(Event Bus)
http://www.jianshu.com/p/ca090f6e2fe2

我这篇基本上就是按照上面那篇写的,对Sticky那一块进行了一些修改。
写下来让自己记得更深刻。这篇文章面向有RxJava基础的人,要是HelloWorld都没写过建议先看基础部分。

Git地址:
https://github.com/relengxing/RxBus

步骤

  1. 新建工程
  2. 添加rxjava和rxandroid依赖
  3. 完成以下界面
界面
  1. 编写RxBus文件
  2. 编写其他代码

RxBus是一个全局使用的总线,应该使用单例模式。
单例模式的具体写法可以自己研究下。
参考代码:
http://www.race604.com/java-double-checked-singleton/

/** 
* Created by relengxing on 2016/8/12. 
*/
public class RxBus {    
    private RxBus() {    

    }    
    public static RxBus getDefault() {        
        return HelperHolder.instance;    
    }    
    private static class HelperHolder {        
        public static final RxBus instance = new RxBus();    
    }
}

事件总线

那么需要一根总线来传输数据。
这根总线就是RxJava中的Subject。

Subject可以看成是一个桥梁或者代理,在某些ReactiveX实现中( 如RxJava) ,它同时充当了Observer和Observable的角色。因为它是一个Observer,它可以订阅一个或多个Observable;又因为它是一个Observable,它可以转发它收到(Observe)的数据,也可以发射新的数据。

在RxJava中
针对不同的场景一共有四种类型的Subject。

  • AsyncSubject
  • BehaviorSubject
  • PublishSubject
  • ReplaySubject
    关于这四种类型的具体说明参考:RxJava:Subject介绍

这里使用的是PublishSubject
PublishSubject:只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者;
又因为线程安全的问题,需要把PublishSubject转化为一个线程安全的Subject,这部分内容也在RxJava:Subject介绍最后一部分串行化中有介绍。
最后代码写成如下:

private final Subject<Object,Object> bus;
private RxBus() {    
    bus = new SerializedSubject<>(PublishSubject.create());
}

总线有了,还差事件发布者(被观察者)和事件接受者(观察者)。

发送事件

将事件post至Subject,此时Subject作为Observer接收到事件(onNext),然后会发射给所有订阅该Subject的订阅者。
因为使用的是PublishSubject,所以必须先订阅事件再发送事件才能介绍到,否则这些发送的事件会遗失。

public void post(Object object){    
    bus.onNext(object);
}

接收事件

public <T> Observable<T> toObservable(Class<T> eventType){ 
   return bus.ofType(eventType);
}

ofType 是 filter 操作符的一个特殊形式。它过滤一个Observable只返回指定类型的数据。ofType 默认不在任何特定的调度器上指定 。

有一点需要注意的是,在接收事件的地方不需要接收事件或者生命周期结束的时候一定要取消订阅,防止内存泄漏。

if (!rxSubscription2.isUnsubscribed()) {    
    rxSubscription2.unsubscribe();
}

支持Sticky事件

在Android开发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。Android中就有这样的实例,也就是Sticky Broadcast,即粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便无法接收到刚才的广播,为此Android引入了StickyBroadcast,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完Receiver后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者。

参考:深入RxBus:[支持Sticky事件]

关于方案选择不再详述了,参考上面的链接。
同样使用的是ConcurrentHashMap
参考资料中使用的是

private final Map<Class<?>, Object> mStickyEventMap;

那么同一个类只会有一个对象保留,后面发送的对象会把前面的对象覆盖掉。
而我希望一个新的对象不会覆盖老的对象,需要自己手动来删除。
所以这个地方改成

private final ConcurrentHashMap<Class<?>,List<Object>> map;

Sticky事件和普通事件使用的是同一个Bus,所以接收者接收的是同一个对象时,当他们都订阅了事件时是没有区别的。

发送Sticky事件

这个其实就是在发送普通时间之前把这个事件写入到刚刚的map中去。

public void postSticky(Object object){    
    synchronized (mStickyEventMap){       
        List list = mStickyEventMap.get(object.getClass());        
        if (list == null) {            
            list = new ArrayList();       
       }        
        list.add(object);        
        mStickyEventMap.put(object.getClass(),list);    
    }    
    post(object);
}

接收Sticky事件

这个就是先查看map中是否有这个事件,有的话使用.merginWith一起发出来。

public <T> Observable<T> toObservableSticky(final Class<T> eventType){    
    synchronized (mStickyEventMap){        
        Observable<T> observable = bus.ofType(eventType);            
        final List list =  mStickyEventMap.get(eventType);        
        if (list != null && !list.isEmpty()) {            
            return observable.mergeWith(Observable.create(new Observable.OnSubscribe<T>(){                
            @Override                
            public void call(Subscriber<? super T> subscriber) {                    
                  for (Object obj :list) {                        
                      subscriber.onNext(eventType.cast(obj));
                }
            }
        }));
        }else {
            return observable;
        }
    }
}

还写了一些常用方法,例如post一个事件的时候覆盖同类事件,接收事件时消耗掉事件,代码在简书上写起来还是有点麻烦,详情看GitHub,地址:https://github.com/relengxing/RxBus
要使用的时候把RxBus文件直接复制到工程即可。
如果有BUG可以在评论区告诉我。

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

推荐阅读更多精彩内容