热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于android:EventBus-30-源码分析

EvenntBus是一个开源库,它利用公布订阅者者模式来对我的项目进行解耦。它能够利用很少的代码,来实现多组件间通信。android的组件间通信,咱们不由得会想到handler音讯机制和播送机制,通过它们也能够进行通信,然而应用它们进行通信,代码量多,组件间容易产生耦合援用。对于EventBus的工作模式,这里援用一张官网图帮忙

EventBus 3.0源码剖析

简介

EvenntBus 是一个开源库,它利用公布/订阅者者模式来对我的项目进行解耦。它能够利用很少的代码,来实现多组件间通信。android的组件间通信,咱们不由得会想到handler音讯机制和播送机制,通过它们也能够进行通信,然而应用它们进行通信,代码量多,组件间容易产生耦合援用。对于EventBus的工作模式,这里援用一张官网图帮忙了解。

为什么会抉择应用EventBus来做通信?

  • 简化了组件间交换的形式
  • 对事件通信单方进行解耦
  • 能够灵便不便的指定工作线程,通过ThreadMode
  • 速度快,性能好
  • 库比拟小,60k左右,对包大小无影响
  • 应用这个库的app多,有权威性
  • 性能多,使用方便

EventBus的应用也非常简单,三板斧。register,unregister, subscribe/post

三个重要的角色

1:Publisher 事件发布者

2:Subscriber 事件订阅者

3:Event 事件

Publisher post 事件后,Subscriber会主动收到事件(订阅办法会被被动调用,并将事件传递过去)。

应用

 //倒入gradle 依赖
 implementation 'org.greenrobot:eventbus:3.3.1'

1:定义事件类型

public static class MessageEvent { /* Additional fields if needed */ }

2:在须要订阅事件的模块中注册EventBus,页面销毁时留神登记

 @Override
 protected void onStart() {
       super.onStart();
    EventBus.getDefault().register(this);    
 }

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

3:注册须要承受的事件类型 //留神同一种事件类型不能反复注册。不然会解体,且订阅办法必须是public类型的。

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
    // Do something
}

4.发送事件

EventBus.getDefault().post(new MessageEvent());
这是步骤3种的办法就会收到MessageEvent事件的回调

源码剖析

EventBus 主类中只有不到600行代码,十分精简。EventBus应用了对外提供了单例模型,外部构建应用了Build模式。

1 register

public void register(Object subscriber) {
    if (AndroidDependenciesDetector.isAndroidSDKAvailable() &&     !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
        // Crash if the user (developer) has not imported the Android compatibility library.
        throw new RuntimeException("It looks like you are using EventBus on Android, " +
                "make sure to add the \"eventbus\" Android library to your dependencies.");
    }

        //1从这里开始看,获取调用者的类对象。
    Class subscriberClass = subscriber.getClass();
    //2 找到订阅类中的订阅办法
    List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    
    //3遍历订阅办法,将订阅者和其中的订阅办法绑定。
       synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

Step1

看下步骤2中的subscriberMethodFinder的findSubscriberMethods办法

List findSubscriberMethods(Class subscriberClass) {    
        //1首先从缓存map中拿到订阅类的订阅办法列表,应用了缓存进步性能,nice,不出所料METHOD_CACHE的类型是Map, List >
    List subscriberMethods = METHOD_CACHE.get(subscriberClass);
    //2 如果不为空,阐明之前该类已经注册过,该类的新对象不用从新做绑定了,因为此时的操作是类层面的
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
        //如果subscriberMethods 为null,阐明该类是第一次注册,须要将其中的接管办法保存起来,
      //ignoreGeneratedIndex 默认为false
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    //如果subscriberMethods为null,阐明以后类对象没有生命订阅办法,抛出异样
    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;
    }
}

Step2

从步骤2中找出类的注册办法列表,而后遍历列表,调用上面的办法,将类对象和注册办法绑定。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //1 找到订阅办法的事件类型,即发送事件的MessageEvent.class
    Class eventType = subscriberMethod.eventType;
  
      //2 将订阅者类对象和订阅事件绑定成一个对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //subscriptionsByEventType 这个汇合必定是用来搁置同一事件类型的订阅汇合的,因为一个事件可能会有多个订阅的。
    CopyOnWriteArrayList subscriptiOns= subscriptionsByEventType.get(eventType);
    if (subscriptiOns== null) {
        subscriptiOns= new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
            //如果一个订阅者屡次订阅了一个事件(@Subscribe注解的办法的参数是同一类型),抛出异样
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

        //3 依照订阅办法中@Subscribe中的priority参数进行排序,默认为最低优先级0。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;
        }
    }

        // typesBySubscriber类型Map>>,Key 为订阅者,value为订阅者中的订阅办法,用来记录每个订阅者外部都订阅了哪些事件类型
    List> 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).
            Set, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry, 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);
        }
    }
}

2 post

/** Posts the given event to the event bus. */
public void post(Object event) {
        //1首先获取以后线程的工作状态
    PostingThreadState postingState = currentPostingThreadState.get();
    //2获取以后线程的工作队列
    List eventQueue = postingState.eventQueue;
    //3 将事件退出到事件队列
    eventQueue.add(event);
     //4 如果以后线程的工作状态没有正在发送事件 
    if (!postingState.isPosting) {
            //标记postingState 的是否是主线程,并将工作状态isPosting 设为true
        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;
        }
    }
}


/**
* 发送事件
* @param event 事件
* @param postingState 以后线程相干的配置
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class eventClass = event.getClass();
        boolean subscriptiOnFound= false;
        //如果应用继承事件的父类/接口,比方你发送了MessageEvent 事件,如果该事件继承了BaseEvent和Ievent接口,那么当你发送                        MessageEvent 事件时,零碎也会发送BaseEvent和Ieven事件
        if (eventInheritance) {
                //遍历父类,将事件的父类/接口通通退出到eventTypes中
            List> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h  clazz = eventTypes.get(h);
                //遍历eventTypes,顺次发送调用事件
                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);
            }
            //EventBus 外部也实用EventBus 发送了一个异样事件
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
    

postSingleEventForEventType

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
    CopyOnWriteArrayList subscriptions;
    synchronized (this) {
        //1获取该事件的所有订阅关系列表
        subscriptiOns= subscriptionsByEventType.get(eventClass);
    }
    //2 遍历订阅关系列表,顺次将事件发送到订阅者
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            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

这里就比拟要害了,最终到了事件散发的中央了。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //1 首先判断订阅关系中订阅办法的线程,就是申明线程时应用@Subcribe注解时传入的threadMode字段的值
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING: //间接发送事件
            invokeSubscriber(subscription, event);
            break;
        case MAIN:  //在主线程相应事件
                    //事件收回线程是否是主线程
            if (isMainThread) { 是,间接发送
                invokeSubscriber(subscription, event);
            } else {不是通过mainThreadPoster发送
                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转发
                backgroundPoster.enqueue(subscription, event);
            } else { 非主线程发送事件,间接发送
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC: //异步线程相应事件
                //通过asyncPoster发送事件
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

invokeSubscriber

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

在postToSubscription中有三个重要的角色mainThreadPoster,backgroundPoster,asyncPoster

其中mainThreadPoster的类型是HandlerPoster。其实就是Handler。调用其enqueu()办法

而backgroundPoster和asyncPoster 实质都是Runnable

3 unregister

解绑办法就简略多了,重要的是就把register里提到的2个重要的几何中删除订阅者

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    //1 从typesBySubscriber找到订阅者所订阅的事件类型列表
    List> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        //2 遍历列表,顺次解绑订阅者和事件类型。应该是从post剖析里的订阅事件汇合subscriptionsByEventType里移除对应事件类型的该订阅者
        for (Class eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        //3移除订阅者
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

/**
* 解绑订阅者和事件类型
* @param subscriber 订阅者
* @param eventType  订阅的事件类型
*/
 private void unsubscribeByEventType(Object subscriber, Class eventType) {
                //从subscriptionsByEventType里获取该订阅事件的订阅者汇合。
        List subscriptiOns= subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i 

4 Subscribe注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

5 ThreadMode

//定义事件回调办法工作线程的类
public enum ThreadMode {
    /**
     *间接在发送事件的线程里调用Subscriber,这个是默认的设置,事件交付开销最小,因为它防止了线程切换。因而它是那种很快实现的单任务             *默认的的线程工作模型。应用该模型的事件必须很快实现,因为当公布线程是主线程时,它可能阻塞主线程。
     /
    POSTING,

    /**
     * 在Android平台,订阅者将会在Android的主线程调用。如果公布线程时主线程,订阅办法将会被间接调用。进而阻赛公布线程,如果公布线             * 程不是主线程。事件将会排队期待散发。应用这种模式的订阅者必须疾速实现工作,防止阻赛主线程。非Android平台和Posting一样
     */
    MAIN,

    /**
     * 在Android平台,订阅者将会在Android的主线程调用。不同于MAIN,事件将会有序散发。确保了post调用时非阻赛的。
     */
    MAIN_ORDERED,

    /** 
     * 在Android平台,订阅者将会在后盾线程被调用,如果公布线程不是主线程,订阅者将会被间接调用,如果公布线程时主线程,那么EventBus             * 应用后盾线程,进而有序散发所有事件,应用此模式的Subscribers应该疾速实现工作免得阻赛后台线程。非Android平台,总是用后盾线程                   * 相应事件 
     */
    BACKGROUND,

    /**
     * 订阅者将会在独自的线程被调用,总是独立于公布线程和主线程。公布事件从不会期待应用这种模式的订阅办法。如果订阅办法执行耗时工作,         * 则应该应用此模式。比方;网络拜访。防止同时触发大量的长时间运行的异步订阅办法,从而限度并发的线程数量。EventBus 应用线程池来            * 高效的服用已实现异步订阅告诉的线程
     */
    ASYNC
}

6 EventBus2.0和EventBus3.0的区别?

这是在面试过程中,面试官最常问的一个问题。

EventBus2.0和3.0最大的区别有两点:

1.EventBus2.0中咱们在书写订阅办法时的名字必须是onEvent结尾,而后通过命名不同来区别不同的线程模式。例如对应posting则命名为onEvent(),onEventMainThread()则对应main等。而3.0则能够用任何名字作为办法名称,只须要在办法名的后面用@Subscribe注解来进行正文,而后应用threadMode来设置在哪里线程中接管事件和处理事件

2.EventBus2.0应用的是反射的形式来查找所有的订阅办法,而3.0则是在编译时通过注解处理器的形式来查找所有的订阅办法。性能上来说,3.0比2.0要高的多。


推荐阅读
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 深入解析HTTP网络请求API:从基础到进阶的全面指南
    本文全面解析了HTTP网络请求API,从基础到进阶,详细介绍了Android平台上的两种原生API——HttpUrlConnection和HttpClient。这两种API通过对底层Socket的封装,提供了高效、灵活的网络通信功能。文章不仅涵盖了基本的使用方法,还深入探讨了性能优化、错误处理和安全性等方面的高级主题,帮助开发者更好地理解和应用这些工具。 ... [详细]
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
  • Eclipse JFace Text框架中IDocument接口的getNumberOfLines方法详解与编程实例 ... [详细]
  • 在基于.NET框架的分层架构实践中,为了实现各层之间的松散耦合,本文详细探讨了依赖注入(DI)和控制反转(IoC)容器的设计与实现。通过合理的依赖管理和对象创建,确保了各层之间的单向调用关系,从而提高了系统的可维护性和扩展性。此外,文章还介绍了几种常见的IoC容器实现方式及其应用场景,为开发者提供了实用的参考。 ... [详细]
  • 如何使用 net.sf.extjwnl.data.Word 类及其代码示例详解 ... [详细]
  • 本文作为“实现简易版Spring系列”的第五篇,继前文深入探讨了Spring框架的核心技术之一——控制反转(IoC)之后,将重点转向另一个关键技术——面向切面编程(AOP)。对于使用Spring框架进行开发的开发者来说,AOP是一个不可或缺的概念。了解AOP的背景及其基本原理,对于掌握这一技术至关重要。本文将通过具体示例,详细解析AOP的实现机制,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 在 Android 开发中,通过合理利用系统通知服务,可以显著提升应用的用户交互体验。针对 Android 8.0 及以上版本,开发者需首先创建并注册通知渠道。本文将详细介绍如何在应用中实现这一功能,包括初始化通知管理器、创建通知渠道以及发送通知的具体步骤,帮助开发者更好地理解和应用这些技术细节。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • DVWA学习笔记系列:深入理解CSRF攻击机制
    DVWA学习笔记系列:深入理解CSRF攻击机制 ... [详细]
  • Delphi XE Rtti单元深入解析:TRttiContext的应用与实践
    Delphi XE Rtti单元深入解析:TRttiContext的应用与实践 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • 如何撰写PHP电商项目的实战经验? ... [详细]
author-avatar
多米音乐_53913411
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有