EventBus的使用和源码解析

为什么要使用EventBus

在Android开发过程中经常有这样的场景–>栈顶Activity需要关闭非栈顶的Activity,或调用其中的某些方法。

案例:我有三个Activity,主界面Activity,个人中心Activity,登录界面Activity,主界面打开了个人中心页面,个人中心页面有个退出登录的功能,点击后退出登录关闭主界面Activity和当前Activity,打开登录页面.

这只是个简单的案例,当然关闭当前Activity和打开登录页面的Activity都是没什么难度的东西。

我们来讨论主界面关闭的一些实现方案。

  1. 得到主界面Activity的对象。这边可能需要某个静态方法来持有这个对象,以前见过一种写法是写一个ActivityManager来持有这些Activity对象,然后手动管理这些Activity。

  2. 发送一个广播通知主界面Activity.

  3. 在当前Activity销毁时传一个resultCode,主界面看到这个resultCode来判断是否是否要做关闭操作。

暂时能想到的就这三种实现方案,分析一下三种实现:

第三种实现方案并不适用于比如我现在是主界面–>个人主页–>个人中心,做第三种方案的话代码写到哭晕在厕所,并且第三种方案代码写起来会很复杂,页面可能还没关掉就把自己绕晕了(个人想法,不推荐!)

第二种实现方案:发送一个广播做通知,其实是一个蛮不错的选择,发送广播然后实现广播类做回调通知。但是如果现在需求是对一个Fragment做通知。这个时候广播就派不上用场了。毕竟广播只能再Activity中注册发送广播。

第一种实现方案:这种方案在以前还是蛮常见的,以前学Android的时候会看到一些大牛们,自己写一个ActivityManager用来存放Activity对象做生命周期管理。但是这样做其实是会有问题的。因为持有的是Activity的对象,这就导致了Activity只能手动销毁。每次写关闭页面的时候都要调用这个方法来销毁Activity,不然分分钟就OOM了。还有另外一个缺点就是需要遍历整个栈来确定存活的Activity对象。找到你要找的Activity,然后调用其中的方法。如果是要销毁则还要将它移除栈中。(心很累)

想想这些方法真的都好难用呀。
这个时候就需要来个上帝来管理这些东西了。EventBus一脸害羞的跳出来了。一种全局的观察者模式,你可以把你的愿望(Event)请求post给这位上帝,它提供多线程的处理方案,来让你的(愿望)在你指定的地方实现。

EventBus使用

Github上可以看到gradle所需配置

compile 'org.greenrobot:eventbus:3.0.0'

在gradle文件做上如上配置就可以了

分三个步骤

  1. 定义一个作为通知的实体类Event;
  2. 在需要订阅的地方做注册和取消注册,写一个订阅方法(方法需要写注解 @Subscribe(threadMode = ThreadMode.XXX))
  3. 在任意地方调用EventBus.getDefault().post(Event());

简单代码如下:
在MainActivity注册和解除注册,并写出订阅方法

package com.example.cncn.myapplication;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册一个EventBus
        EventBus.getDefault().register(this);
        findViewById(R.id.tvClickView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, UserInfoActivity.class));
            }
        });
    }


    @Subscribe
    public void onMainEvent(Event event) {
        //打印对象的地址信息
        Log.d("MainActivity", "EventInfo" + event.toString() + "_" + event.eventInfo);
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("MainActivity","我被关掉了");
        //如果已经注册,千万别忘了取消注册
        if (EventBus.getDefault().isRegistered(this))
            EventBus.getDefault().unregister(this);
    }
}

在UserInfoActivity发送通知

package com.example.cncn.myapplication;

import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import org.greenrobot.eventbus.EventBus;

/**
 * Created by xiaolong on 2017/3/24.
 * Email:719243738@qq.com
 */

public class UserInfoActivity extends AppCompatActivity {
    private TextView tvClickView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvClickView = (TextView) findViewById(R.id.tvClickView);
        tvClickView.setText("点我退出登录!");
        tvClickView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(UserInfoActivity.this, LoginActivity.class));
                Event event = new Event(110, "关闭页面吧!!");
                Log.d("UserInfoActivity", event.toString());
                EventBus.getDefault().post(event);
                finish();
            }
        });
    }
}

LoginActivity

package com.example.cncn.myapplication;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

/**
 * Created by xiaolong on 2017/3/24.
 * Email:719243738@qq.com
 */

public class LoginActivity extends AppCompatActivity {
    private TextView tvClickView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvClickView = (TextView) findViewById(R.id.tvClickView);
        tvClickView.setText("点我登录!");
        tvClickView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(LoginActivity.this,MainActivity.class));
            }
        });
    }
}

实体类

package com.example.cncn.myapplication;

/**
 * Created by xiaolong on 2017/3/24.
 * Email:719243738@qq.com
 */

public class Event {
    public int eventCode;
    public String eventInfo;

    public Event(int eventCode, String eventInfo) {
        this.eventCode = eventCode;
        this.eventInfo = eventInfo;
    }
}

运行结果

打印接锅

如此简单便捷的使用方式,确实是做APP层消息通知的不二选择。不过在注册过EventBus的类中,千万别忘了在结束的时候取消注册。这么好用的东西还是需要去了解一下其内部实现的。而且EventBus内部实现其实并不复杂。

EventBus源码解析

注册EventBus的时候需要执行,EventBus.getDefault().register(this);
我们先从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;
    }

大致代码如下,单例模式获取一个EventBus对象,保证所有的EventBus对象是同一个。

进入 EventBus构造看一下


    /**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        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;
    }

两个构造方法,一个是无参的public的方法,另外一个是内部方法,带参数的。
第一个方法默认调用有参数的构造方法,来生成一个EventBus对象。
这边DEFAULT_BUILDER声明如下

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

存放着一个默认的EventBusBuilder对象;
进入EventBusBuilder中看

public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List<Class<?>> skipMethodVerificationForClasses;
    List<SubscriberInfoIndex> subscriberInfoIndexes;

    EventBusBuilder() {
    }

    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
        this.throwSubscriberException = throwSubscriberException;
        return this;
    }
    public EventBusBuilder eventInheritance(boolean eventInheritance) {
        this.eventInheritance = eventInheritance;
        return this;
    }

    public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
        if (skipMethodVerificationForClasses == null) {
            skipMethodVerificationForClasses = new ArrayList<>();
        }
        skipMethodVerificationForClasses.add(clazz);
        return this;
    }

    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
        return this;
    }

    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
        this.strictMethodVerification = strictMethodVerification;
        return this;
    }

    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

    public EventBus build() {
        return new EventBus(this);
    }

}

里面是一些EventBus的基础配置。打印异常信息,发送未被订阅的消息等。
特别需要注意的是 ignoreGeneratedIndex 这个变量是用来确定EventBus用什么方式来获取订阅方法。
true代表的是不优先使用索引,用反射的方式,false代表的是优先使用索引。默认是false。
这边有个addIndex方法是用来添加订阅方法索引的,这是3.0版本的新特性。
在EventBus带参数的构造函数里面初始化了 SubscriberMethodFinder 这个类,三个参数分别为索引列表,
是否严格方法定义,是否无视索引。

getDefault方法总结,该方法返回一个单例的EventBus对象,如果对象没被创建就创建一个默认的EventBus对象。

接下来从注册方法下手


    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方法找到了一个订阅者中的所有订阅方法,返回一个 List,然后之后遍历所有的订阅方法,做订阅操作。进入到findSubscriberMethods看看如何实现的。


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

findSubscriberMethods这个方法声明SubscriberMethodFinder这个类中,类中声明了一个ConcurrentHashMap用来做SubscriberMethod的缓存。

回到findSubscriberMethods中,首先是通过类来当key,查找在当前缓存中是否存在这个类的订阅方法列表。有就直接return 缓存中的列表。

没有就进入下一步,这边有两种方式来获取类中的订阅方法通过ignoreGeneratedIndex来确定获取订阅方法的方式为true时会使用反射方法获取订阅者的事件处理函数,为false时会优先使用subscriber Index(订阅方法索引)生成的SubscriberInfo来获取订阅者的事件处理函数(减少反射耗时),默认false。(使用订阅方法索引需自己构建一个EventBus对象,将在后面提及,目前的使用并不能用到索引)

进入findUsingInfo这个方法

 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();//这个方法用来从对象池中获取一个FindState对象,如果对象池都没有可用对象的话新建一个。用来节省创建对象的花销
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
  1. 从FindState池——FIND_STATE_POOL中获取一个FindState对象。(SubscriberMethodFinder这个类中声明了一个FIND_STATE_POOL的对象池,这个用来存放空(并非NULL)的FIND_STATE对象,减少创建对象的花销)
  2. 该方法会将findState.clazz域中使用了@subscribe标注、方法中只有一个参数、且方法修饰符为public的方法,创建一个SubscriberMethod对象,并添加到findState的List集合中。
  3. 将findState.clazz域更新为clazz = clazz.getSuperclass(); 如果该超类名字以java. javax. android.开头则clazz变成null;不再往上寻找父类;
  4. 拷贝一份findState的List集合并返回,最后回收findState对象,回收的只是释放这个对象内部的变量的资源占用,但这个对象还是存在的,放回对象池中,下次可以再取出来用;

看一下如何用反射获取订阅方法。

private List<SubscriberMethod> 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();
        } 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");
            }
        }
    }
  1. 获取订阅类声明的所有方法; 然后对获取到的方法全部遍历一遍
  2. 获取方法的修饰符:即方法前面的public、private等关键字。
  3. 如果该类方法使用了@subscribe标注、方法中只有一个参数、且方法修饰符为public。findState.checkAdd(method, eventType) 如果之前没有存在过则返回true
  4. 判断@Subscribe标注中的threadMode对应的值,默认模式ThreadMode.POSTING
  5. 创建一个SubscriberMethod对象,该对象很简单就是保存有方法、方法参数类型、线程模式、订阅的优先级、sticky标志位。与Retrofit类似只是这里创建了一个SubscriberMethod对象。并将该对象添加到FindSate的List集合中。

回到注册方法,来看一下subscribe方法的实现

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {  
        Class<?> eventType = subscriberMethod.eventType; //note1  
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //note2  

        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //note3  
        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(); //note4  
        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); //note5  
        if (subscribedEvents == null) {  
            subscribedEvents = new ArrayList<>();  
            typesBySubscriber.put(subscriber, subscribedEvents);  
        }  
        subscribedEvents.add(eventType);  

        if (subscriberMethod.sticky) { //note6  
            if (eventInheritance) {  
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();  
                for (Map.Entry<Class<?>, Object> entry : entries) {  
                    Class<?> candidateEventType = entry.getKey();  
                    if (eventType.isAssignableFrom(candidateEventType)) { //从stickyEvents获取对应的事件交给当前事件订阅者处理  
                        Object stickyEvent = entry.getValue();  
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent); //该方法底层还是会执行postToSubscription方法  
                    }  
                }  
            } else {  
                Object stickyEvent = stickyEvents.get(eventType);  
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);  
            }  
        }  
}  
  1. 获取方法参数类型;注意:使用@Subscribe标注的方法有且仅有一个参数
  2. 利用订阅者对象及其事件处理方法构建一个Subscription对象,该对象存储有Object、SubscriberMethod对象
  3. 从subscriptionsByEventType集合中获取当前事件对应的Subscription对象集合; 如果得到的集合为空则创建一个这样的集合,并将刚创建的Subscription对象添加进subscriptionsByEventType集合中;如果得到的集合不为空且刚创建的Subscription对象已经存在该集合中则抛出异常,即同一个对象不能注册两次!
  4. 将第二步创建的Subscription对象按照优先级存入Subscription对象集合中,该集合中的元素都是按照优先级从高到低存放.
  5. 以subscriber对象为键,从typesBySubscriber获取该对象对应的接收事件类型集合,没有则创建一个这样的集合,然后当前事件类型添加到该集合中,最后将整个集合添加进typesBySubscriber集合中;有则直接添加到接收事件类型集合中;
  6. 该值默认为false,除非在注册事件方法时使用了如下的标注@Subscribe(sticky = true);那么就会执行到这里。stickyEvent也是EventBus3.0的一大特点,该类事件一旦发送给EventBus,那么EventBus就会将它存入Map
public void post(Object event) {  
        PostingThreadState postingState = currentPostingThreadState.get(); //note1  
        List<Object> eventQueue = postingState.eventQueue;   
        eventQueue.add(event); //note2  
        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);//note3  
                }  
            } finally {  
                postingState.isPosting = false;  
                postingState.isMainThread = false;  
            }  
        }  
}  
  1. 获取当前线程对应的一个PostingThreadState()对象;回顾一下我们在EventBus中创建的如下域
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {    
        @Override  protected PostingThreadState initialValue() { return new PostingThreadState(); } //当前推送线程状态   
};  

ThreadLocal类型的特点是当调用currentPostingThreadState.get()方法的时候,会返回当前线程所持有的一个 PostingThreadState对象;在不同的线程中执行同样一行代码它们得到的对象是不同的。PostingThreadState也很简单,就是定义了一堆数据,没有任何方法。下面就是它的所有源码

final static class PostingThreadState {  
        final List<Object> eventQueue = new ArrayList<Object>(); //待派送的事件队列  
        boolean isPosting; //当前PostingThreadState对象是否正在派送事件的标志位  
        boolean isMainThread; //当前PostingThreadState对象是否是工作在UI线程的标志位  
        Subscription subscription; //事件处理器  
        Object event; //待处理事件  
        boolean canceled; //是否取消事件派送的标志位  
}  
  1. 向PostingThreadState的事件队列中添加一个事件
  2. 从PostingThreadState的事件队列——eventQueue中移出一个事件,并调用postSingleEvent方法进行派送

postSingleEvent方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {  
        Class<?> eventClass = event.getClass();  
        boolean subscriptionFound = false;  
        if (eventInheritance) {   
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //note2  
            int countTypes = eventTypes.size();  
            for (int h = 0; h < countTypes; h++) { //note3  
                Class<?> clazz = eventTypes.get(h);  
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);  
            }  
        } else {  
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);  
        }  
        if (!subscriptionFound) { //note4  
            if (logNoSubscriberMessages) {  
                Log.d(TAG, "No subscribers registered for event " + eventClass);  
            }  
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&eventClass != SubscriberExceptionEvent.class) {  
                post(new NoSubscriberEvent(this, event));  
            }  
        }  
  1. eventInheritance该标志位默认为true,表示只要 满足订阅事件是该事件的父类或者实现了该事件同样接口或接口父类 中的任何一个条件的订阅者都会来处理该事件。
  2. 该方法从名字来看就是获取eventClass的所有父类往上都能追溯到Object、和所有实现的接口、以及接口父类;EventBus进行了优化采用了缓存机制Map
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {  
        //第一个是带处理的原始事件,第三个参数是原始事件的关联类  
        CopyOnWriteArrayList<Subscription> subscriptions;  
        synchronized (this) {  
            subscriptions = subscriptionsByEventType.get(eventClass);//note1  
        }  
        if (subscriptions != null && !subscriptions.isEmpty()) {  
            for (Subscription subscription : subscriptions) {  
                postingState.event = event;  
                postingState.subscription = subscription;  
                boolean aborted = false;  
                try {  
                    postToSubscription(subscription, event, postingState.isMainThread); //note2  
                    aborted = postingState.canceled;  
                } finally {  
                    postingState.event = null;  
                    postingState.subscription = null;  
                    postingState.canceled = false;  
                }  
                if (aborted) {  
                    break;  
                }  
            }  
            return true;  
        }  
        return false;  
}  
  1. 获取原始事件的关联类对应的所有Subscription对象
  2. 将上述Subscription对象集合进行遍历,使用postToSubscription方法处理原始事件

postToSubscription方法

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {  
        //第一个参数是事件处理器、第二个参数是待处理事件、第三个为当前线程是否是UI线程的标志位  
        switch (subscription.subscriberMethod.threadMode) {  
            case POSTING: //note1  
                invokeSubscriber(subscription, event);  
                break;  
            case MAIN: //note2  
                if (isMainThread) {  
                    invokeSubscriber(subscription, event);  
                } else {  
                    mainThreadPoster.enqueue(subscription, event);  
                }  
                break;  
            case BACKGROUND: //note3  
                if (isMainThread) {  
                    backgroundPoster.enqueue(subscription, event);  
                } else {  
                    invokeSubscriber(subscription, event);  
                }  
                break;  
            case ASYNC: //note4  
                asyncPoster.enqueue(subscription, event);  
                break;  
            default:  
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);  
        }  
}
  1. POSTING直接在当前线程执行,调用invokeSubscriber方法
  2. MAIN即UI线程执行:如果当前线程就是UI线程则直接调用invokeSubscriber方法,否则将任务交给mainThreadPoster——HandlerPoster(this, Looper.getMainLooper(), 10)对象异步执行,最终会调用invokeSubscriber(PendingPost pendingPost)方法;
  3. BACKGROUND背景线程执行:其实就在非UI线程中执行,如果当前线程是非UI线程则直接调用invokeSubscriber方法,否则将任务交给backgroundPoster——BackgroundPoster(this)对象异步执行,最终会调用invokeSubscriber(PendingPost pendingPost)方法;
  4. ASYNC:将任务交给 asyncPoster—— AsyncPoster(this)对象异步执行,最终会调用invokeSubscriber(PendingPost pendingPost)方法;
  5. 就是根据不同的Post模式,选择不同的方式反射执行方法。

接下来看取消注册的方法

public synchronized void unregister(Object subscriber) {  
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);//note1  
        if (subscribedTypes != null) {  
            for (Class<?> eventType : subscribedTypes) {  
                unsubscribeByEventType(subscriber, eventType); //  
            }  
            typesBySubscriber.remove(subscriber); //note3  
        } else {  
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());  
        }  
}  
  1. 从typesBySubscriber获取该对象接收的事件类型集合;
  2. 对得到的接收事件类型集合中的每个事件类型调用unsubscribeByEventType进行处理;跟着我们就分析该方法
  3. 该对象从typesBySubscriber集合中移除;
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {  
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //note1  
        if (subscriptions != null) {  
            int size = subscriptions.size();  
            for (int i = 0; i < size; i++) {  
                Subscription subscription = subscriptions.get(i);  
                if (subscription.subscriber == subscriber) { //note2  
                    subscription.active = false;  
                    subscriptions.remove(i);  
                    i--;  
                    size--;  
                }  
            }  
        }  
}  

1、从subscriptionsByEventType集合中获取该事件类型对应的Subscription集合
2、如果集合中的元素——Subscription的subscriber域等于目标subscriber,则将该Subscription从subscriptionsByEventType集合中移除出去;

源码部分就讲解到这里
由于对注解不是很了解,所以在通过索引获取订阅方法的时候我也是一脸蒙逼,因为索引列表是在构造函数中生成的。
但是addIndex从来没被调用过。我就很好奇索引列表是怎么来的,后面才明白是另外有方法来添加订阅者索引列表,下面我们来看一下如何使用索引。

索引的好处

EventBus提供了一个扩展包 EventBusAnnotationProcessor 这个方法可以在编译的时候通过注解,自动生成订阅方法索引列表的类。由于是在编译阶段就执行的,这样做可以避免反射获取所需的耗时。缺点可能是会占用一小部分内存。

如何使用索引

使用索引需要在项目gradle下添加

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

如下

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'

        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

然后在app的gradle中添加

//apply 需添加
apply plugin: 'com.neenbedankt.android-apt'
//android中需添加
android {
    apt{
        arguments{
            //路径加类名,用来确定存放的位置,可以自己定义
            eventBusIndex "org.greenrobot.eventbus.EventBusTestIndex"
        }
    }
}
//dependencies需添加
dependencies {
    provided 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

其中eventBusIndex 是路径加类名,用来确定存放的位置,可以自己定义。
配置完之后先Rebuild一把
这里写图片描述
可以看到在debug目录下生成了这个EventBusTestIndex这个类。

类中方法如下

package org.greenrobot.eventbus;

import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import org.greenrobot.eventbus.ThreadMode;

import java.util.HashMap;
import java.util.Map;

/** This class is generated by EventBus, do not edit. */
public class EventBusTestIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.base.BaseFuncActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("logoutEvent", com.cncn.tourenforce.bean.event.LogoutEvent.class),
        }));

        putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.mine.AboutActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("downLoadSuccess", com.cncn.tourenforce.bean.event.UpdateVersionEvent.class),
        }));

        putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.check.CheckReasonEditActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("checkSuccess", com.cncn.tourenforce.bean.event.CheckSuccessEvent.class),
        }));

        putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.check.SignListFragment.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("resetList", com.cncn.tourenforce.bean.event.SignSelectResetEvent.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

就是一个静态的Map对象用来存放所有订阅方法字典。然后提供一个get方法用来获取这个字典中的对象。

然后就是使用了

EventBus.builder().addIndex(subscriberClass -> new EventBusTestIndex().getSubscriberInfo(subscriberClass)).installDefaultEventBus();

这边再贴一遍installDefaultEventBus这个方法的源码

  /**
     * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
     * done only once before the first usage of the default EventBus.
     *
     * @throws EventBusException if there's already a default EventBus instance in place
     */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

可以看出这个方法用来自定义一个EventBusBuider对象配置到EventBus中去,并替换掉defaultInstance。
注意:这里需要在Default EventBus 对象还没生成的时候执行这段代码。如果已经有默认的EventBus对象存在了再installDefaultEventBus就会抛异常。
EvenBus在一些细节上面的异常处理值得借鉴。在开发者使用框架的时候。如果这段话没抛异常。估计自己都要调半天了。

所以我们需要在APP启动的时候把这个方法加进去,新建一个App类继承Application

import android.app.Application;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.EventBusTestIndex;


public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.builder().addIndex(subscriberClass -> new EventBusTestIndex().getSubscriberInfo(subscriberClass)).installDefaultEventBus();
    }
}

AndroidManifest中别忘了把Application改成当前App

<application
        android:name=".base.App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/CustomAppTheme">
</application> 

之后的方法获取就都是通过索引获取了。

总结

没有精确去分析所有的源码。这边大概做一下总结,整个流程大概如下。
1. 注册EventBus,EventBus会获取当前类中的订阅方法,包括方法的参数,类型,优先级等信息。在获取的方法中,会先遍历缓存。如果在缓存中没有才去调用获取方法。
2. 获取订阅方法有两种方式,一种是反射,反射获取全部方法,找到加了@Subscribe注解并有一个参数的方法,另外一种是通过订阅方法索引来得到订阅方法。反射的效率较低,所以优先使用用订阅方法索引来获取,当索引列表找不到这个方法。它就会走反射的途径。注册过后这个订阅方法就被存起来了。等待接受消息。
3. 在3.0以前是没有注解方法的那时候都是通过反射来获取和执行方法。而且方法必须以onEvent开头,分别为onEventMainThead,onEventBackground,onEvent,onEventAsync,3.0以后就全部替换为注解的形式
4. postEvent,会把这个Event加入消息队列,然后通过Event的类型信息来得到它的订阅方法,然后根据相应的订阅方式反射调用订阅方法。没有选择的threadMode的post一般都会在UI线程执行。如果当前post不是UI线程,这边会用Handle的机制来让接受Event的方法运行在UI线程。
5. 解除注册,将该对象从对象缓存列表中移除,获取当前对象的订阅列表,然后将其从订阅列表移除
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值