盘点常用的Android开发库(1) -- EventBus

一、简介

EventBus是Android和Java的发布/订阅消息总线。它简化了组件之间的通信,将事件的发送者和接受者进行分离、避免了复杂且容易出错的依赖关系和生命周期问题、并且体积小、效率高、使用简单。

二、使用

1.添加依赖

implementation 'org.greenrobot:eventbus:3.1.1'

2.注册

和android四大组件的broadcast receiver一样,需要对其进行注册和解除。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus);

        EventBus.getDefault().register(this);
    }

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

3.定义消息对象

消息对象是普通的旧Java对象,没有任何特殊要求。

public class MessageObj {
    private volatile static MessageObj instance;
    
    public String message;
    
    private MessageObj(){}

    public static MessageObj getInstance(){
        if (instance == null){
            synchronized (MessageObj.class) {
                if (instance == null) {
                    return new MessageObj();
                }
            }
        }
        return instance;
    }
}

4.订阅者

订阅者实现消息处理,使用@Subscribe注释定义的。

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMessageObj(MessageObj messageObj){
        //TODO 处理获取的消息对象
    }

其内传入了ThreadMode,ThreadMode是一个枚举对象,有四种线程模式,分别是;:

  • POSTING:默认,消息的处理和发布在同一个线程中,避免了线程间的切换,开销最小。
  • MAIN:将在Andoid的主线程中调用订阅者,为了防止ANR,因此在这里不能进行耗时操作。
  • BACKGROUND:将在Android的后台线程中调用订阅者,因此不能进行UI操作。如果发布消息的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布消息的线程是在后台线程,那么消息处理函数就使用该线程。
  • ASYNC:订阅者将在单独的线程中被调用,它始终独立发布消息的线程和主线程,因此同样不能进行UI操作。

5.发布消息

从代码的任何部分发布消息。所有与该消息类型匹配的当前注册的订阅者都将收到它。

EventBus.getDefault().post(MessageObj.getInstance());

6.黏性事件

某些事件包含事件发布后感兴趣的信息。例如,事件表示某些初始化已完成。或者,如果您有一些传感器或位置数据,并且您希望保留最新值。您可以使用粘性事件,而不是实现自己的缓存。所以EventBus将某种类型的最后一个粘性事件保存在内存中。然后,粘性事件可以传递给订阅者或明确查询。因此,您不需要任何特殊逻辑来考虑已有的数据。总结一句话就是:黏性事件可以先发布、后注册,在注册期间,所有粘性订阅者方法将立即获得先前发布的粘性事件。

    //发布事件
    EventBus.getDefault().post(MessageObj.getInstance());       //普通方式
    EventBus.getDefault().postSticky(MessageObj.getInstance()); //黏性事件


    //订阅者--普通方式
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMessageObj(MessageObj messageObj){
        //TODO 处理获取的消息对象
    }

    //订阅者--黏性事件
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void getMessageObj(MessageObj messageObj){
        //TODO 处理获取的消息对象
    }

7.优先级

虽然EventBus的大多数用例不需要优先级,但在某些特殊情况下它们可能会派上用场。您可以通过在注册期间为订户提供优先级来更改事件传递的顺序。在同一传递线程(ThreadMode)中,较高优先级的订户将在优先级较低的其他订户之前接收事件。注意:优先级不会影响具有不同ThreadModes的订阅者的传递顺序

    //订阅者--黏性事件
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
    public void getMessageObj(MessageObj messageObj){
        //TODO 处理获取的消息对象
    }

priority参数就是决定优先级的参数,它是一个整数类型的值,默认是0,其值越大表示优先级越高,这个订阅者会优先接受到事件。

三、分析

1.getDefault

通过单例模式double-checked locking获取对象,并在构造函数中获取EventBusBuilder构造器,并初始化必须的参数和集合。

    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();    
    
    ...    
   
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

    ...

    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        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;
    }

    

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

首先获取订阅者class对象,SubscriberMethodFinder调用findSubscriberMethods方法查询到SubscriberMethod集合,并通过subscribe使订阅订阅者订阅集合所有的SubscriberMethod。SubscriberMethod类中,主要保存订阅方法的Method对象、线程模式、事件类型、优先级、是否是粘性事件等属性。

这里看一下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;
        }
    }

这里有一个缓存机制、优先使用缓存中的数据、如果缓存中数据为空,ignoreGeneratedIndex表示是否忽略注解器生成的MyEventBusIndex,在EventBusBuilder构造器中默认为false,所以这里通过findUsingInfo方法查找集合并重新存入缓存中。

下面我们来分析下findUsingInfo方法。

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //获得订阅者的信息,一开始会返回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);
    }

首先它初始化了FindState对象,该对象是SubscriberMethodFinder的静态内部类,它保存了SubscriberMethod集合以及对其的校验。如果我们通过EventBusBuilder配置了MyEventBusIndex,便会获取到subscriberInfo,调用subscriberInfo的getSubscriberMethods方法便可以得到SubscriberMethod相关的信息,否则调用findUsingReflectionInSingleClass(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");
            }
        }
    }

这里通过Java反射机制获取所有的method,然后通过注解接口Subscribe筛选订阅方法,并将相关信息保存到FindState中。

最终上面提到的findUsingInfo方法调用getMethodsAndRelease(findState)返回SubscriberMethod集合。

    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

从findState获取subscriberMethods,放进新的ArrayList。需要注意的是这里使用了一个复用池FIND_STATE_POOL。

3.subscribe

在register中,最终调用了subscribe方法来使subscriber订阅subscriberMethod。

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //获取订阅事件类型的class类
        Class<?> eventType = subscriberMethod.eventType;
        //订阅者和订阅方法 封装成Subscription
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //获取当前订阅事件中Subscription的List集合
        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);
            }
        }

        //根据优先级来设置放进subscriptions的位置,优先级高的会先被通知
        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;
            }
        }
        
        //根据subscriber(订阅者)来获取它的所有订阅事件
        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);
            }
        }
    }

首先根据subscriber和subscriberMethod创建一个Subscription对象,该对象封装了一个active。然后通过事件类型获取Subscription集合,如果该集合不存在,则创建集合并将上面的Subscription对象存入集合中。如果集合中包含了上述Subscription对象,则抛出异常表示该事件已经注册过了。接着根据优先级来设置放进subscriptions的位置。最后将订阅事件类型用同样的方式存入事件类型的集合中。至此事件订阅成功,至于后面的黏性事件处理,后面在一起分析。

4.post

    /** 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 = 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;
            }
        }
    }

首先通过currentPostingThreadState获取当前线程的postingState,rrentPostingThreadState是一个ThreadLocal,不同的线程之间不会相互影响。得到当前线程的消息队列,并将当前事件event放入消息队列eventQueue中,然后判断postingState是否有消息正在分发,设置是否在主线程中分发,最后调用postSingleEvent进行分发。

    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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

eventInheritance表示是否向上查找事件的父类,默认值为true,当为true时,通过lookupAllEventTypes找到所有的父类的事件并存在List中,最后则遍历事件通过postSingleEventForEventType处理事件。

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            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;
    }

首先获取Subscription集合,并遍历集合,将subscription封装进PostingState中,最后调用postToSubscription进行分发。

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

postToSubscription中获取线程模式,之后根据所设置的线程模式来选择线程执行订阅方法的线程。invokeSubscriber方法主要是通过订阅方法的反射来执行。而mainThreadPoster主要是使用消息队列的形式Handler分发。

5.unregister

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

首先获取所有的订阅事件类型,然后遍历该集合并调用unsubscribeByEventType()方法从事件类型中移除订阅者。

    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> 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--;
                }
            }
        }
    }

获取Subscription集合并遍历,通过Subscription中的subscriber属性比较订阅者,判断出要注销的订阅者,设置active为false并从移除Subscription。

四、参考链接

EventBus文档

EventBus用法及源码解析

发布了10 篇原创文章 · 获赞 3 · 访问量 503

猜你喜欢

转载自blog.csdn.net/ledding/article/details/100195045