adnroid EventBus简单使用与源码探究

背景

在Android开发中,我们经常会遇到对于一个相同ID的对象,现在在不同activity页面,当我们在其中一个activity修改该对象的属性时,我们怎么来同步这些现实呢?在我之前的的项目中,自己一直是发广播或者是定一个静态方法,在修改的activity中,调用这个静态方法,这个是可以解决问题,但是不够方便。android开源项目中,做事件通信的很多,EventBus,Rxandroid..等等,从使用的简单程度和项目的成熟度,在项目中使用eventbus都是一个不错的选择 。
本博使用eventbus版本:2.4.4
开发工具:Android studio 1.5

简单使用

第一步,注册接受者

 EventBus.getDefault().register(this);

第二步,定义事件,其实就是一个Java实体类

public  class MyEventObject {};

第三步,在接受者中声明接受事件的方法onEventMainThread
或者是其它()注意:这些方法可以有不同的重载

public void onEventMainThread(MyEventObject object) {

    }
 public void onEventMainThread(MyEventObject2 object) {
    }

 /**
  注意:这两个重载方法,哪个能接受到,发布的这个事件尼?
  答案是:事件发布时,指定的哪个EventObject,哪个重载方法就能收到,订阅事件
 **/

(初学者可以先跳过这四个方法的详解)延伸其实也可以根据,不同需求使用如下方法:

onEvent:
/**如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,
onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。
使用这个方法时,在onEvent方法中不能执行耗时操作,
如果执行耗时操作容易导致事件分发延迟。**/
onEventMainThread:
/**
如果使用onEventMainThread作为订阅函数,
那么不论事件是在哪个线程中发布出来的,
onEventMainThread都会在UI线程中执行,
接收事件就会在UI线程中运行,这个在Android中是非常有用的,
因为在Android中只能在UI线程中更新UI,
所以在onEvnetMainThread方法中是不能执行耗时操作的。
*/
onEventBackground:
/**
如果使用onEventBackgrond作为订阅函数,
那么如果事件是在UI线程中发布出来的,
那么onEventBackground就会在子线程中运行,
如果事件本来就是子线程中发布出来的,
那么onEventBackground函数直接在该子线程中执行。
*/
onEventAsync:
/**
使用这个函数作为订阅函数,
那么无论事件在哪个线程发布,
都会创建新的子线程在执行onEventAsync.
也就是说在这个方法中可以执行一些耗时操作
*/

第四步,发布事件

 EventBus.getDefault().post(new FirstEvent("呵呵,渺小的地球人"));

这样当,发布事件后,MainActivity就可以收到SecdondActivtiy发布的事件,然后更新先关的字段显示

Evenbus疑问及原理

疑惑

Eventbus里的几个概念对象到开发中:
1.事件发布者:activity.fragement,service,thread
2.事件订阅者:activity.fragement,service,thread
3.事件:Android组件中实体类
学习完EventBus简单实用,肯定会有很多疑问:
**1当有事件发布的时候,是谁在调用onEvent…这些方法?
2.如何根据事件发布在哪些线程,而让这些方法运行在规则约定的线程?
3.我们以前实用Intent在页面之间传递数据,现在实用这种类似于回调的几种方法,这些方法,会不会造成类存泄露啊?**
带着这些疑问,我们来看看源码里是怎么实现

源码分析

eventBus使用原理

**开始前,明确我们想搞清楚哪几个问题:
a.如何调用onEventXXXX方法
b.如何根据onEventXXXX指定线程的调度
c.同一个onEventXXXX的重载方法是如何发布事件的
d.最后,通过阅读源码,使用eventBus,在声明注册、反注册、onEventXXXX方法声明、事件发布,时需注意哪些事情。**

1.注册从EventBus.getDefault().register(this);入手

/**
  在这个方法的源码注释里,已经说的很清楚
  1.当补关注事件时,一定要解除事件的绑定,在组件的生命周期中,解除订阅
  2.根据需要,指定onEventXXXX方法,是否需要在ui线程调用onEventXXXX等等,如何选择方法,参看上面四个方法的注释
  */

 public void register(Object subscriber) {
        register(subscriber, false, 0);
    }

我们在切到这个方法的实现方法:

   private synchronized void register(Object subscriber, boolean sticky, int priority) {
   /***这里通过反射,找到四个(有几个就是几个)方法,将它们封装成
   SubscriberMethod对象,添加到subscriberMethods 这个集合里
 **/
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
             //订阅事件
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

是不是很好奇,这个SubscriberMethod里面到底封装了什么?

 SubscriberMethod(Method method, ThreadMode threadMode, Class<?> eventType) {
        this.method = method;//同过反射,拿到组件里的哪个onEventXXXX方法
        this.threadMode = threadMode;//注意这里,就是我们要知道的,onEventXXXX到底在哪个线程运行
        this.eventType = eventType;//事件,就是我们定义的那个FirstEvent实体类
    }

如何知道,onEventXXXX到底在哪个线程运行?
好,来看看EventBus类里的这个成员这个类:
private final SubscriberMethodFinder subscriberMethodFinder;
其实是这个类的findSubscriberMethods(),使用了反射来,将所有的onEvnetXXXX方法和参数,运行在哪个线程,来一一分装的,这个方法很长,但是逻辑很清晰:

/**
1.通过我们,穿进来的订阅者对象(在本例中是activity),通过反射的, Method[] methods = clazz.getDeclaredMethods();找到所有的方法,循环,找到methodName.startsWith(ON_EVENT_METHOD_NAME),找到onEvent开头的方法,并且过滤掉系统的方法,最后根据名字中是否包含
"MainThread"、"BackgroundThread"、"Async",等区分他们运行在哪些线程                 
*****/
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);
        }
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            String name = clazz.getName();
           //过滤掉系统方法
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }

            //找onEnent方法
            methods to be public (might change with annotations again)
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
                    int modifiers = method.getModifiers();
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        if (parameterTypes.length == 1) {
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            //根据名字,区别运行在哪个线程
                            if (modifierString.length() == 0) {
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            Class<?> eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            methodKeyBuilder.append(methodName);
                            methodKeyBuilder.append('>').append(eventType.getName());
                            String methodKey = methodKeyBuilder.toString();
                            if (eventTypesFound.add(methodKey)) {
                                // Only add if not already found in a sub class
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

好了,我们再回到事件订阅这个方法:

 subscribe(subscriber, subscriberMethod, sticky, priority);

所以订阅的事件都被放在里:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

好了,现在就就等着你发布事件
2.事件发布,从EventBus.getDefault().post(new FirstEvent("你好"));

/**
PostingThreadState ,封装了调用post方法的运行线程信息
发布一条事件,就将其从list中删除,发布事件在postSingleEvent()中实现;
***/
 /** 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;
            }
        }
    }

切到postSingleEvent()中来:

/**
注意,这里就是我们说过的,同一个eventClass的不同订阅onEventxxx都可以响应
**/
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));
            }
        }
    }

再进到postToSubscription()里来:

/**
根据不同的订阅,订阅时确定的threadMode,invokeSubscriber发布订阅
***/
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }

切到invokeSubscriber()中,看看实现:

/**
好,看到反射了没有?再次反射调用订阅的onEventXXX方法将,事件发布到订阅者
*/
void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

好了,以上我们就看到了,整个事件:订阅—>发布—->到订阅者的流程

然而,还有一个问题,很纠结,我们知道了,onEventXXXX要运行在哪里线程里,eventBus是如何让他实现的:
看看这个方法:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }

再切到enqueue()中来:

public void enqueue(Subscription subscription, Object event) {
          //PendingPost 链表,缓存了所有的订阅事件
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

看看这个实现了 Runnable借口,在run方法里使用反射来调用onEventXXXX

class AsyncPoster implements Runnable {}

这样就实现了,onEventAsync每次都开启子线程运行,其他两种类似

嗯哼,这里算是把eventBus的整个运行流程,原理搞清楚了,但是那个ExecutorService executorService是什么?线程池,感兴趣的可以继续切进去。
注意:
我们在指定这几onEventXXXX一定要是public 的否则或报错

相关博客戳这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值