eventbus全解析

eventbus旨在为Android中的各组件提供解耦和的数据与事件传递。首先在需要接收事件和数据的组件中(官方推荐在onStart方法中)注册:

EventBus.getDefault().register(this);
然后在组件中定义接收事件和数据的方法。在最新的eventbus3.0.0中,方法使用注释(annotation)来注册,即在方法的前添加注释

@Subscribe(threadMode = ThreadMode.XXXX)
XXXX处可选填MAIN,ASYNC,BACKGROUND,POSTING。

MAIN:方法将运行在主线程中。

ASYNC:方法将运行在新开辟的子线程中。

BACKGROUND:方法将运行在子线程中,这里分两种情况:1.如果传递事件的线程为主线程,则新开辟一个子线程来运行该方法;2.如果传递事件的线程为子线程,则直接在该子线程中运行。

POSTING:方法将运行在原线程中,无论原线程是主线程或者是子线程,方法将继续在原线程中运行。这也是方法默认的运行模式。

如果要传递事件,直接调用语句:

EventBus.getDefault().post(new Object());
在传递事件的时候,要尤其注意参数的类型。EventBus在判断事件该传递到哪个方法时的判断依据就是方法中的参数类型。而且,每个方法只能包含一个参数,也就是一个参数类型。如果有多个方法包含了同样的参数类型,那么每个方法都会接收到该事件。

在组件运行结束后,我们还要注销掉之前注册的EventBus,官方推荐在onStop中进行,代码也很简单:

EventBus.getDefault().unregister(this);
以上就是一个较为完整的EventBus的使用。由此可见,EventBus运用起来十分简单方便,使用后,代码也会变得更加清晰。但要在使用中注意传递的事件,而且每个方法只能包含一个参数类型。

StickyEvent
有时候,我们想要传递事件到一个组件,但是该组件还未创建和初始化,如果只是像上面那样简单地调用post方法,事件的传递最终将无效化。这时候,我们就可以使用stickyevent :

EventBus.getDefault().postSticky(new Object());
这样就传递了一个StickyEvent。但仅仅是这样还不行,我们还需要在接收的方法处添加 :

@Subscribe(threadMode = XXXX, sticky = true)
由于在EventBus中,方法的sticky属性默认为false,所以我们需要手动设置方法的sticky属性为true,这样方法就能接收StickyEvent了。

事件处理的优先级及事件的拦截

由于每个含有相同参数类型的方法都会接收到该参数类型的事件,EventBus为了区别各方法,设置了priority优先级属性 :

@Subscribe(threadMode = XXXX, sticky = XXXX, priority = XXXX)
priority默认为0,数字越大,代表优先级越高,也就会越早接收到事件。

在方法中,调用如下的语句,优先级低于该方法的其他方法便接收不到该事件了:

EventBus.getDefault().cancelEventDelivery(event);
但是,目前取消事件仅限于用在POSTING模式的方法中。

下面,和大家一起来看一看源码,看一看EventBus是如何实现的:

首先是register方法:

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

register的传入参数是this,也就是我们注册的组件。从代码中可以看出,程序一开始使用反射找到组件的类,然后使用findSubscribeMethods方法,传入的参数为组件的class。从字面意思可以看出来,这是找出类中所有注册的方法,然后保存到List中。

下面我们来追踪一下findSubscribeMethods方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> 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;
        }
    }
程序中分情况分别调用了findUsingReflection方法,和findUsingInfo方法,下面是findUsingReflection方法:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
还没完,直接看findUsingReflectionInSingleClass方法:

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

果然,还是使用反射,一次枚举类中的方法,查看它们的Annotation,找到注册的方法,然后将它们都保存下来,返回到register方法中,再依次进行注册。再来看register方法中的subscribe方法:
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);
            }
        }
    }
在subscribe方法中,EventBus对注册的方法进行分类注册,其中的subscriptionsByEventType的定义为:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
方法根据事件类型的不同,最终保存到Map中。有上面第7行的else语句也可以看出,一个类中注册的事件类型必须是唯一的,即一个类中不能注册多个事件类型相同的方法。

subscribe方法还对priority属性和sticky属性进行了分类:

第14-20行,将priority值高的方法排在List的前面。

第29行开始,对sticky属性为true的方法进行分类判别。

好了,register方法看完了,总结一下,就是通过Annotation找到注册的类中的方法,然后将它们分类,最后保存到一个Map中。

现在,我们就已经领会到EventBus的精髓了,post方法就是根据事件类型,从Map中找到对应的方法,然后依据threadMode,priority,sticky三个属性来执行。

由于篇幅的限制,这里就不再对post方法进行追踪了,大家可以自己来看一看。

最后,再post一个EventBus的新应用:

异常容错处理

在EventBus3中,如果在@Subscrible标注的方法中,如果程序出错,不会立即使程序crash,而是由EventBus拦截异常,并打印错误日志。
用户可以通过EventBusBuilder来配置获取EventBus实例后的对象,来决定在处理event时是否需要抛出异常信息:

eventBus = EventBus.builder().sendNoSubscriberEvent(false)        
            .sendSubscriberExceptionEvent(false)                       
            .throwSubscriberException(BuildConfig.DEBUG) //只有在debug模式下,会抛出错误异常 
            .build();

以上代码使用Builder设计模式,来构建返回一个eventBus实例。在调试阶段,可以在程序出现异常时直接Crash发现错误。


EventBus源码:https://github.com/greenrobot/EventBus

参考文章: EventBus新特性及用法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值