一、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<Object, List<Method>> 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<Method> methodLs = methods.get(object);
if (methodLs == null) {
this.methods.put(object, findSubscriber(object));
}
}
...
//查找类下,所有被subscribe标识的方法
private List<Method> findSubscriber(Object object) {
List<Method> 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<Object> objects = methods.keySet();
//如果post的入参,是已注册的key,则进入下方循环
for (final Object object : objects) {
//取得该类的所有被注解方法
List<Method> 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原理类似,功能一致。百多行代码即实现了整体的通讯总线。