Android EventBus3源码解析(上)

本文详细解析了EventBus3的订阅者注册与注销流程,包括如何通过反射获取订阅者方法,处理方法的优先级,以及注册时的异常处理。同时,探讨了注册与注销的实现机制,避免内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

读前须知

        1.本文是对Android EventBus3的源码解析,若对此框架的基本使用还不了解可先进行快速学习再阅读本文。学习此框架,个人觉得这篇文章写得很好:EventBus使用详解
        2.由于篇幅很长,所以分为上中下三篇来解析。

订阅者注册

注册和注销订阅者最常用的方法就是下面这样。

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册订阅者
        EventBus.getDefault().register(this);
}

@Override
protected void onDestroy() {
        super.onDestroy();
        //注销订阅者
        EventBus.getDefault().unregister(this);
}

@Subscribe(threadMode = ThreadMode.MAIN,priority = 3)
public void onHandleMessage(Message msg){
		//处理订阅的事件
        textView.setText(msg.getMsg());
}

getDefault()是一个静态方法,先跳进去看看。

/** 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;
    }
...
static volatile EventBus defaultInstance;
...

这是一个经典的懒汉式单例模式,返回一个默认的EventBus。接着再跳进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方法获取一个方法的列表。通过类来获取方法,很明显这就需要用到反射了。在此之前我们再回顾一下订阅者处理方法的使用。

@Subscribe(threadMode = ThreadMode.MAIN,priority = 3)
public void onHandleMessage(Message msg){
		//处理订阅的事件
        textView.setText(msg.getMsg());
}

接着深入findSubscriberMethods方法看看。

...
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
...

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //1
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        //2
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        
        //3
        if (ignoreGeneratedIndex) {
        	//4
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
        	//5
            subscriberMethods = findUsingInfo(subscriberClass);
        }
       
        if (subscriberMethods.isEmpty()) {
        	//6
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
        	//7
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

        要怎么获取方法列表呢?先在一个存储方法列表的缓冲(METHOD_CACHE)里找,在代码块1处。
        在缓冲里找到了就直接返回这个方法列表就可以了,在代码块2处。
        如果缓冲里没有就跳到代码3处判断。ignoreGeneratedIndex表示是否无视索引(默认下是false,后面在解析Builder时会说到)。
        无视索引就会跳到代码4处通过反射来找。否则跳到代码5来找。都进去看看代码。

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
...
	//找订阅者的事件处理方法
	findUsingReflectionInSingleClass(findState);
	//顺便再去它对应的子类找找
	findState.moveToSuperclass();
...
}

在findUsingReflectionInSingleClass里面注意这行代码。

...
//订阅者事件处理方法必须为public方法
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0){
...
}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");
}

这也就是为什么事件处理方法为private时会报错,上面代码已经明确作出规定了,之前在学习EventBus时,习惯把方法写成私有的,结果就报错了。
事件处理方法为private
        继续分析,如果注册订阅者的类及它的子类里压根就没有事件处理方法,就会跳到代码6处抛出异常。
        在一切都没问题后,就到代码7处把事件处理方法的列表缓冲起来。至此findSubscriberMethods方法就结束了。
        接下来才是真正的注册。

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

跳进subscribe方法看看。代码很长分几段来分析。

// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //1
        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)) {
            	//2
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        ...

        代码1处把 EventBus.getDefault().register(obj) 传入的对象(一般是Activity)封装进了订阅类对象,然后保存起来。
        如果同一个对象注册两次就会跳到代码2处抛出异常,这个坑之前在学习时也是踩过的。继续。

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

//org.greenrobot.eventbus.Subscribe.java
int priority() default 0;

        接下来这个地方就会根据事件处理方法的优先级来选择插入的位置了。默认下优先级是0,数字越大优先级越高。把事件处理方法都放在一个列表里,说明EventBus很有可能会用到观察者模式。继续。

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

这里把新的事件类型添加进列表中,接下来就是处理粘性事件了(StickyEvent)。

if (subscriberMethod.sticky) {
...
    Object stickyEvent = stickyEvents.get(eventType);
    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    }
}
...
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
}
...

        在使用粘性事件的时候我们知道,在注册以前其实粘性事件可能就已经存在了,注册完毕立马就能响应粘性事件。
        其实这里也就是注册的尾声了。当注册没问题后,就会检查注册对象的类是不是有处理粘性事件的方法,如果有而且此时正好还存有对应的粘性事件,那么订阅者此时就会来处理这个粘性事件了。到这里,整个订阅者注册操作就结束了。

订阅者注销

使用默认EventBus来注册,在onDestory()必须将其注销。

@Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
}

        在上文我们知道getDefault()会返回一个静态变量,将Activity与静态变量绑定在一起,如果此时不解绑就会导致内存泄漏(静态变量算是内存泄漏的常客了)。
        跳进unregister方法看看是怎么注销的。

/** 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 {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

        代码很简单,其实就是注册的反过程,将订阅者从订阅者列表和事件类型列表中移除。这里还可以看到反复注销同一个对象是不会抛出异常的,但重复注册会。

下一篇

EventBus注册和注销讲完了,但这仅仅是个开始,感兴趣的朋友可以看看接下来的内容。
Android EventBus3源码解析(中)
Android EventBus3源码解析(下)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值