EventBus源码分析及仿EventBus实现

EventBus源码分析

我分析源码一般是从整体来进行分析,对于一些细节的东西,我没有时间也没有能力去分析。

简单使用

导入依赖

implementation'org.greenrobot:eventbus:3.1.1'

定义观察者

public class MyObserver1 {
    public MyObserver1() {
        EventBus.getDefault().register(this);
    }

    @Subscribe
    public void myEvent(MyEvent event){
        System.out.println("我是观察者1,我观察到了");
    }
}
public class MyObserver2 {
    public MyObserver2() {
        EventBus.getDefault().register(this);
    }

    @Subscribe
    public void myEvent(MyEvent event) {
        System.out.println("我是观察者2,我观察到了");
    }
}

定义被观察者和测试

public class MyObserved {
    public MyObserver1 observer1;
    public MyObserver2 observer2;

    public MyObserved() {
        observer1 = new MyObserver1();
        observer2 = new MyObserver2();
    }

    public static void main(String[] args) {
        MyObserved observed = new MyObserved();
        Scanner in = new Scanner(System.in);
        while (true) {
            int code = in.nextInt();
            if (code == 1) {
                observed.sendEvent();
            } else {
                EventBus.getDefault().unregister(observed.observer1);
                EventBus.getDefault().unregister(observed.observer2);
                break;
            }
        }
    }

    public void sendEvent() {
        MyEvent event = new MyEvent();
        EventBus.getDefault().post(event);
    }
}

输出

1
我是观察者1,我观察到了
我是观察者2,我观察到了
1
我是观察者1,我观察到了
我是观察者2,我观察到了
2

具体分析

这里就从上面使用到的方法来进行分析。

入口方法

getDefault()

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

这里就是采用双重校验并加锁(DCL)的单例模式生成EventBus实例。

注册

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

findSubscriberMethods(Class<?> subscriberClass)

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);
    }
    //。。。省略
}

findUsingReflection(Class<?> subscriberClass)

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    //...
        findUsingReflectionInSingleClass(findState);
    //...
}

findUsingReflectionInSingleClass(FindState findState)

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

以上几个方法的大致逻辑就是先看有没有SubscriberMethod的缓存,有的话直接用,没有的话就找出添加了@Subscribe注解的方法。在findUsingReflectionInSingleClass方法里也可以看到很明显的以反射机制处理注解的逻辑。

SubscriberMethod类

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

    //...
}

从成员变量可以看出这个SubscriberMethod是用来封装观察者的添加了Subscribe注解的方法,观察者有一个观察方法就有一个SubscriberMethod。

最终封装

上面的register方法只看了一部分,还有下面一部分。

public void register(Object subscriber) {
    //...
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

这里将观察者的每个观察方法进行最终封装。

subscribe(Object subscriber, SubscriberMethod subscriberMethod)

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    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);
        }
    }
    //。。。
}

上面的逻辑就是为了避免二次注册,而我省略的部分可以总结为将Subscription添加进容器里。

Subscription类

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
    //...
}

主要封装了观察者对象和其方法

EventBus有个关键的成员变量:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

这个Map以事件类型为键,对应的观察方法的容器为值。

而Subscription封装好了之后就是放入这个Map里的。

发送

post()

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

post主要逻辑就是根据传入的事件,根据其事件类型(Class<?>)从subscriptionsByEventType取出对应事件类型的Subscription,然后利用反射机制,调用方法。

仿EventBus实现

这里就根据上面的分析来实现一个简单的EventBus。

构造单例对象

private HashMap<Class<?>, ArrayList<Observation>> mObserverArrayListHashMap;

private MyEventBus() {
    mObserverArrayListHashMap = new HashMap<>();
}

public static MyEventBus getDefault() {
    return SingletonHolder.SINGLETON;
}

private static class SingletonHolder {
        private static final MyEventBus SINGLETON = new MyEventBus();
    }

这里我采用静态内部类的方式实现单例模式

注册

public void register(Object observer) {
    Class<?> observerClass = observer.getClass();
    List<ObserverMethod> observerMethods = findObserverMethods(observerClass);
    for (ObserverMethod observerMethod : observerMethods) {
        observe(observer, observerMethod);
    }
}

private List<ObserverMethod> findObserverMethods(Class<?> observerClass) {
        List<ObserverMethod> observerMethods = new ArrayList<>();
        for (Method method : observerClass.getDeclaredMethods()) {
            Observe observe = method.getAnnotation(Observe.class);
            if (observe != null) {
                Class<?> eventType = method.getParameterTypes()[0];
                ObserverMethod observerMethod = new ObserverMethod(method, eventType);
                observerMethods.add(observerMethod);
            }
        }
        return observerMethods;
    }
    
private void observe(Object observer, ObserverMethod observerMethod) {
        Class<?> eventType = observerMethod.getEventType();
        Observation observation = new Observation(observer, observerMethod);
        ArrayList<Observation> observations = mObserverArrayListHashMap.get(eventType);
        if (observations == null) {
            observations = new ArrayList<>();
            mObserverArrayListHashMap.put(eventType, observations);
        } else {
            if (observations.contains(observation)) {
                return;
            }
        }
        observations.add(observation);
    }

两个封装类

class ObserverMethod {
    private Method mMethod;
    private Class<?> mEventType;

    public ObserverMethod(Method method, Class<?> eventType) {
        mMethod = method;
        this.mEventType = eventType;
    }

    public Method getMethod() {
        return mMethod;
    }

    public Class<?> getEventType() {
        return mEventType;
    }
}
class Observation {
    private Object observer;
    private ObserverMethod observerMethod;

    public Observation(Object observer, ObserverMethod observerMethod) {
        this.observer = observer;
        this.observerMethod = observerMethod;
    }

    public Object getObserver() {
        return observer;
    }

    public ObserverMethod getObserverMethod() {
        return observerMethod;
    }
}

发送

public void post(Object event) {
    Class<?> eventType = event.getClass();
    ArrayList<Observation> observations = mObserverArrayListHashMap.get(eventType);
    if (observations == null) {
        return;
    }
    for (Observation observation : observations) {
        Object observer = observation.getObserver();
        Method method = observation.getObserverMethod().getMethod();
        try {
            method.invoke(observer, event);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

测试

观察者

public class MyObserver1 {
    public MyObserver1() {
        MyEventBus.getDefault().register(this);
    }

    @Observe
    public void myEvent(MyEvent event){
        System.out.println("我是观察者1,我观察到了");
    }
}

public class MyObserver2 {
    public MyObserver2() {
        MyEventBus.getDefault().register(this);
    }

    @Observe
    public void myEvent(MyEvent event){
        System.out.println("我是观察者2,我观察到了");
    }
}

事件

public class MyEvent {
}

被观察者及测试

public class MyObserved {
    public MyObserved() {
        MyObserver1 observer1 = new MyObserver1();
        MyObserver2 observer2 = new MyObserver2();
    }

    public static void main(String[] args) {
        MyObserved observed = new MyObserved();
        Scanner in = new Scanner(System.in);
        while (true) {
            int code = in.nextInt();
            if (code == 1) {
                observed.sendEvent();
            } else {
                break;
            }
        }
    }

    public void sendEvent() {
        MyEvent event = new MyEvent();
        MyEventBus.getDefault().post(event);
    }
}

output:
1
我是观察者1,我观察到了
我是观察者2,我观察到了
1
我是观察者1,我观察到了
我是观察者2,我观察到了
2

Process finished with exit code 0
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容

  • 项目到了一定阶段会出现一种甜蜜的负担:业务的不断发展与人员的流动性越来越大,代码维护与测试回归流程越来越繁琐。这个...
    fdacc6a1e764阅读 3,187评论 0 6
  • EventBus源码分析 Android开发中我们最常用到的可以说就是EventBus了,今天我们来深入研究一下E...
    BlackFlag阅读 509评论 3 4
  • EventBus基本使用 EventBus基于观察者模式的Android事件分发总线。 从这个图可以看出,Even...
    顾氏名清明阅读 625评论 0 1
  • EventBus地址:https://github.com/greenrobot/EventBus 一、event...
    君莫看阅读 1,995评论 2 11
  • 简介 前面我学习了如何使用EventBus,还有了解了EventBus的特性,那么接下来我们一起来学习EventB...
    eirunye阅读 465评论 0 0