為什么要手寫一個(gè)EventBus?
有人可能要說(shuō)了,晚上有寫好的EventBus,直接用不得了,自己寫的能有人家寫的好嗎澜薄?確實(shí),我做不到官方那么好摊册。但我們做開發(fā)以來(lái)肤京,一直都是“拿來(lái)主義”,感覺(jué)會(huì)用api實(shí)現(xiàn)項(xiàng)目功能就可以了茅特。即使看過(guò)官方的源碼忘分,也是似懂非懂的,很快就忘記了温治。因?yàn)閷?duì)第三方框架的認(rèn)識(shí)饭庞,如果加上自己的理解,可以寫出一個(gè)簡(jiǎn)易版本的框架熬荆。那么下次有人再問(wèn)你Eventbus的原理舟山,你就可以將你的思路告訴他。
框架思路
寫代碼的時(shí)候卤恳,不是來(lái)了需求就埋頭苦寫累盗,好的設(shè)計(jì)流程能幫你事半功倍。
手寫eventbus.png
思路詳解
注冊(cè)
注冊(cè)的基本思路突琳,通過(guò)上圖的流程圖很清楚了:根據(jù)當(dāng)前的注冊(cè)類若债,獲取SubscribeMethod集合,如果獲取不到就去遍歷帶有指定注解的方法拆融,得到所有注解方法后蠢琳,加入到集合中。
- 緩存镜豹,這里通過(guò)map實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的緩存傲须,避免了每次都去遍歷注冊(cè)類中的方法。優(yōu)秀的框架都會(huì)考慮緩存趟脂,平時(shí)寫代碼的時(shí)候注意這一點(diǎn)泰讽。
- getSubsribeMethods方法∥羝冢考慮下代碼封裝已卸,而不是把所有的代碼抖寫到register這個(gè)方法中。
- isAnnotationPresent 這個(gè)方法表示 某個(gè)方法上面是否含有指定的注解
- SubscribeMethod這個(gè)類 是對(duì)注冊(cè)方法的封裝硼一。里面有三個(gè)參數(shù)method(方法),ThreadMode(線程方式),paramType(參數(shù)類型)
public class EventBus {
public static EventBus eventBus = new EventBus();
public static EventBus getDefault() {
return eventBus;
}
private Map<Object, List<SubscribeMethod>> 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<SubscribeMethod> subscribeMethods = cacheMap.get(obj);
if (subscribeMethods != null) {
return;
}
List<SubscribeMethod> list = getSubsribeMethods(obj);
cacheMap.put(obj, list);
}
private List<SubscribeMethod> getSubsribeMethods(Object obj) {
Class<?> aClass = obj.getClass();
ArrayList<SubscribeMethod> 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;
}
// 獲取注冊(cè)類下的所有方法
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
//如果方法上有指定的注解
if (method.isAnnotationPresent(Subscribe.class)) {
Subscribe annotation = method.getAnnotation(Subscribe.class);
//獲取參數(shù)類型
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new RuntimeException("參數(shù)必須是一個(gè)");
}
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<SubscribeMethod> 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;
}
發(fā)送消息
//發(fā)送消息
public void post(final Object obj) {
Set<Object> keySet = cacheMap.keySet();
Iterator<Object> iterator = keySet.iterator();
while (iterator.hasNext()) {
final Object next = iterator.next();
List<SubscribeMethod> 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();
//發(fā)送消息的類型和注冊(cè)方法的參數(shù)是一致的
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;
}
}
}
}
}