EventBus 3 源码分析

eventBus3简单示例

首先添加依赖, compile ‘org.greenrobot:eventbus:3.0.0’
然后要先定义一个event。

public class MessageEvent {
    private String message;

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

    public String getMessage() {
        return message;
    }
}

第二步,在要订阅事件的类中,

        @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessage(MessageEvent messageEvent) {
        ((TextView)findViewById(R.id.textView)).setText(messageEvent.getMessage());
    }

因为eventbus 3中采用了注解实现,所以我们接受event的方法onMessage方法命名可以任意命名,什么getMessage,mainThreadMessage都可以,eventbus只是根据注解和参数MessageEvent来调用。

第三步,在发送消息的地方直接

EventBus.getDefault().post(new MessageEvent("I am poster"));

完整demo代码

public class SubscriberAcitivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((Button)findViewById(R.id.jump)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SubscriberAcitivity.this, PosterActivity.class));
            }
        });
        EventBus.getDefault().register(this);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMessage(MessageEvent messageEvent) {
        ((TextView)findViewById(R.id.textView)).setText(messageEvent.getMessage());
    }
}
public class PosterActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_poster);
        ((Button)findViewById(R.id.post)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new MessageEvent("I am poster"));
            }
        });
    }
}

效果就是在posterActivity中发的消息,被subscriberActivity接收到并依次更改了自己的UI。从这里可以一眼看出EventBus最大的好处,一是解耦,消息接受者和发送者没有任何关系,二是相对于传统的activity之间的通信机制:handler接收消息异步更改UI,eventBus为我们进行了封装简化了编写过程,当然在eventBus的内部也是通过handler实现的。


java的注解

先来看一下我们在subscriber中添加的注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    boolean sticky() default false;
    int priority() default 0;
}

首先注解的定义方法十分简单,类似接口public @interface Subscribe{},就定义了@Subscribe这个注解。然后在上面添加上元注解,元注解就是我们定义的这个注解的注解,描述了我们自定义注解的基本信息。可以看到上面有3个元注解
@Documented是用javadoc生成文档相关的
@Retention定义了该注解被保留的时间长短,Runtime的意思当然就是程序运行时也有效。
@Target则是注解针对什么使用,@Target({ElementType.METHOD})表示这个注解是使用与方法的。

然后就是注解中的成员变量,ThreadMode 是一个枚举,在为注解设置成员时,基本语法就是

类型 成员名称() default 默认值

在使用时,直接@Subscribe(threadMode = ,sticky = ,priority = )即可,不给即为默认值。


java中的反射

定义完注解,那么该如何使用呢,这个时候java的反射就派上用场了。
反射学术化的定义很多,但在这里我们的应用却很简单。
首先,在jvm类加载器加载类时会为每个类生成一个Class对象并放在方法区中,也就是说对于一个类,我们可能没有通过new 实例化出它的对象,但是我们可以拿到它的Class对象。简单来讲,程序一但运行,我们可以拿到每个类的全部信息,就是Class。
既然说了是全部信息,那么在Class中就会包含该类的全部方法,然后对于每一个方法又可以拿到该方法的全部注解或者指定注解。见下面的代码:

        Class<?> clazz = SubscriberAcitivity.class;
        for (Method method : clazz.getDeclaredMethods()){
        //拿到指定注解
            Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                                if (subscribeAnnotation != null) {
                                //通过注解,获取当时我们设定的几个值
                                subscribeAnnotation.threadMode();
                                subscribeAnnotation.sticky()
                                subscribeAnnotation.priority();
                    }
        };

也就是说,通过注解我们可以利用反射找到某个方法,然后注解中可以存放一些我们后面可能用到的属性。
最后利用反射调用method.invoke(clazz, new MessageEvent(“I am poster”)),表明我们的SubscriberActivity调用了@Subscribe注解标识的方法,给入的参数是MessageEvent。
可以看到,通过java反射,我们定义了方法,定义了类,但是对于类的实例化,甚至对于方法的调用,都不需要我们直接去做。也就是避免了new Object().method()这样的写法,将方法定义者和方法调用者松耦合。


event bus 源码分析

介绍完前面的基础,下面来分析一下eventBus的源码。
首先eventBus是一个典型的观察者模式,所以我们也就分观察者和被观察者两部分分析:

观察者 register

首先从入口进入,EventBus.getDefault().register(this);,把当前消息的订阅者注册到eventbus

    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

典型的DCL单例模式实现,获取EventBus实例后register。构造方法中调用真正构造方法,这里使用到里Builder模式,在builder中可以设置是否打印异常日志、发送异常事件、订阅者的父类是否可以接收event,是否使用线程池以,及诸如Subscriber Index的新特性的设置。

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

利用反射获取订阅者的Class对象。然后findSubscriberMethods中找遍历订阅者的所有方法,并找其中仅有一个参数,并且有注解@Subscribe的方法。通过注解获取ThreadMode等信息后封装进SubscriberMethod中,然后将订阅方法添加到并保存下来,最后存在一个哈希表 METHOD_CACHE中,表的键就是订阅者的Class对象,值就是接收事件的方法。然后将上面找到的方法遍历,并调用subscribe方法。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //依据event类型,获取针对一种event的全部订阅subscription
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //不存在初始化,存在则检查新的订阅是否存在
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            //根据优先级将新的订阅添加到队列指定位置。
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        //检查该 subscriber 已有的event事件类型并存储。
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        if (subscriberMethod.sticky) {
            ...
            //关于订阅方法设置sticky的一些额外处理。
        }
    }

可以看到,通过subscribe方法,将订阅者和接收不同事件的方法封装到了一起,然后根据event类型分类存放,可猜想这样可以根据event来通过Map将event发送给相关subscriber处理,同时还将subscriber所订阅的全部event存储了起来。

subscriber发布订阅已经完成,作为观察者模式中的观察者,现在即等待被观察者发出消息,接收并执行逻辑。


被观察者 post

被观察者发送event,观察者看到event后执行逻辑。
eventBus中通过post方法发布一个事件,也就是我们的自定义实体类XXXEvent。

public void post(Object event) {
        //通过ThreadLocal为每个线程维护一个变量副本,记录post线程的线程状态
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            //判断是否是UI线程
            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方法获取当前发起post线程的队列,并将event加入到队里中,然后将队列中所有事件拿出执行postSingleEvent

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            ...
            //如果事件可以被父类继承执行相关逻辑。
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            ...
            //没有找到订阅subscription的一些补救措施
        }
    }

在postSingleEventForEventType方法中,首先依据event,去我们前面说到的map中找订阅subscription,找到相关的subscription后在方法postToSubscription执行的是真正的调用。

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,来确定订阅者接收event后逻辑应该执行在哪个线程。如果是POSTING,则直接在post事件的线程中执行。执行这个postToSubscription的当然也就是post线程了,所以直接调用订阅者。调用的方式也十分简单,直接通过java反射机制,Method.invoke方法给入调用者和event参数即可。

如果不是当前线程,则加到UI线程、后台线程、异步线程对应的Poster的队列中去。下面分情况分析:


MAIN
先判断当前线程是否为UI线程,如果是就直接invoke。否则给到HandlerPoster的队列中,HandlerPoster实际是一个handler,并且在构造方法中将UI线程的Looper绑定到了这个handler中,这个HandlerPoster也就相当于执行在UI线程中。分析其enqueue方法

void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

PendingPost中封装的是订阅信息和当前发送出来的event,订阅信息中包含subscriber、以及处理相关event的method,handlerActive表示当前handler是否繁忙,不忙就让它处理消息,也就是自己给自己发送一个空消息,这样可以通过handler的机制,保证逻辑是执行在UI线程的。handler的handleMessage就是从这个队列中取出全部pendingPost然后通过反射调用了,当然这里还加上了一个时间判断,如果方法执行时间过长,重新给自己发送一个消息,然后结束自己。这样做也就是给UI线程喘气的时间,让它可以绘制界面,通过UI线程自己的消息机制,在不是那么忙的时候继续拿pendingPost,反射调用。当然,在调用subscriber的时候,还要判断其是否处于active状态,也就是说如果unrigster了的subscriber不接收事件。

BACKGROUND
逻辑类似MAIN,主要确保处理event的逻辑执行在后台线程,也就是 不是UI线程
BackgroundPoster是一个Runnable,其中封装了队列,Runnable的执行交给EventBus中定义的全局线程池。执行enqueue时先是同样将信息封装进PendingPost,然后在线程池不繁忙时,执行该Runnable。
线程做的事情就是从队列中拿出任务执行,执行完了结束其线程本身,没执行完在该线程中一直执行,通过反射进行类似的相关调用。

@Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

ASYNC
异步实现,立即通过线程池执行处理当前PendingPost,与background不同的是,background中的一个线程,作为后台线程会处理很多Post事件,而Async仅处理一个PendingPost。Background是和Main相对的概念,而ASYNC则是执行单独任务的轻量级的线程。BACKGROUND线程全局只有一个,而ASYNC可以有很多。

@Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

总结

EventBus就是一个总线,订阅者注册到这里,并把自己的方法存储进去。事件发送者发送event,去eventBus中寻找订阅者的信息及方法,然后通过eventBus选择执行的方式,进行方法调用。
至此,源码部分整体逻辑分析完成,当然还要很多其他特性其他设置,需要进一步研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值