EventBus3.0使用及源码解析

EventBus3.0使用

EventBus3.0官方使用文档
EventBus3.0GitHub地址
在EventBus3.0的官方文档中,我们可以从下面几个步骤去使用EventBus来实现事件响应机制。

官方图片
上图是官方的角色协作图,可以看出EventBus有一下几个角色

  • Event :传递的事件对象
  • Subscriber : 事件的订阅者
  • Publisher : 事件的发布者
  • ThreadMode : 定义函数在哪种类型的线程中执行

1.事件(Event)定义

在gradle中配置EventBus

compile 'org.greenrobot:eventbus:3.0.0'

之后我们定义一个简单的Event


public class MessageEvent {

    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}

2.定义订阅者的响应事件

订阅者事件就是事件触发时响应的方法,这些方法用@Subscribe 注解声明。

// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
    doSomethingWith(event);
}

当然,订阅者是需要在EventBus中注册和解除注册,才能完成一个正常的流程,一般推荐在Activity和Fragment的onStart和onStop生命周期方法中进行Subscribe的注册和解除注册。

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

3.发送事件

在代码中发送一个Event事件,当前所有订阅了该事件的订阅者都能响应这个事件。

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

ThreadMode选择

ThreadMode是用来控制Subscribe方法被调用时的线程,它有以下几种选择方式

1.ThreadMode.POSTING

即onMessage方法在跟发送方法(post)同一个线程被调用,这种模式意味着最少的开销,因为它完全避免了线程切换。所以在一些耗时较短的任务可以选择这种模式。这也是默认的ThreadMode。

// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
    log(event.message);
}

2.ThreadMode: MAIN

显而易见,这种模式下响应方法会在Android主线程(UI)被调用。故不赘述。

// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}

3.ThreadMode: BACKGROUND

Subscribers方法 会在一个后台线程被调用。这种模式下分两种情况,
一、当post方法不在主线程时,Subscribers方法 会直接在跟post同一个线程被调用。
二、当post方法在主线程时,


// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
    saveToDisk(event.message);
}

4.ThreadMode: ASYNC

这种模式下,事件处理方法在一个单独的线程中被调用。不会阻塞posting的线程,为了避免触发大量的长时间运行的异步Handler方法同时限制并发线程的数量,EventBus使用线程池来重用线程处理异步事件响应。

// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
    backend.send(event.message);
}

订阅事件的优先级

按照订阅事件的优先级高低,来决定同类型响应事件方法的调用顺序。

  @Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui线程执行 优先级100
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }

跟广播的传递规则类似,高优先级的响应方法可以终止事件的传递

 EventBus.getDefault().cancelEventDelivery(event) ;//优先级高的订阅者可以终止事件往下传递

stickyEvent

发送StickyEvent:

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

接受StickyEvent:


@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);    
    super.onStop();
}

解除注册:

MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // "Consume" the sticky event
    EventBus.getDefault().removeStickyEvent(stickyEvent);
    // Now do something with it
}

使用annotationProcessor

EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,
处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的
信息速度要快.


android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
            }
        }
    }
}

dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

如果上面的配置不起作用,可以使用apt配置



buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"
    }
}

配置完成后 rebuild工程后,就会在\build\generated\source\apt\PakageName\目录生成MyEventBusIndex 索引,
添加索引:

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
EventBus eventBus = EventBus.builder()
    .addIndex(new MyEventBusAppIndex())
    .addIndex(new MyEventBusLibIndex()).build();

EvenEvent3.0 post事件分发流程分析

post流程

首先是调用post方法发送事件

 /** Posts the given event to the event bus. */
     public void post(Object event) {
        //得到当前线程的Posting状态.
        PostingThreadState postingState = currentPostingThreadState.get();
        //获取当前线程的事件队列
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                //一直发送
                while (!eventQueue.isEmpty()) {
                    //发送单个事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

post() 方法首先从 currentPostingThreadState 对象中取了一个 PostingThreadState ,我们来看看这个 currentPostingThreadState 对象的创建代码

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new
ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。 ThreadLocal 所包裹的是一个 PostingThreadState 类,它仅仅是封装了一些事件发送中过程所需的数据。

final static class PostingThreadState {
    //通过post方法参数传入的事件集合
    final List<Object> eventQueue = new ArrayList<Object>(); 
    boolean isPosting; //是否正在执行postSingleEvent()方法
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
    }

接下来我们看下发送事件的核心方法,postSingleEvent:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //是否触发订阅了该事件(eventClass)的父类,以及接口的类的响应方法.
        if (eventInheritance) {
            //查找eventClass类所有的父类以及接口
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //循环postSingleEventForEventType
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                //只要右边有一个为true,subscriptionFound就为true
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //post单个
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        //如果没发现
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                //发送一个NoSubscriberEvent事件,如果我们需要处理这种状态,接收这个事件就可以了
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

继续查看postSingleEventForEventType方法

 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        //获取订阅了这个事件的Subscription列表.
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                //是否被中断
                boolean aborted = false;
                try {
                    //分发给订阅者
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

获取订阅了这个事件的Subscription列表,然后根据订阅者的ThreadMode,按照不同的线程切换方式依次分发给订阅者

void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

这里是用到了反射机制,调用响应事件的方法,并将event作为参数传递过去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值