EventBus使用以及源码分析

什么是EventBus?

EventBus是一种开源的第三方工具框架,能对我们的项目代码进行高效的解耦,使程序易读性更高代码更简洁;
例如:网络访问,我们一般会先开启一个子线程用于网络访问,收到服务器的返回结果后还需要handler发送到UI线程来更新UI;如果使用EventBus只需要简单的几行代码就能搞定,下面是伪代码:
常规写法

//子线程线程
new Thread(new Runnable(){
    ......
    network connect
    ......
    mainHandler.sendMessage(Message);
}).start();

//ui线程
new Handler(){
    handleMessage(){
        //update ui
    }
}

EventBus加入进去

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

public void onEventXXX(ParamsType){
    //接收到发布事件后执行
}

//子线程
EventBus.getDefault().post(ParamsType);

从上面的代码是不是发现EventBus使用十分方便,而且你发现主线程和子线程没什么特殊的联系,下面就具体讲讲EventBus是如何运行的,先学会如何使用在分析其内部是如何运作的;

EventBus使用方法

EventBus库地址
从上面的伪代码不难看出,EventBus主要分为三个东西构成:

  • subscriber 订阅消息的订阅者
  • poster 发布消息的发布者
  • Message 消息

工作过程:
订阅者订阅消息,发布者发布消息后,订阅者会收到订阅类型的消息,进而触发相应的动作;两者可以在同一个线程也可以在不同的线程,这刚好适合我们UI线程执行网络的异步访问,UI线程订阅网络消息,子线程发布消息通知给UI线程,UI线程进而更新UI,大大的减少了两个线程的耦合程度,工作概图如下:

这里写图片描述

下面以我写的一个秒表demo为例,子线程计时后将秒值传递个主线程来更新UI:

  • 使用EventBus,先把它的库加进来
compile 'org.greenrobot:eventbus:3.0.0'
  • 编写程序的逻辑代码
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button)findViewById(R.id.btn);
        mTextView = (TextView)findViewById(R.id.display);
        mTextView.setText("这是一个计时器");

        EventBus.getDefault().register(this);   //订阅消息
        mTimeThread = new Thread(mTimeTask);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mTimeThread.start();
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)   //收到消息后执行
    public void onEventMainThread(Integer integer){
        if(mTextView != null){
            mTextView.setText(integer+"");
        }
    }

    Runnable mTimeTask = new Runnable() {
        int time = 0;
        @Override
        public void run() {
            while(time < 1000){
                EventBus.getDefault().post(new Integer(time));//发布消息
                time++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };


    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);   //取消注册
    }

demo下载地址
从上面的代码你需要注意的几点是:

  • 使用前必须注册,不使用的时候要取消注册,防止内存泄露
  • 现在最新的EventBus只支持注解方式的执行事件,就像我上面onEventMainThread方法的注解

可能看完后你会有这样的疑惑? 子线程的post发布后,主线程是怎么收到消息来执行onEventMainThread方法,这就是EventBus的精妙之处,大致的原理是:
  订阅者注册register时,EventBus会扫描注册类的所有方法,并把方法带有@Subscribe的注解方法统计起来,封装到一个Map数据结构中去,map存放了方法Method实例、方法参数类型和订阅者实例;当post发布消息的时候,会把发布的消息的类型与之前map中存放的方法参数类型进行对比,如果类型相同的话,就利用java反射来调用这个方法;注解里面还有一个ThreadMode参数,这个参数表示调用这个方法在哪个线程里面执行,主线程还是非主线程

源码分析

订阅register --- EventBus.getDefault().register(this);

查找注册的Method

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

先获取订阅者subscriber的Class对象,然后调用findSubscriberMethods方法查找Class对象的订阅方法,进入findSubscriberMethods方法内部看其实现细节:

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

这里,前面5行代码是先去缓存中查看是否有已经缓存好的list,防止多次注册同一个对象;如果没有在调用findUsingReflection和findUsingInfo两个方法,这两个方法都会去调用同一个方法findUsingReflectionInSingleClass方法,查看其内部逻辑:

private List findUsingReflection(Class subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();  //遍历其父类的方法
        }
        return getMethodsAndRelease(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();  //这个clazz就是我们的订阅者,获取这个类的自身所有方法
        } 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(); //获取方法的属性,排除非公有、静态的方法
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                //这个就是获取方法注解的,只有注解含有Subscribe的才通过
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class eventType = parameterTypes[0];
                        //查看是否有添加过这个method,没有添加通过
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //把最终的method,参数类型,线程mode和优先级封装在SubscriberMethod类里面并添加到findState的一个list里面去
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    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");
            }
        }
    }

上面代码主要完成的工作是:
列出subscriber的所有Method;遍历寻找出非静态,公有的方法并且含有subscribe注解的方法,封装为成为一个SubscriberMethod对象,并且添加都FindState这个类实例的SubscriberMethods的List当中去;
这一步执行完后,你跳回去看方法findUsingReflection,他会调用moveToSuperclass去遍历订阅者父类里面的方法,依次找完全部方法;但这里不会去系统API里面去找的,如java、javax和Android开头的不会去找;
细心的朋友可能还会问,这里只是找出了所有的方法,并没有把方法的List返回回去,返回在这里:

private List getMethodsAndRelease(FindState findState) {
        List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

这个方法主要是把之前存在FindState里面的结果(List)SubscriberMethods赋给另外一个引用并把它返回出去;还有这个FindState是个什么东西?FindState是一个查找方法的内部工具类,它里面有4个自己的静态成员,4个成员可以同时工作在多线程中,并且可以重复利用,具有一定的工作效率,省去了中间的销毁创建等工作

封装Method到Map数据结构中

找出所有的Methods后会调用subscribe(subscriber, subscriberMethod)进行method插入数据结构中去,看源码:

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);  //方法和订阅者封装为一个Subscription
        CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);  //根据类型从subscriptionsByEventType(hashmap)拿到它的value,这个value是一个list的索引
        //如果value是空的就把新的值添加进去
        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);
            }
        }
        //把当前封装好的Subscription插入到list中去,根据优先级;最终post的时候从前面优先级高的先调用
        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> 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).
                Set, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry, 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);
            }
        }
    }

subscribe方法主要是一个数据结构的操作过程:
把Subscription加入到list的CopyOnWriteArrayList里面去,在把这个list添加到HashMap的subscriptionsByEventType里面去,记住这个subscriptionsByEventType结构体,在post时候也可以用到;可能大家对这个数据的封装、加入其它数据结构有点混乱,下面我用一张图总结下:

这里写图片描述

到这里注册部分就完成,实际过程更新一个查找方法,存储到一个HashMap中去,这个最终的HashMap叫做subscriptionbyEventType,记住这个,在后面post发布消息的时候还会用

发布消息

你可能感兴趣的:(EventBus使用以及源码分析)