EventBus的简单和原理

基本介绍

  1. EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
    源码

基本使用

  1. 自定义一个类,用于发送和接收信息。构造时传入要发送的信息,然后get方法返回信息。(也可以是空类).
    例如:
public class FirstEvent {  
  
    private String mMsg;  
    public FirstEvent(String msg) {  
        // TODO Auto-generated constructor stub  
        mMsg = msg;  
    }  
    public String getMsg(){  
        return mMsg;  
    }  
}  
  1. 在要接收信息的地方注册EventBus
  • 例如:在要接收信息的activity中的onCreate()中注册
EventBus.getDefault().register(this);
  //使用默认方式注册
  1. 在注册的页面中注销EventBus
  • 例如:在activity中的onDestroy中注销:
EventBus.getDefault().unregister(this);    //反注册EventBus
  1. 发送消息,使用EnventBus的post方法
  • 发送消息是使用EventBus中的Post方法来实现发送的,发送过去的是我们新建的类的实例!
EventBus.getDefault().post(new FirstEvent("FirstEvent btn clicked"));  
  1. 接收消息,在要接收消息的地方重写EnventBus的接收信息的函数
  • 例如重写onEnventMainthread()
 public void onEventMainThread(FirstEvent event) {    
        Log.d("harvic", "onEventMainThread收到了消息:" + event.getMsg());  
    }  

EventBus的接收函数

  1. EventBus的接收函数有以下4种:onEvent、onEventMainThread、onEventBackgroundThread、onEventAsync
  • onEvent: 如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
  • onEventMainThread: 如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行, 接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
  • onEventBackground: 如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
  • onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程来执行.
  1. 告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布。观察者被告知事件发生叫做事件的接收,是通过上面的订阅函数实现的。

EnventBus如何决定所调用的函数

  1. 有多个不同参数的函数时,根据所传参数来决定调用的函数

例如有下面三个接收函数:

public void onEventMainThread(FirstEvent event) {                               // 1
    Log.d("harvic", "onEventMainThread收到了消息:" + event.getMsg());  
}  
  
public void onEventMainThread(SecondEvent event) {                              //  2
    Log.d("harvic", "onEventMainThread收到了消息:" + event.getMsg());  
}  
  
public void onEvent(ThirdEvent event) {                                         //  3
    Log.d("harvic", "OnEvent收到了消息:" + event.getMsg());  
}  

根据传的参数类型调用

EventBus.getDefault().post(new FirstEvent("FirstEvent btn clicked"));      //调用函数 1
EventBus.getDefault().post(new SecondEvent ("SecondEvent btn clicked"));   //调用函数 2
EventBus.getDefault().post(new ThirdEvent ("ThirdEvent btn clicked"));     //调用函数 3
  1. 有多个相同参数不同调用函数时,每一个都会被调用到

例如有下面三个接收函数:

//SecondEvent接收函数一  
public void onEventMainThread(SecondEvent event) {   
    Log.d("harvic", "onEventMainThread收到了消息:" + event.getMsg());  
}  
//SecondEvent接收函数二  
public void onEventBackgroundThread(SecondEvent event){  
    Log.d("harvic", "onEventBackground收到了消息:" + event.getMsg());  
}  
//SecondEvent接收函数三  
public void onEventAsync(SecondEvent event){  
    Log.d("harvic", "onEventAsync收到了消息:" + event.getMsg());  
}  

当调用EventBus.getDefault().post(new SecondEvent ("SecondEvent btn clicked"));时,上述三个函数都会被调用,但顺序不确定。

EventBus原理

设计模式

EventBus是基于观察者模式扩展而来的,又可称为发布 - 订阅模式,它定义了对象间的一种1对多的依赖关系,每当这个对象的状态改变时,其它的对象都会接收到通知并被自动更新。

当然,EventBus的观察者模式和一般的观察者模式不同,它使用了扩展的观察者模式对事件进行订阅和分发,其实这里的扩展就是指的使用了EventBus来作为中介者,抽离了许多职责,如下是它的官方原理图:

EventBus的简单和原理_第1张图片

每次我们在register之后,都必须进行一次unregister,这是为什么呢?

  • 因为register是强引用,它会让对象无法得到内存回收,导致内存泄露。所以必须在unregister方法中释放对象所占的内存。

EventBus2.x的版本与EventBus3.x的版本有哪些区别呢?

  • EventBus2.x使用的是运行时注解,它采用了反射的方式对整个注册的类的所有方法进行扫描来完成注册,因而会对性能有一定影响。
  • EventBus3.x使用的是编译时注解,Java文件会编译成.class文件,再对class文件进行打包等一系列处理。在编译成.class文件时,EventBus会使用EventBusAnnotationProcessor注解处理器读取@Subscribe()注解并解析、处理其中的信息,然后生成Java类来保存所有订阅者的订阅信息。这样就创建出了对文件或类的索引关系,并将其编入到apk中。
  • 从EventBus3.0开始使用了对象池缓存减少了创建对象的开销。

现在比较流行的事件总线还有RxBus,那么,它与EventBus相比又如何呢?

  • RxJava的Observable有onError、onComplete等状态回调。
  • Rxjava使用组合而非嵌套的方式,避免了回调地狱。
  • Rxjava的线程调度设计的更加优秀,更简单易用。
  • Rxjava可使用多种操作符来进行链式调用来实现复杂的逻辑。
  • Rxjava的信息效率高于EventBus2.x,低于EventBus3.x。

源码分析

注册方法

首先,我们从获取EventBus实例的方法getDefault()开始分析:

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

在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例。

接着,我们看到EventBus的默认构造方法中做了什么:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBusBuilder的构造方法中什么也没有做,那我么继续查看EventBus的这个有参构造方法:

private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map>> typesBySubscriber;
private final Map, Object> stickyEvents;

EventBus(EventBusBuilder builder) {
    ...

    // 1
    subscriptionsByEventType = new HashMap<>();

    // 2
    typesBySubscriber = new HashMap<>();

    // 3
    stickyEvents = new ConcurrentHashMap<>();

    // 4
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);

    ...

    // 5
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);

    // 从builder取中一些列订阅相关信息进行赋值
    ...

    // 6
    executorService = builder.executorService;
}

在注释4处,新建了三个不同类型的事件发送器,这里总结下:

  • mainThreadPoster:主线程事件发送器,通过它的mainThreadPoster.enqueue(subscription, event)方法可以将订阅信息和对应的事件进行入队,然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法
  • backgroundPoster:后台事件发送器,通过它的enqueue() 将方法加入到后台的一个队列,最后通过线程池去执行,注意,它在 Executor的execute()方法 上添加了 synchronized关键字 并设立 了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
  • asyncPoster:实现逻辑类似于backgroundPoster,不同于backgroundPoster的保证任一时间只且仅能有一个任务会被线程池执行的特性,asyncPoster则是异步运行的,可以同时接收多个任务
public void register(Object subscriber) {
    Class subscriberClass = subscriber.getClass();

    // 1
    List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 2
            subscribe(subscriber, subscriberMethod);
        }
    }
}

在注释1处,根据当前注册类获取 subscriberMethods这个订阅方法列表 。在注释2处,使用了增强for循环令subsciber对象 对 subscriberMethods 中每个 SubscriberMethod 进行订阅。

源码分析

你可能感兴趣的:(第三方库,android,多线程)