otto源码解析

tips:

  • 接口的使用
public interface ThreadEnforcer {
    ThreadEnforcer ANY = new ThreadEnforcer() {
        public void enforce(Bus bus) {
        }
    };
    ThreadEnforcer MAIN = new ThreadEnforcer() {
        public void enforce(Bus bus) {
            if(Looper.myLooper() != Looper.getMainLooper()) {
                throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper());
            }
        }
    };

    void enforce(Bus var1);
}
  • 判断是否为主线程:
if(Looper.myLooper() != Looper.getMainLooper()) {
                throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper());
            }
  • 主要是Bus.java里面的代码:
    关键的方法有

public void register(Object object)
该方法的作用是查找object里面所有带有Produce和Subscribe注解的方法,并保存在Map中,并且会立即执行Produce注解的方法。

private static void loadAnnotatedMethods(Class<?> listenerClass) {
        HashMap subscriberMethods = new HashMap();
        HashMap producerMethods = new HashMap();
        Method[] arr$ = listenerClass.getDeclaredMethods();//将所有的方法都取出来
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Method method = arr$[i$];
            Class[] parameterTypes;
            Class eventType;
            if(method.isAnnotationPresent(Subscribe.class)) {  //自定义注解获取方法
                parameterTypes = method.getParameterTypes();
                if(parameterTypes.length != 1) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires " + parameterTypes.length + " arguments.  Methods must require a single argument.");
                }

                eventType = parameterTypes[0];
                if(eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType + " which is an interface.  Subscription must be on a concrete class type.");
                }

                if((method.getModifiers() & 1) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType + " but is not \'public\'.");
                }

                Object methods = (Set)subscriberMethods.get(eventType);
                if(methods == null) {
                    methods = new HashSet();
                    subscriberMethods.put(eventType, methods);
                }

                ((Set)methods).add(method);
            } else if(method.isAnnotationPresent(Produce.class)) {
                parameterTypes = method.getParameterTypes();
                if(parameterTypes.length != 0) {
                    throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires " + parameterTypes.length + " arguments.  Methods must require zero arguments.");
                }

                if(method.getReturnType() == Void.class) {
                    throw new IllegalArgumentException("Method " + method + " has a return type of void.  Must declare a non-void type.");
                }

                eventType = method.getReturnType();
                if(eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType + " which is an interface.  Producers must return a concrete class type.");
                }

                if((method.getModifiers() & 1) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType + " but is not \'public\'.");
                }

                if(producerMethods.containsKey(eventType)) {
                    throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
                }

                producerMethods.put(eventType, method);
            }
        }

        PRODUCERS_CACHE.put(listenerClass, producerMethods); //将所有带有Subscribe.class gen跟Produce.class 注解的方法保存下来
        SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
    }
  • public void post(Object event)
    发送事件event,根据之前注册过的object里面的方法,查找参数为event的Subscribe方法,并invoke该方法。这样就达到了post之后,调用对应Subscribe方法的目的。
 EventHandler(Object target, Method method) {
        if(target == null) {
            throw new NullPointerException("EventHandler target cannot be null.");
        } else if(method == null) {
            throw new NullPointerException("EventHandler method cannot be null.");
        } else {
            this.target = target;
            this.method = method;
            method.setAccessible(true);  //注意此处的开关是关闭java的安全检查机制,能提高反射的性能
            boolean prime = true;
            this.hashCode = (31 + method.hashCode()) * 31 + target.hashCode();
        }
    }
    
 public void handleEvent(Object event) throws InvocationTargetException {
        try {
            this.method.invoke(this.target, new Object[]{event});
        } catch (IllegalAccessException var3) {
            throw new AssertionError(var3);
        } catch (InvocationTargetException var4) {
            if(var4.getCause() instanceof Error) {
                throw (Error)var4.getCause();
            } else {
                throw var4;
            }
        }
    }
  • public void unregister(Object object)
    注销object,删除掉map中保存的object的方法,释放object,防止内存泄露。

总结:otto将所有带有注解的类存到map里面,包含了类,方法名,参数,等post或者有注解的时候,使用invoke 反射调用,所以都是使用了反射实现。

这里有一个小工具类,以后可以直接copy ,查找annotion

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.squareup.otto;

import com.squareup.otto.EventHandler;
import com.squareup.otto.EventProducer;
import com.squareup.otto.Produce;
import com.squareup.otto.Subscribe;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

final class AnnotatedHandlerFinder {
    private static final Map<Class<?>, Map<Class<?>, Method>> PRODUCERS_CACHE = new HashMap();
    private static final Map<Class<?>, Map<Class<?>, Set<Method>>> SUBSCRIBERS_CACHE = new HashMap();

    private static void loadAnnotatedMethods(Class<?> listenerClass) {
        HashMap subscriberMethods = new HashMap();
        HashMap producerMethods = new HashMap();
        Method[] arr$ = listenerClass.getDeclaredMethods();
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Method method = arr$[i$];
            Class[] parameterTypes;
            Class eventType;
            if(method.isAnnotationPresent(Subscribe.class)) {
                parameterTypes = method.getParameterTypes();
                if(parameterTypes.length != 1) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires " + parameterTypes.length + " arguments.  Methods must require a single argument.");
                }

                eventType = parameterTypes[0];
                if(eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType + " which is an interface.  Subscription must be on a concrete class type.");
                }

                if((method.getModifiers() & 1) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType + " but is not \'public\'.");
                }

                Object methods = (Set)subscriberMethods.get(eventType);
                if(methods == null) {
                    methods = new HashSet();
                    subscriberMethods.put(eventType, methods);
                }

                ((Set)methods).add(method);
            } else if(method.isAnnotationPresent(Produce.class)) {
                parameterTypes = method.getParameterTypes();
                if(parameterTypes.length != 0) {
                    throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires " + parameterTypes.length + " arguments.  Methods must require zero arguments.");
                }

                if(method.getReturnType() == Void.class) {
                    throw new IllegalArgumentException("Method " + method + " has a return type of void.  Must declare a non-void type.");
                }

                eventType = method.getReturnType();
                if(eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType + " which is an interface.  Producers must return a concrete class type.");
                }

                if((method.getModifiers() & 1) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType + " but is not \'public\'.");
                }

                if(producerMethods.containsKey(eventType)) {
                    throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
                }

                producerMethods.put(eventType, method);
            }
        }

        PRODUCERS_CACHE.put(listenerClass, producerMethods);
        SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
    }

    static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
        Class listenerClass = listener.getClass();
        HashMap handlersInMethod = new HashMap();
        if(!PRODUCERS_CACHE.containsKey(listenerClass)) {
            loadAnnotatedMethods(listenerClass);
        }

        Map methods = (Map)PRODUCERS_CACHE.get(listenerClass);
        if(!methods.isEmpty()) {
            Iterator i$ = methods.entrySet().iterator();

            while(i$.hasNext()) {
                Entry e = (Entry)i$.next();
                EventProducer producer = new EventProducer(listener, (Method)e.getValue());
                handlersInMethod.put(e.getKey(), producer);
            }
        }

        return handlersInMethod;
    }

    static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
        Class listenerClass = listener.getClass();
        HashMap handlersInMethod = new HashMap();
        if(!SUBSCRIBERS_CACHE.containsKey(listenerClass)) {
            loadAnnotatedMethods(listenerClass);
        }

        Map methods = (Map)SUBSCRIBERS_CACHE.get(listenerClass);
        if(!methods.isEmpty()) {
            Iterator i$ = methods.entrySet().iterator();

            while(i$.hasNext()) {
                Entry e = (Entry)i$.next();
                HashSet handlers = new HashSet();
                Iterator i$1 = ((Set)e.getValue()).iterator();

                while(i$1.hasNext()) {
                    Method m = (Method)i$1.next();
                    handlers.add(new EventHandler(listener, m));
                }

                handlersInMethod.put(e.getKey(), handlers);
            }
        }

        return handlersInMethod;
    }

    private AnnotatedHandlerFinder() {
    }
}

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

推荐阅读更多精彩内容