EventBus原理分析

EventBus原理分析

EventBus的使用,同时本文也是基于3.1.1版来进行讲解

首先关联EventBus的库

implementation 'org.greenrobot:eventbus:3.1.1'

1.事件订阅

EventBus.getDefault().register(this);

2.事件发布

EventBus.getDefault().post(new MessageEventBean("shoft"));

3.接收事件(订阅方法)

@Subscribe(threadMode = ThreadMode.MAIN)
public void threadMain(MessageEventBean messageEventBean){
    tv.setText(messageEventBean.getMsg());
}

4.取消订阅

EventBus.getDefault().unregister(this);

以上就是EventBus的简单使用。

事件订阅

首先我们来分析事件订阅,看看它里面到底干了些啥。。打开EventBus#getDefault()方法,发现它是DCL双重锁机制的单例模式,这种模式保证了其在同一个进程中的访问是安全的。紧接着跟踪new EventBus(),发现最终实现了EventBus自己的的一个构造方法,并在里面进行了大量的初始化工作。

public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();   //=============1
            }
        }
    }
    return defaultInstance;
}

public EventBus() {
   this(DEFAULT_BUILDER);
}
//进行了大量的初始化操作
EventBus(EventBusBuilder builder) {     //=============2
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

以上就是EventBus#getDefault()方法所作的一些工作,接下来分析EventBus#register(this)方法,register方法中首先会获取到订阅类的类型,然后再去获取当前订阅类中的所有订阅方法,最后循环遍历订阅方法,完成订阅。。

ublic void register(Object subscriber) {
    Class subscriberClass = subscriber.getClass(); //1.获取到订阅类的类型
    List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//2.获取到当前订阅类中的所有订阅的方法,即接收事件的方法。
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);//3.循环订阅方法,完成订阅
        }
    }
}
获取当前订阅类中的所有订阅方法

接下来,我们来分析它是怎样来获取到当前订阅类中的所有订阅的方法,即查看EventBus#findSubscriberMethods(Class subscriberClass)方法,把当前订阅类型subscriberClass作为参数传入,发现其获取订阅方法数组的方式有两种,一种是findUsingReflection(subscriberClass),称其为方法一,另一中是findUsingInfo(subscriberClass),称其为方法二。

List findSubscriberMethods(Class subscriberClass) {
	//从METHOD_CACHE这个ConcurrentHashMap集合中取到所有订阅方法的数组
    List subscriberMethods = METHOD_CACHE.get(subscriberClass);
    //若subscriberMethods有值,则直接返回
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
	//ignoreGeneratedIndex值默认为false,以下是两种获取订阅方法数组的方式,我们称其为方法一、方法二。
    if (ignoreGeneratedIndex) {			
        subscriberMethods = findUsingReflection(subscriberClass); //====方法一
    } else {
        subscriberMethods = findUsingInfo(subscriberClass); 	//====方法二
    }
    //判断订阅类中是否有订阅方法,若无,抛出异常,若存在,则把所有订阅方法作为数组存入METHOD_CACHE集中,并返回订阅方法数组
    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(subscriberClass)中,传入subscriberClass作为参数,方法中,首先会准备一个FindState类来保存订阅类的信息,紧接着初始化订阅类,并赋值给clazz,然后通过while循环来遍历添加当前订阅类及其父类的所有订阅方法(默认会去查询父类的订阅方法)。最后释放FindState中订阅类的信息,并返回订阅类中的订阅方法数组。

private List findUsingReflection(Class subscriberClass) {
    //准备一个FindState,里面保存了订阅类的信息
    FindState findState = prepareFindState(); 
    //初始化FindState
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
    	//这个方法才是获取到订阅方法数组的关键
        findUsingReflectionInSingleClass(findState);
        //往父类查找
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);//释放FindState中保存的所有订阅类的信息,并返回订阅方法数组
}

这里比较关键的就是订阅方法添加的这个方法findUsingReflectionInSingleClass,接下来继续讲解findUsingReflectionInSingleClass方法,这个方法中首先获取订阅类中的所有方法,然后循环遍历出订阅方法,最后把订阅方法的信息全部添加到subscriberMethods数组中。

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {   
    	//获取当前订阅类中的所有方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        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];
                    if (findState.checkAdd(method, eventType)) {//判断是否添加过
                    	//获取当前订阅方法的线程模式
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //把订阅方法及其参数、线程模式、进程优先级作为SubscriberMethod实体加入subscriberMethods数组中
                        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");
        }
    }
}

到了这里,方法一的订阅方法数组就已经存在了。。

方法二findUsingInfo(subscriberClass)中,内容与方法一相似,不过这里多了一个SubscriberInfo接口类,它主要是用来存储订阅相关信息,如果SubscriberInfo接口中的订阅信息接口被实现过,则会从SubscriberInfo接口中取出订阅方法,并添加到subscriberMethods数组中,如果SubscriberInfo未被实例化或者接口未被实现过,则还是通过方法一来进行添加订阅方法。。。

private List findUsingInfo(Class subscriberClass) {
    FindState findState = prepareFindState(); 
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
    //初始化subscriberInfo接口类
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
        //获取订阅方法数组
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                //循环添加订阅方法到subscriberMethods数组中
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            findUsingReflectionInSingleClass(findState); //同方法一
        }
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

到此为止,订阅方法数组的添加都已讲解完毕。。

循环订阅方法,完成订阅

上面把订阅类中所有的订阅方法都加入到了数组中,接下来就对这些订阅方法与订阅类进行订阅。subscribe(subscriber, subscriberMethod),订阅类与订阅方法作为参数传入。把订阅方法参数类型作为KEY,订阅类和订阅方法作为VALUE存入subscriptionsByEventType集合,同时把subscriber(订阅类)为KEY,subscribedEvents(参数类型数组)为VALUE存入typesBySubscriber集合中,这样就把订阅类、订阅方法、参数类型进行了订阅,

需要说明的是,当订阅方法中设置了粘滞属性sticky为true时,若stickyEvents集合中保存了参数值,以及参数对象,则最终会执行invokeSubscriber(subscription, event)方法,这个方法时通过反射来调用指定的订阅方法。在这里,很好地解释了Eventbus的sticky,它可以先发送,后接收。。。

// 必须同步调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class eventType = subscriberMethod.eventType;//获取订阅方法参数类型
    //把订阅类和订阅方法作为参数传入Subscription,得到Subscription的实例
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //在subscriptionsByEventType集合,eventType(参数类型)作为KEY,Subscription实例作为VALUE,进行绑定。
    CopyOnWriteArrayList 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();
    //直接把Subscription的实例newSubscription添加,或根据优先级来进行添加
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

//在typesBySubscriber集合中,subscriber(订阅类)为KEY,subscribedEvents(参数类型数组)为VALUE,进行绑定。
    List> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);


	//判断当前订阅方法中sticky是否为true,默认为false
    if (subscriberMethod.sticky) {
        if (eventInheritance) {//默认为true
            // 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();
                    //该方法中打开会进入到postToSubscription(newSubscription, stickyEvent, isMainThread())这个方法,继续打开会进入到postToSubscription方法中,这里有五种线程模式,针对订阅方法中线程模式的不同,相应的处理方式也会有所差异,不过其最终调用的核心是invokeSubscriber(subscription, event)方法,这个方法有一段代码subscription.subscriberMethod.method.invoke(subscription.subscriber, event);通过反射来调用指定的方法。。我觉得这段代码才是整个EventBus的核心。。
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

好了,事件订阅的流程,到此为止

事件发布

EventBus.getDefault().post(new MessageEventBean("shoft"));

EventBus.getDefault()的解释同上,那么接下来讲解post(new MessageEventBean(“shoft”))方法,初始化了一个PostingThreadState的对象,发布事件的一些相关信息都存在PostingThreadState类中,事件只能同步发送,一次只发送一个事件,

public void post(Object event) {
	//初始化PostingThreadState
    PostingThreadState postingState = currentPostingThreadState.get();
    List eventQueue = postingState.eventQueue;
    eventQueue.add(event);//把参数添加到eventQueue数组

    if (!postingState.isPosting) {//判断是否正在发布事件
        postingState.isMainThread = isMainThread();
        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(eventQueue.remove(0), postingState);传入了一个参数对象以及PostingThreadState实例,它里面有一个postSingleEventForEventType(event, postingState, clazz)方法,

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
	//获取参数类型
    Class eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {//eventInheritance默认为true
    	//获取当前参数类型及其父类的数组
        List> 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) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
                //没有订阅类
            post(new NoSubscriberEvent(this, event));
        }
    }
}

打开查看其源码,发现它会从subscriptionsByEventType集合中取值,这个集合在事件订阅的时候就已经生成了,我么需要关注的是postToSubscription(subscription, event, postingState.isMainThread);这个方法,其它的都是赋值。。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
    CopyOnWriteArrayList subscriptions;
    synchronized (this) {
    	//从subscriptionsByEventType集合中,根据参数类型取出subscriptions数组,里面保存着订阅类、订阅方法。。
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
        //赋值
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
            //发布订阅
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

postToSubscription(subscription, event, postingState.isMainThread)这个方法,方法中有五种线程模式,分别是POSTING、MAIN、MAIN_ORDERED、BACKGROUND、ASYNC,在这里,很明显,核心代码只有一句

invokeSubscriber(subscription, event),在这句代码中,事件的发布才真正完成。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            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(subscription, event)方法,发现它里面非常简洁,只有一句代码,当然我指的是有效的,

就是这句subscription.subscriberMethod.method.invoke(subscription.subscriber, event),它采用了反射机制,通过反射的方式,调用了指定的订阅方法。。到了这里,我们就可以明白了EventBus的工作机制。。

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

总结:事件的发布,其发送方式是同步的,只有当一个事件发送成功,才会发送下一个事件。然后通过反射机制,调起之前订阅过的方法。。这样消息就完美的进行了传递。说到底,EventBus就是通过反射来进行消息传递的。

取消订阅

EventBus.getDefault().unregister(this);

关闭订阅类时,需要把相关的订阅信息给清除掉,那查看unregister(this)方法,在该方法中主要是解绑,即用来移除在事件订阅时绑定的一些订阅相关的数据。明显点就是清除在subscriptionsByEventType集合中保存的指定的订阅方法参数。以及清除在typesBySubscriber集合中指定的订阅类。。

public synchronized void unregister(Object subscriber) {
//从typesBySubscriber集合中,根据订阅类,取出当前订阅类中的订阅方法参数类型数组。
    List> 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());
    }
}
	//移除数据
 private void unsubscribeByEventType(Object subscriber, Class eventType) {
 //从subscriptionsByEventType集合中,根据参数类型,取出订阅类以及订阅方法的实例的数组subscriptions
        List subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                //逐一移除参数类型
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

总结:取消订阅,通俗点讲就是清除事件订阅时保存的数据,,防止订阅类关闭时,造成内存泄漏。

以上就是EventBus的原理解析流程,讲的有误的地方希望大家留言指出,谢谢。。

你可能感兴趣的:(Android源码)