为什么要手写一个EventBus?
有人可能要说了,晚上有写好的EventBus,直接用不得了,自己写的能有人家写的好吗?确实,我做不到官方那么好。但我们做开发以来,一直都是“拿来主义”,感觉会用api实现项目功能就可以了。即使看过官方的源码,也是似懂非懂的,很快就忘记了。因为对第三方框架的认识,如果加上自己的理解,可以写出一个简易版本的框架。那么下次有人再问你Eventbus的原理,你就可以将你的思路告诉他。
框架思路
写代码的时候,不是来了需求就埋头苦写,好的设计流程能帮你事半功倍。
思路详解
注册
注册的基本思路,通过上图的流程图很清楚了:根据当前的注册类,获取SubscribeMethod集合,如果获取不到就去遍历带有指定注解的方法,得到所有注解方法后,加入到集合中。
缓存,这里通过map实现了一个简单的缓存,避免了每次都去遍历注册类中的方法。优秀的框架都会考虑缓存,平时写代码的时候注意这一点。
getSubsribeMethods方法。考虑下代码封装,而不是把所有的代码抖写到register这个方法中。
isAnnotationPresent 这个方法表示 某个方法上面是否含有指定的注解
SubscribeMethod这个类 是对注册方法的封装。里面有三个参数method(方法),ThreadMode(线程方式),paramType(参数类型)
public class EventBus {
public static EventBus eventBus = new EventBus();
public static EventBus getDefault() {
return eventBus;
}
private Map> cacheMap;
private Handler handler;
private ExecutorService executor;
private EventBus() {
cacheMap = new HashMap<>();
handler = new Handler(Looper.getMainLooper());
executor = Executors.newCachedThreadPool();
}
public void register(Object obj) {
List subscribeMethods = cacheMap.get(obj);
if (subscribeMethods != null) {
return;
}
List list = getSubsribeMethods(obj);
cacheMap.put(obj, list);
}
private List getSubsribeMethods(Object obj) {
Class> aClass = obj.getClass();
ArrayList list = new ArrayList<>();
while (aClass != null) {
//如果父类是android java开头,就不需要
if (aClass.getName().startsWith("android.")
|| aClass.getName().startsWith("androidx.")
|| aClass.getName().startsWith("java.")
|| aClass.getName().startsWith("javax.")
) {
break;
}
// 获取注册类下的所有方法
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
//如果方法上有指定的注解
if (method.isAnnotationPresent(Subscribe.class)) {
Subscribe annotation = method.getAnnotation(Subscribe.class);
//获取参数类型
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new RuntimeException("参数必须是一个");
}
ThreadMode mode = annotation.getMode();
SubscribeMethod subscribeMethod = new SubscribeMethod(method, mode, parameterTypes[0]);
subscribeMethod.toString();
list.add(subscribeMethod);
}
}
aClass = aClass.getSuperclass();
}
return list;
}
public void unregister(Object subscriber) {
List list = cacheMap.get(subscriber);
if (list != null) {
cacheMap.remove(subscriber);
}
}
}
package com.autoai.bus.core;
import java.lang.reflect.Method;
public class SubscribeMethod {
private Method method;
private ThreadMode threadMode;
private Class> paramType;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public ThreadMode getThreadMode() {
return threadMode;
}
public void setThreadMode(ThreadMode threadMode) {
this.threadMode = threadMode;
}
public Class> getParamType() {
return paramType;
}
public void setParamType(Class> paramType) {
this.paramType = paramType;
}
public SubscribeMethod(Method method, ThreadMode threadMode, Class> paramType) {
this.method = method;
this.threadMode = threadMode;
this.paramType = paramType;
}
}
package com.autoai.bus.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode getMode() default ThreadMode.MAIN;
}
发送消息
//发送消息
public void post(final Object obj) {
Set keySet = cacheMap.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
final Object next = iterator.next();
List subscribeMethodList = cacheMap.get(next);
if (subscribeMethodList == null) return;
for (SubscribeMethod subscribeMethod : subscribeMethodList) {
final Method method = subscribeMethod.getMethod();
Class> paramType = subscribeMethod.getParamType();
ThreadMode mode = subscribeMethod.getThreadMode();
//发送消息的类型和注册方法的参数是一致的
if (paramType.isAssignableFrom(obj.getClass())) {
switch (mode) {
case MAIN:
if (Looper.myLooper() == Looper.getMainLooper()) {
invoke(next, method, obj);
} else {
handler.post(new Runnable() {
@Override
public void run() {
invoke(next, method, obj);
}
});
}
break;
case ASYNC:
if (Looper.myLooper() == Looper.getMainLooper()) {
executor.submit(new Runnable() {
@Override
public void run() {
invoke(next, method, obj);
}
});
} else {
invoke(next, method, obj);
}
break;
case DEFALUT:
break;
}
}
}
}
}