打造自己的EventBus

看到EventBus的第一眼,真的觉得很惊艳。

觉得它牛逼的地方有两点:

1.支持指定事件处理的线程。避免在处理事件的时候还要自己写代码跳转到不同的线程,能简化不少工作。

2.没有使用interface,不同的事件处理函数,可以采用不同的名称,方便区分,方便不同事件的处理逻辑分离。很棒吧。代码可读性提升了。

进一步深入研究发现几点很不爽的地方,以至于我放弃它,自己另外实现:

1.强引用,注册之后,必须保证unregister。实际使用的时候,很难保证所有人都去unregister。

2.事件都是根据类型来判断的,特别还是支持继承关系。这个看似很强大,实际上我感觉如果真这么用,事件不断增加,继承关系的层级越来越多,以后的维护和调试都是恶梦。不过我觉得客户端完全不需要这么复杂的结构,没人会这么用。反而我本身的需求是,同样类型的对象表示不同的事件。

3.方法名称后缀来指定线程以及监听消息。假如出现拼写错误,要找出个问题,可能要花不少事件。

说了这么多。那么我要实现的EventBus,将针对上述3点吐糟来做改进:

1.使用weakreference,即使没有unregister也不干扰对象释放;

2.事件类型通过字符串标识,具体事件相关的数据直接使用Object类型,即使没有任何数据也行。就像下班了这个简单的事件,你告诉我下班了,不需要其它任何附加信息,我也知道我该关掉电脑然后回家。

3.我们对方法名称没有任何要求,我们使用注解来标识事件处理函数和指定处理线程。

下面开工,首先定义一个名为OnEvent的注解,注册有两个参数,枚举类型的threadMode用来指定处理线程。字符串类型的eventType用来指定事件类型

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnEvent {
    ThreadMode threadMode() default ThreadMode.Async;
    String eventType();
}

public enum ThreadMode {
    /**
     * deliver event in main thread(UI thread)
     */
    MainThread,
    /**
     * deliver event in thread that post the event
     */
    PostThread,
    /**
     * deliver event in background threads,
     * not the post one and main thread
     */
    Async
}

接着实现我们自己的EventBus类:

主要方法有register方法来注册事件监听,unRegister来取消注册,postDelayed来延时传递事件,post来传递事件。

public class EventBus{
    ……
    public void register(Object receiver) {
        if(receiver==null)return;
        List<ReceiverMeta> metas = ReceiverParser.parse(receiver);
        storage.addReceiver(receiver, metas);
    }

    public void unRegister(Object receiver) {
        if(receiver==null)return;
        List<ReceiverMeta> metas = ReceiverParser.parse(receiver);
        storage.removeReceiver(receiver, metas);
    }
    ……
    public void postDelayed(Object event, String eventType, long mills) {
        try {
            bgPoster.postEventDelayed(event,eventType, mills);
        } catch (InterruptedException ex) {
            post(event,eventType);
        }
    }

    public void post(Object event,String eventType) {
        List<ReceiverWrapper> receiverWrappers = storage.getReceivers(eventType);
        if (receiverWrappers != null) {
            for (ReceiverWrapper wrapper : receiverWrappers) {
                switch (wrapper.meta.threadMode) {
                    case PostThread:
                        deliverThroughCurThread(wrapper, event);
                        break;
                    case MainThread:
                        deliverToMainThread(wrapper, event);
                        break;
                    case Async:
                        deliverAsynchronized(wrapper, event);
                        break;
                }
            }
        }
    }
    ……
}

1.register

register调用ReceiverParser类的静态方法parse来解析对象监听的事件以及方法。

该方法会遍历对象类型中所有的方法,找出其中public以及有@OnEvent注解的方法,且没有参数或者只有一个Object类型参数方法。每个解析出来的Method都会被封装成一个个的ReceiverMeta对象。最后返回的是一个List<RecevierMeta>实例。

register获得List<ReceiverMeta>对象,再调用ReceiverStorage的对象的addReceiver方法保存监听信息,在addReceiver方法中,监听者会被WeakReference包装起来。使用弱引用,避免干扰正常的对象回收。监听者的弱引用以及ReceiverMeta会被封装成ReceiverWrapper对象中,然后调用addReceiverWrapper方法,将ReceiverWrapper对象存入到列表中,列表再存入到hash map中,key为事件名称。

2.unRegister

unRegister同样调用ReceiverParser类的parse方法解析出ReceiverMeta列表,调用ReceiverStorage的removeWrapper方法一个个的移除ReceiverWrapper对象来取消注册。

3.postDelayed

postDelayed使用BackGroundPoster实例的postEventDelayed来实现延时传递。

public class BackGroundBus {
    private Looper looper;
    private final Thread thread;
    private Handler handler;
    private final EventBus eventBus;
    private final CountDownLatch cdl=new CountDownLatch(1);

    public BackGroundBus(final EventBus eventBus){
        this.eventBus=eventBus;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                BackGroundBus.this.looper = Looper.myLooper();
                BackGroundBus.this.handler=new Handler(looper){
                    @Override
                    public void handleMessage(Message msg) {
                        if(msg.obj!=null&&msg.obj instanceof DelayedEvent){
                            DelayedEvent delayedEvent=(DelayedEvent)msg.obj;
                            eventBus.post(delayedEvent.event,delayedEvent.eventType);
                        }
                    }
                };
                cdl.countDown();
                Looper.loop();
            }
        });
        thread.start();
    }

    public void postEventDelayed(Object event,String eventType,long millis) throws InterruptedException{
        cdl.await();
        DelayedEvent delayedEvent=new DelayedEvent();
        delayedEvent.event=event;
        delayedEvent.eventType=eventType;
        Message msg=handler.obtainMessage();
        msg.obj=delayedEvent;
        handler.sendMessageDelayed(msg, millis);
    }

    public void stop() throws InterruptedException {
        cdl.await();
        looper.quit();
    }
}

BackGroupPoster 通过内建的Thread以及对应的Looper来实现事件的循环处理,最终还是通过EventBus实例的post方法来传递事件。

4.post

post方法通过事件名称(字符串)来找出ReceiverStorage实例中存储的ReceiverWrapper对象,然后循环处理,通过ReceiverWrapper对象指定的线程来传递事件。

post又分别通过下面三个方法,将事件通过当前线程、主线程、或者异步的方式传递。

   private void deliverThroughCurThread(final ReceiverWrapper wrapper, final Object event) {
        try {
            wrapper.receive(event);
        } catch (Exception e) {
            onException(e);
        }
    }

    private void deliverToMainThread(final ReceiverWrapper wrapper, final Object event) {
        mainThreadPoster.deliverEvent(wrapper, event);
    }

    private void deliverAsynchronized(final ReceiverWrapper wrapper, final Object event) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    wrapper.receive(event);
                } catch (Exception e) {
                    onException(e);
                }
            }
        });
    }



5.错误监听

   /**
     * 异常监听器
     */
    public static interface ExceptionListenner{
        void onException(Exception e);
    }

通过EventBus实例的setExListenner方法可以指定异常监听器来处理异常。

之所以使用这种模式,是为了避免异常影响事件的继续传递。


6.postDelayed需要注意的地方

通过postDelayed传递的事件是无法实现当前线程传递的,如果制定了当前线程传递,最后是在BackGroundPoster的线程中传递的。


最后,附上源代码:SimpleEventBus


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值