EventBus3.0 study

概述

eventbus出来很久了,最近想用一下eventbus,自己对着一些博客撸了一个demo,发现竟然crash了,然后去看看源码发现3.0的eventbus有了很多改动。技术变化真快,得保持谦虚的态度,踏踏实实的学习。正好今天内部群里发了一个如何新技术的学习图,感觉挺好的
EventBus3.0 study_第1张图片

作为一个android入门小白还是要多学点。

基本概念

eventbus是一个是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。比如请求网络,等网络返回时通过Handler或Broadcast通知UI,两个Fragment之间需要通过Listener通信,这些需求都可以通过EventBus实现,作为一个消息总线主要有三个组成部分:

  • 事件(Event)
  • 事件订阅者(Subscriber)
  • 事件发布者(Publisher)

官方一张图可以很好地说明这三者关系
这里写图片描述

细细想来eventbus确实比android的回调函数要强很多,回调只能回到特定的类中,而Eventbus只要在某类中监听了某个消息,无论在哪个类中,只要有消息发出,监听类都会得到执行,而且比一些广播啥的更加高效。Eventbus主要功能是替代Intent,Handler,BroadCast(onreceive不能超过10s)在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。不过也看到一些人说Eventbus用多了会使代码显得比较混乱。

如何使用

添加依赖 compile 'org.greenrobot:eventbus:3.0.0'
- 定义传递的数据结构
这是传递的一个载体,由于传递调用post需要一个object,因此定义的类也可以是一个空类

public class TestEvent {
    private String mMsg;

    public TestEvent(String msg) {

        mMsg = msg;
    }

    public String getMsg() {
        return mMsg;
    }
}
  • 注册与注销
    在需要接受消息的类中进行注册,我需要在MainActvity中接收消息,就在onCreate中注册
EventBus.getDefault().register(this);

在onDestroy中注销

 EventBus.getDefault().unregister(this);
  • 订阅函数
    在注册的类中书写处理函数,以前都是说要以onEvent开头分为4个
    1、onEvent
    2、onEventMainThread
    3、onEventBackgroundThread
    4、onEventAsync
    对应着在不同线程处理,其实3.0的eventbus抛弃了这种写法,你可以自定义函数名,但是,必须要在定义的函数上加上subscribe的注解,注解里面可以定义threadMode ,是否是sticky的,优先级,订阅函数是靠参数区分而不是靠函数名。比如这里就是在MAIN thread中
@Subscribe(threadMode = ThreadMode.MAIN)
    public void doMain(TestEvent event) {
        String msg = "onEventMainThread get the msg: \n" + event.getMsg();
        tv_msg.setText(msg);
        Log.d("thread main", Thread.currentThread().getName());
        Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
    }

关于这几个参数这里介绍下:
- threadMode
在EventBus中的观察者通常有四种线程模型,分别是PostThread(默认)、MainThread、BackgroundThread与Async。

PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。

-sticky
类似粘性广播那样,的粘性事件,简单来说就是发送该事件之后订阅该事件也可以收到订阅前的消息(只能收到最近的一条消息)
-priorty
默认优先级为0,只有在同一个threadmode中优先级越大越先收到订阅事件。

  • 发布函数
    通过post或者postStcky来发布一条事件,如下,在TestActvity1中,点击按钮就发布一条事件
 public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_msgpost:
                Log.d("thread send", Thread.currentThread().getName());
                EventBus.getDefault().post(new TestEvent("testEvent1 msg send byTestAvtivity1"));
                break;
        }

    }

实例

代码下载传送门
这里做了俩个功能,MianActvity中只有俩个button 测试类名(btn_act1)、模拟广播的传递链(btn_act2),
MainActivty的订阅函数

   @Subscribe(threadMode = ThreadMode.MAIN)
    public void doMain(TestEvent event) {
        String msg = "onEventMainThread get the msg: \n" + event.getMsg();
        tv_msg.setText(msg);
        Log.d("thread main", Thread.currentThread().getName());
        Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void doBack(TestEvent event) {
        Log.d("thread back", Thread.currentThread().getName());
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void doAsync(TestEvent event) {
        Log.d("thread async", Thread.currentThread().getName());
    }

    @Subscribe(threadMode = ThreadMode.POSTING)
    public void dopost(TestEvent event) {
        Log.d("thread post", Thread.currentThread().getName());
    }

在跳转到TestActvity1中点击按钮发布,

 public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_msgpost:
                Log.d("thread send", Thread.currentThread().getName());
                EventBus.getDefault().post(new TestEvent("testEvent1 msg send byTestAvtivity1"));
                break;
        }

    }

TestActivy1点击之后从打印中可看到如下日志
这里写图片描述
这也证明了订阅函数是靠参数区分而不是靠函数名 ,在MainActvity中订阅函数名称不同但是入参一样都是TestEvent。

btn_act2中发布函数中模拟发起网络请求

  public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_msgpost2:
                EventBus.getDefault().post(new TestEvent2(" net request--send by TestAvtivity2"));
                break;
        }

    }

MainActivty的链式订阅函数

@Subscribe
    public void doReceive(TestEvent2 event2) {
        String msg = " receive the msg: \n" + event2.getMsg();
        tv_msg.setText(msg);
        EventBus.getDefault().post(new TestEvent3(" Net Datas"));
        tv_step2.setText("request Net Data ing…… ");
    }

    @Subscribe
    public void showNetData(TestEvent3 event3) {
        String msg = "show data:\n" + event3.getMsg();
        tv_step3.setText(msg);

    }

EventBus3.0 study_第2张图片
这比回调或者广播高效多了

源码分析

  • 注册
EventBus.getDefault().register(this);
  /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

    public static EventBusBuilder builder() {
        return new EventBusBuilder();
    }

在getdefault中使用双重枷锁,放止并发的产生,在register中去除了之前的4个参数的方法(Object subscriber, String methodName, boolean sticky, int priority)这些都移到了注解中,然后开始register方法

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

然后获取传入class对象subscriberMethodFinder.findSubscriberMethods(subscriberClass)获取到注解的订阅函数

List findSubscriberMethods(Class subscriberClass) {
        List subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

这里根据是否产生索引来得到订阅方法名
分为是否产生索引??这里没看懂,但是findUsingReflectionfindUsingInfo里面都是通过一个while循环来找到具体的方法名

private List findUsingReflection(Class subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

这里findUsingReflectionfindUsingInfo都有findState.moveToSuperclass();这里EventBus对继承的父类也会进行查找注册函数,即子类注册一次即可,父类直接使用 @Subscribe来产生订阅函数即可。

findUsingReflectionInSingleClass(findState);是二者核心函数

 private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            // 事件处理方法必须为public,这里过滤掉所有非public方法
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class[] parameterTypes = method.getParameterTypes();
                // 事件处理方法必须只有一个参数
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                // 如果某个方法加了@Subscribe注解,并且不是1个参数,则抛出EventBusException异常
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

可以看出,这里该方法主要作用就是找出subscriberClass类以及subscriberClass的父类中所有的事件处理方法(添加了@Subscribe注解,访问修饰符为public并且只有一个参数)。值得注意的是:如果子类与父类中同时存在了相同事件处理函数,则父类中的不会被添加到subscriberMethods。

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

        if (!postingState.isPosting) {
         // 标识post的线程是否是主线程
            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()) {
                 // 循环处理eventQueue中的event对象
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
             // 重置postingState的一些标识信息
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    } 
  

其中currentPostingThreadState是一个ThreadLocal类型,里面存储了PostingThreadState;

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

PostingThreadState()中则包括post队列的一些信息

    /** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
        final List eventQueue = new ArrayList();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }
 
  
  • 取消事件初注册
    /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List> 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());
        }
    }

这里所做的工作只是将注册的事件从Eventbus中移除

水平有限这里只是简单的分析了一下源码,了解大概而已。

你可能感兴趣的:(Android相关,Android,进阶,源码分析,Eventbus)