EventBus的使用以及学习心得

本文详细介绍EventBus的使用步骤,包括库导入、事件定义、注册注销、消息处理及发送。解析EventBus的ThreadMode作用,深入源码分析消息传递流程,涉及post()方法、事件分发及不同线程模式下事件处理机制。

一、EventBus使用步骤:

    1. gradle导入EventBus库

implementation 'org.greenrobot:eventbus:3.1.1'

    2. 代码用使用EventBus
       (1). 定义自己所需要的Event事件类,在里面定义你需要“沟通”的内容==>Handler中的一个Message消息.
       //如果你想实现一个简单的注册登录界面,那么你需要传递的信息是:Name(昵称)、Password(密码)、Message(用户信息)

public class MessageEvent {
    private String mMessage;
    private String mName;
    private String mPassword;
    public MessageEvent() {
    }
    public void setMessage(String message) {
        this.mMessage = message;
    }
    public String getMessage() {
        return mMessage;
    }
    public void setName(String name) {
        this.mName = name;
    }
    public String getName() {
        return mName;
    }
    public void setPassword(String password) {
        this.mPassword = password;
    }
    public String getPassword() {
        return mPassword;
    }
    @Override
    public String toString() {
        return "Name = " + mName + ",  password = " + mPassword + ", message = " + mMessage;
    }
}

       (2). 注册/注销EventBus对象,以便在该类中使用之.
         //如果你想在两个Activity之间相互传递上面所定义的MessageEvent消息,则需要在你想要处理事件的Activity中完成EventBus的注册以及注销
        //建议:在Activity的标准生命周期中注册和注销EventBus,因为两者必须成对出现。如onStart()--onStop(); onCreate()--onDestory(); onResume()--onPause()

Activity:
protected void onStart() {
    super.onStart();
    EventBus.getDefault().register(this); //注册EventBus
}
protected void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this); //注销EventBus
}

      (3). 声明并注释好订阅方法.
         //此方法即是你想要拿到消息之后进行处理的method

@Subscribe(threadMode = ThreadMode.MAIN)
public void handleMessageEvent(MessageEvent event) {
    mName = event.getName();
    mPassword = event.getPassword();
    mMessage = event.getMessage();
    Toast.makeText(MainActivity.this, "handleMessageEvent: receive Message, name = " + mName +
           ", password = " + mPassword + ", message = " + mMessage, Toast.LENGTH_LONG).show();
}

     (4). 发送消息事件

MessageEvent messageEvent = new MessageEvent();
messageEvent.setMessage("Hello world");
messageEvent.setName("张三");
messageEvent.setPassword("123456"); //定义好需要发送消息的Event类里的内容
EventBus.getDefault().post(messageEvent); //发送该EventMessage

    至此,EventBus已经成功在你的项目里面搭建好并完成消息发送+处理了。
二、EventBus的ThreadMode
在上面的第三个步骤,我们可以看到,在声明并注释订阅方法时,需要指定一个threadMode。这个threadMode,其实就是指定当前handleEvent的方法跑在哪一个线程里面。常用的ThreadMode主要有四个: POSTING、MAIN、BACKGROUND、ASYNC。
    (1). ThreadMode.POSTING               当前handleEvent的方法的执行线程,和post这个Event的线程保持一致。如果是在主线程post该Event,handleEvent则在主线程;如果是在异步线程,则跑在同一个异步线程中。
    //如果你在postEvent和handleEvent之间需要保持线程的同步,则需要使用此Mode
    (2). ThreadMode.MAIN                     当前handleEvent的方法的执行线程永远都在主线程,不管post这个Event的线程是在主线程还是异步线程。
    //如果在handleEvent中需要处理一下UI的工作,则需要使用此Mode
    (3). ThreadMode.BACKGROUND    当前handleEvent方法的执行线程永远在异步线程。如果在主线程post该Event,则new一个新的异步线程处理该方法;如果是在异步线程post,则直接在该异步线程处理。
    //如果多个Subscribe注册了同一个MessageEvent,则会按顺序一个一个传下去处理
    (4). ThreadMode.ASYNC                 不管post该Event的线程是在主线程或者是异步线程,均会开启新的异步线程来执行handleEvent方法
    //如果在handleEvent中需要处理耗时的工作,使用此Mode
如果想要进一步深入了解EventBus的ThreadMode,建议访问EventBus的主页阅读: http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/

三、EventBus源码深入了解
1.发送消息事件,调用到EventBus的post()方法,该方法主要是将当前的event塞到一个eventQueue队列中,然后再从队列中出列一个Event,并将其传到postSingleEvent()方法
//如果不了解队列的工作原理和特性,建议先去源码查看一下Queue的使用

 /** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            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;
            }
        }
    }

2. 在前面我们可以看到,messageEvent是通过参数传递到了postSingleEvent()方法,而这个方法则是根据传进来的event类来分发相关的事件处理

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);  
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); //根据拿到的event和evnet的类型来进行消息的分发处理
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); //根据拿到的event和evnet的类型来进行消息的分发处理
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

3. 继续往postSingleEventForEventType()方法走下去,这个方法比较简单,就是根据事件的类型,从所有注册了订阅方法里面匹配出符合相关类型的subscribe,并将此事件通过postToSubscription()方法继续传下去

 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        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;
    }

4. 终于来到了最关键的地方了,在这里我们可以看到,EventBus是根据当前注册的订阅方法的threadMode,分别将event通过不同的方式传到相关的订阅方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING: //posting, 则直接调用subscribe过的方法,从这里可以看到,系统没有新开任何线程去调用订阅方法,所以posting类型是直接走在调用EventBus.getDefault().post(messageEvent)的线程的
                invokeSubscriber(subscription, event); 
                break;
            case MAIN: //main, 判断当前是否为主线程,如果是主线程,则直接调用订阅方法,否则,将该event以及subscribe入列到mainThreadPoster,一个主线程的poster中。具体这个mainThreadPoster是如果工作的,下面会详细讲解
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED: //main_ordered,  这个和main很相近,但是它是有一个顺序要求的。由于这个threadMode并不常用,所以在这里不做具体解释,有兴趣的同学可以在EventBus的官网上找到它的具体解释以及用法
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND: //background, 如果当前是主线程,则将event入列到backgroundPoster中,利用background线程进行事件的分发。这里使用到了backgroundPoster,和main一下,会在后面界面这个poster的工作原理
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC: //async, 这个和一开始讲的一样,不管是异步线程还是主线程,此event都是入列到asyncPoster中,通过asyncPoster来进行事件的分发
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

5. 在前一步我们可以看到,ThreadMode为MAIN、Background、async,分别使用了mainThreadPoster、backgroundPoster以及asyncPoster。那么这三个poster究竟是什么呢?在这里给大家详细展示一下:
    从命名来看,它们三者都有一个poster,所以我们可以很大胆的猜测,EventBus应该是定义了一个Poster接口或者是Poster的静态类,然后这三者都是继承与这个类或者实现了这个方法,然后分别完成相关的线程处理以及事件分发逻辑。
   通过全局查看,我们发现了这个Poster接口,虽然找到了这个接口,但是这个接口具体是怎么实现/怎么使用的呢,我们继续往上找

interface Poster {
    /**
     * Enqueue an event to be posted for a particular subscription.
     *
     * @param subscription Subscription which will receive the event.
     * @param event        Event that will be posted to subscribers.
     */
    void enqueue(Subscription subscription, Object event);
}

   通过Eclipse的快捷键Ctrl+T,我们可以发现,一个有三个类实现了这个Poster接口:HandlerPoster、AsyncPoster以及BackgroundPoster。其实,从这里大家应该就能知道了,这三者其实对应的就是上面所说的mainThreadPoster、asyncPoster以及backgroundPoster。(如果不信的同学,可以继续往上找,看一下这三个类都是谁在用,然后在对应在EventBus类中postToSubscription这里的三个对象,就能知道了)
那我们在这里再仔细看一下,这三个Poster究竟是何方神圣:
   (1). HandlerPoster

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    public 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");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

      看到这里,大家是不是都惊呆了!其实,HandlerPoster就是一个简单的集成Handler的类,通过sendMessage/handleMessage的方式完成了事件的处理分发。当然,大家可能会问,这个HandlerPoster是怎么变成主线程的mainThreadPoster的呢?
大家可以参考我上一篇文章 HandlerThread小讲解
      (2). BackgroundPoster

final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @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) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }
}

      BackgroundPoster就更简单了,直接是实现了一个Runnable接口,直接在run()方法中完成事件的分发。Runnable接口是怎么工作的,大家具体可以参考 Google API,这里就不详细陈述了。
      (3). AsyncPoster

class AsyncPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

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

}

      AsyncPoster和BackgroundPoster的工作原理基本上是一致的


至此,整一个EventBust的工作流程以及原理基本上就介绍完成了,当然,这里主要是简单的带大家走了一遍EventBus从post到接收事件的逻辑,里面还有很多很值得深入理解学习的知识点,这就靠大家的挖掘了。

转载于:https://my.oschina.net/mkonone/blog/2961749

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值