EventBus3.0学习笔记

Stella981
• 阅读 662
public void onEvent(MessageEvent event) {
        log(event.message);
    }
public void onEventMainThread(MessageEvent event) {
        textField.setText(event.message);
    }
public void onEventBackgroundThread(MessageEvent event){
        saveToDisk(event.message);
    }

什么是EventBus

EventBus是一个发布 / 订阅的事件总线。git地址:https://github.com/greenrobot/EventBus

笼统来讲EventBus可以分为三部分,发布者、订阅者、总线。订阅者通过总线订阅事件,发布者通过总线发布时间,因此订阅者就可以收到发布者发布的事件了。

如何使用EventBus

EventBus的使用也是很简单的,首先来看几个方法:

EventBus.getDefault().register(this);//订阅事件

EventBus.getDefault().post(object);//发布事件

EventBus.getDefault().unregister(this);//取消订阅

首先需要在oncreat里面注册EventBus,类似广播的注册,当然也需要在destory的时候取消注册。在需要发送也就是发布消息的地方调用post方法,注意里面的参数,然后在接受也就是事件处理的地方写几个方法即可。在3.0以前接收事件是这样的:

 public void onEvent(MessageEvent event) {
        log(event.message);
    }
 public void onEventMainThread(MessageEvent event) {
        textField.setText(event.message);
    }
public void onEventBackgroundThread(MessageEvent event){
        saveToDisk(event.message);
    }

方法必须以onEvent开头,但是3.0以后采用了注解的方式:

    @Subscribe(threadMode = ThreadMode.MainThread) //在ui线程执行
    public void onUserEvent(UserEvent event) {
    }
    @Subscribe(threadMode = ThreadMode.BackgroundThread) //在后台线程执行
    public void onUserEvent(UserEvent event) {
    }
    @Subscribe(threadMode = ThreadMode.Async) //强制在后台执行
    public void onUserEvent(UserEvent event) {
    }
    @Subscribe(threadMode = ThreadMode.PostThread) //默认方式, 在发送线程执行
    public void onUserEvent(UserEvent event) {
    }

举个栗子

public class MainActivity extends Activity {

    private final static String TAG = "EventBusTest";  
    
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);
        // 1.注册事件订阅者("登录")
        EventBus.getDefault().register(this);  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        // 4.解除注册("注销")
        EventBus.getDefault().unregister(this);  
    }  
    
    public void testActivity(View view){
        Intent intent = new Intent(this,third.class);
        startActivity(intent);
    }
    
    // 3.接收方处理消息(处理数据)-- 主线程中执行  
    @Subscribe(threadMode = ThreadMode.MainThread)  
    public void onMainEventBus(MainMessage msg) {  
        Log.e(TAG, "onEventBus() handling message: " + Thread.currentThread().getName());  
    }  
  
    // 3.接收方处理消息(处理数据)-- 后台线程或子线程中执行
    @Subscribe(threadMode = ThreadMode.BackgroundThread)  
    public void onBackgroundEventBus(BackgroundMessage msg) {  
        Log.e(TAG, "onEventBusBackground() handling message: " + Thread.currentThread().getName());  
    }  
  
    // 3.接收方处理消息(处理数据)-- 后台线程中执行  
    @Subscribe(threadMode = ThreadMode.Async)  
    public void onAsyncEventBus(AsyncMessage msg) { 
        Log.e(TAG, "onEventBusAsync() handling message: " + Thread.currentThread().getName());  
    }  
  
    // 3.接收方处理消息(处理数据)-- 和发送方在同一个线程  
    @Subscribe(threadMode = ThreadMode.PostThread)  
    public void onPostEventBus(PostMessage msg) {  
        Log.e(TAG, "onEventBusPost() handling message: " + Thread.currentThread().getName());  
    }  
}

另一个负责发布事件的类:

public class OtherActivity extends Activity {
    private final static String TAG = "EventBusTest";  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.other_activity);
        
         EventBus.getDefault().register(this);
        
    }
    // 3.接收方处理消息(处理数据)-- 主线程中执行  
    @Subscribe(threadMode = ThreadMode.MainThread)  
    public void onMainEventBus(MainMessage msg) {  
        Log.e(TAG, "onEventBus() handling message: " + Thread.currentThread().getName());  
    } 
    public void btnClick(View view) {  
        switch (view.getId()) {  
            case R.id.btn1: 
                
                
                // 2.发送方发送消息 -- 发送MainMessage这个自己定义的对象,可以丰富这个对象,用来传递消息(数据)
                EventBus.getDefault().post(new MainMessage("Hello EventBus"));  
                
                break;  
            case R.id.btn2:
                // 2.发送方发送消息 -- 发送BackgroundMessage这个自己定义的对象,可以丰富这个对象,用来传递消息(数据)
                // 注意,这里是在主线程中发送消息
                EventBus.getDefault().post(new BackgroundMessage("Hello EventBus"));  
                break;  
            case R.id.btn3:  
                new Thread(){
                    public void run() {
                        // 2.发送方发送消息 -- 发送AsyncMessage这个自己定义的对象,可以丰富这个对象,用来传递消息(数据)
                        EventBus.getDefault().post(new AsyncMessage("Hello EventBus"));  
                    };
                }.start();
                break;  
            case R.id.btn4:  
                new Thread(){
                    public void run() {
                        // 2.发送方发送消息 -- 发送PostMessage这个自己定义的对象,可以丰富这个对象,用来传递消息(数据)
                        EventBus.getDefault().post(new PostMessage("Hello EventBus"));  
                    };
                }.start();
                break;  
        }  
    }  
}

用法还是很简单的,需要的可以下载demo:https://yunpan.cn/cSDavPcfvgLvY (提取码:9423)

简单源码解析

还是来简单看下源码,需要源码的可自行去git下载。

注册

register.java这个类是一个单例模式的类,按顺序我们先看他的register方法。

/**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

findSubscriberMethods方法作用就是遍历当前类里面的所有@Subscriber注解的方法。然后返回一个list,接着遍历List,调用subscribe方法订阅事件。

// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        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;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

根据subscriberMethod.eventType,去subscriptionsByEventType去查找一个CopyOnWriteArrayList ,如果没有则创建。

顺便把我们的传入的参数封装成了一个:Subscription(subscriber, subscriberMethod, priority);

这里的subscriptionsByEventType是个Map,key:eventType ; value:CopyOnWriteArrayList ; 这个Map其实就是EventBus存储方法的地方,一定要记住!

简单来讲这个方法就是把该类里面的所有@Subscriber注解的方法放进subscriptionsByEventType这个map里面,为后面事件做准备。

发送

 /** 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 = 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;
            }
        }
    }

这个方法负责把事件发布到事件总线。currentPostingThreadState是一个ThreadLocal类型的,里面存储了PostingThreadState;PostingThreadState包含了一个eventQueue和一些标志位。把我们传入的event,保存到了当前线程中的一个变量PostingThreadState的eventQueue中。

根据isPosting为false的情况不断的调用postSingleEvent(eventQueue.remove(0), postingState)方法。

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);
            }
        } else {
            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) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
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;
    }

根据event的Class,去得到一个List<Class<?>>;其实就是得到event当前对象的Class,以及父类和接口的Class类型,遍历所有的Class,到subscriptionsByEventType去查找subscriptions这个map与register里面的map是同一个。遍历每个subscription,依次去调用postToSubscription(subscription, event, postingState.isMainThread);

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

这个就是根据注解里的threadMode去判断在哪个线程执行了。

case PostThread:

void invokeSubscriber(Subscription subscription, Object event) throws Error {  
          subscription.subscriberMethod.method.invoke(subscription.subscriber, event);  
}

直接在当前线程里面执行了。

case MAIN:

if (isMainThread) {
      invokeSubscriber(subscription, event);
     } else {
        mainThreadPoster.enqueue(subscription, event);
     }

首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。

case BACKGROUND:

              if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }

如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用

executorService = Executors.newCachedThreadPool();。

case ASYNC:

asyncPoster.enqueue(subscription, event);

将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。

这么说BackgroundThread和Async有什么区别呢?

BackgroundThread中的任务,一个接着一个去调用,中间使用了一个布尔型变量handlerActive进行的控制。

Async则会动态控制并发。

取消

 /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

Subscribe

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

ThreadMode threadMode() default ThreadMode.POSTING;

也就是前面说道的执行方式,主线程还是其他线程,默认是当前线程。

boolean sticky() default false;

默认为false,时间立即执行。如果为true那表示事件事件不被马上处理。

int priority() default 0;

优先级,数字越大优先级越高,默认0。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这