框架手写系列---通过反射手写EventBus框架

一、EventBus原理与分析

EventBus作为常用框架之一,从早期的EventBus2.0到目前3.+,经历了从反射到apt实现的转变,本文以原理分析为主,以反射方式实现早期的2.0框架。

EventBus的核心在于:将被注解的方法(如@Subscribe注解方法),放到Map中。后续post的时候,根据post的参数类型(如Message类)在Map中找到对应的方法,并调用。

将步骤分解后,就是以下两个主要操作:

1、注册:通过特定的注解(如@Subscribe),将被注解的方法,添加到Map中。

2、信息传递:通过post方法,在Map中查找符合要求的方法,并反射调用。

二、手写实现

根据分析,我们再将以上的步骤细化,如下:

1、定义一个注解Subscribe,用于标识哪些方法需要被添加到Map中。

//运行时起效
@Retention(RetentionPolicy.RUNTIME)
//针对方法起效
@Target(ElementType.METHOD)
public @interface Subscribe {
   ThreadModel threadModel() default ThreadModel.ANY;
}

  其他相关定义:

 

//线程控制枚举:任意,主线程,子线程
public enum ThreadModel {
    ANY,
    MAIN,
    BACKGROUND
}

2、定义一个单例类(ABus),Map将作为变量放置在该类中,用于存储被Subscribe标记的方法。其中Map的key设为Object类型,可为页面或其他类对象。

//定义一个单例类,也就是通讯总线Bus
public class ABus {
    //用于存储某个类上,有被subscribe标识的方法
    private Map> methods;
    private static volatile ABus aBus;

    private ExecutorService threadPool;
    private Handler handler;

    private ABus() {
        this.methods = new HashMap<>();
        this.threadPool = Executors.newCachedThreadPool();
        this.handler = new Handler(Looper.getMainLooper());
    }

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

3、在ABus中,定义register(Object object)方法,入参object可为Activity、Fragment或者其他类对象。后续将根据这个object查找该类上所有有Subscribe标识的方法,并存入到Map中。

//注册类,将会把该类下的subscribe标识的方法,放入到Map中
public void register(Object object) {
        if (object == null) {
            return;
        }
        List methodLs = methods.get(object);
        if (methodLs == null) {
            this.methods.put(object, findSubscriber(object));
        }
}


...

//查找类下,所有被subscribe标识的方法
private List findSubscriber(Object object) {
        List methods = new ArrayList<>();
        Class aClass = object.getClass();
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //判断该方法是否含有Subscribe注解标识
            Subscribe subscribe = declaredMethod.getAnnotation(Subscribe.class);
            if (subscribe == null) {
                continue;
            }
            //参数获取
            Class[] parameterTypes = declaredMethod.getParameterTypes();
            //特殊要求:参数只能是一个,不能为空,也不能超过一个
            if (parameterTypes.length != 1) {
                continue;
            }
            methods.add(declaredMethod);
        }
        return methods;
}

4、在ABus中,定义post方法post(final Object msg)。post方法将遍历Map中的方法,匹配到与post入参一致的类,并根据线程要求,分发信息。

//post方法
public void post(final Object msg) {
        //获取Map中的所有key
        Set objects = methods.keySet();
        //如果post的入参,是已注册的key,则进入下方循环
        for (final Object object : objects) {
            //取得该类的所有被注解方法
            List subScribeMethods = methods.get(object);
            if (subScribeMethods != null) {
                //遍历方法
                for (final Method method : subScribeMethods) {
                    Class[] parameterTypes = method.getParameterTypes();
                    Class type = parameterTypes[0];
                    //post的入参 与 遍历方法参数 类型对比
                    if (type.isAssignableFrom(msg.getClass())) {
                        //线程控制
                        Subscribe subscribe = method.getAnnotation(Subscribe.class);
                        switch (subscribe.threadModel()) {
                            case MAIN:
                                if (isMainThread()) {
                                    //调用方法
                                    invoke(method, msg, object);
                                } else {
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(method, msg, object);
                                        }
                                    });
                                }
                                break;
                            case BACKGROUND:
                                if (isMainThread()) {
                                    threadPool.execute(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(method, msg, object);
                                        }
                                    });
                                } else {
                                    invoke(method, msg, object);
                                }
                                break;
                            case ANY:
                            default:
                                invoke(method, msg, object);
                                break;
                        }
                    }
                }
            }
        }
    }


    //调用方法
    private void invoke(Method method, Object msg, Object object) {
        try {
            method.invoke(object, msg);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
   //主线程判断
   private boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    } 
  

5、最后,在ABus中,补全unRegister方法,避免内存泄漏,完成整个的设计。

public void unRegister(Object object) {
        if (object == null) {
            return;
        }
        if(methods != null){
            methods.remove(object);
        }
    }

这里写的ABus与EventBus原理类似,功能一致。百多行代码即实现了整体的通讯总线。

你可能感兴趣的:(Android技巧)