仿EventBus写的事件总线工具,主要由MyEventBus实现事件订阅post等逻辑,其他EventBusException、Subscribe、SubscriberMethod、ThreadMode直接从EventBus拿来用的。
实现:
MyEventBus代码:
package com.david.core.event;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 仿eventbus的事件发布/订阅框架,对比eventBus添加了直接对订阅者方法订阅的功能(执行时间比遍历方法更快些)。
*
* example:
* 参考:
* {@link com.david.core.event.TestEvent}
* {@link com.david.basemodule.MainActivity}
*
*
* 注册:
*
* MyEventBus.getDefault().register(this, "testEvent", TestEvent::class.java)
* {@link com.david.basemodule.MainActivity#onCreate(Bundle)}
*
* 解注册:
* MyEventBus.getDefault().unregister(this, "testEvent", TestEvent::class.java)
* {@link com.david.basemodule.MainActivity#onDestroy()}
*
* 发送:
* MyEventBus.getDefault().post(new TestEvent());
* {@link #post(Object)}
*
* 接收方法:
* {@link com.david.basemodule.MainActivity#testEvent(TestEvent)}
*
*
* 注:相比于EventBus没有粘性event及自动搜索父类订阅方法等一些功能,但MyEventBus理论上应该能满足平时80-90%的需求
*
* 由于使用了方法名,因此在混淆时订阅方法不要混淆,如在混淆规则中添加:
* -keep public class com.david.andserver.MainActivity {
* public void onServerStatus(...);
* //或public void on**(...);
* }
* */
public class MyEventBus {
private volatile static MyEventBus defaultInstance;
/*
* In newer class files, compilers may add methods. Those are called bridge or synthetic methods.
* EventBus must ignore both. There modifiers are not public but defined in the Java class file format:
* http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1
*/
private static final int BRIDGE = 0x40;
private static final int SYNTHETIC = 0x1000;
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
private ExecutorService fixedExecutorService;
/**
* loop()运行在主线程的handler,用于切换到主线程
* */
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
/**
* event与method订阅方法对应的map,post过来event之后遍历执行对应列表里的方法
* */
private volatile Map<Class, ArrayList<SubscriberMethod>> eventMethodMap = new HashMap<>();
public static MyEventBus getDefault() {
MyEventBus instance = defaultInstance;
if (instance == null) {
synchronized (MyEventBus.class) {
instance = MyEventBus.defaultInstance;
if (instance == null) {
instance = MyEventBus.defaultInstance = new MyEventBus();
}
}
}
return instance;
}
/**
* 根据方法名注册,相对于遍历方法节省时间(对于类中订阅方法少的类较适用)
* */
public void register(Object subscriber, String methodName, Class eventclass) {
try {
Method method = subscriber.getClass().getDeclaredMethod(methodName, eventclass);
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) == 0 || (modifiers & MODIFIERS_IGNORE) != 0) {
throw new EventBusException(subscriber.getClass().getName() +"."+methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
} else if (!parameterTypes[0].getName().equals(eventclass.getName())) {
/*订阅方法中的event参数与注册方法中传入的event参数不对应*/
throw new EventBusException("method parameter " + parameterTypes[0].getName() +
" is not equals the input eventType:" + eventclass.getName());
}
addMethodToMap(subscriber, eventclass, method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private void addMethodToMap(Object subscriber, Class eventclass, Method method) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation == null) {
Log.w("eventbus", subscriber.getClass().getName() + "." +method.getName() + "'s didn't has annotation of Subscribe.");
return;
}
if (eventMethodMap.get(eventclass) == null) {
ArrayList<SubscriberMethod> methods = new ArrayList<>();
methods.add(new SubscriberMethod(method, subscribeAnnotation.threadMode(), subscriber));
eventMethodMap.put(eventclass, methods);
} else {
SubscriberMethod subscriberMethod = new SubscriberMethod(method, subscribeAnnotation.threadMode(), subscriber);
if (!eventMethodMap.get(eventclass).contains(subscriberMethod)) {
eventMethodMap.get(eventclass).add(subscriberMethod);
}
}
}
/***
* @param subscriber 订阅者
*/
public void register(Object subscriber){
findMethodAndRegister(subscriber);
}
/**
* 查找类中订阅方法并添加到map中,TODO 查找并添加父类中的订阅方法
* */
private void findMethodAndRegister(Object subscriber) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = subscriber.getClass().getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
th.printStackTrace();
return;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
addMethodToMap(subscriber, parameterTypes[0], method);
}
} else if (method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
/**
* post方法:获取对应的订阅方法列表并遍历在相应线程执行
* */
public void post(Object eventObject) {
ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(eventObject.getClass());
if (subscriberMethods == null) {
Log.w("eventbus", "the map of eventMethodMap is empty.");
return;
}
for (SubscriberMethod subscriberMethod : subscriberMethods) {
try {
if (subscriberMethod.threadMode == ThreadMode.ASYNC) {
invokeInSubThread(eventObject, subscriberMethod);
} else if (subscriberMethod.threadMode == ThreadMode.BACKGROUND) {
if (Looper.getMainLooper() != Looper.myLooper()) {
subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
} else {
invokeInSubThread(eventObject, subscriberMethod);
}
} else if ((subscriberMethod.threadMode == ThreadMode.MAIN || subscriberMethod.threadMode == ThreadMode.MAIN_ORDERED)) {
if (Looper.getMainLooper() != Looper.myLooper()) {
//mode为MAIN则post到主线程执行
invokeInMainThread(eventObject, subscriberMethod);
} else {
subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
}
} else {//POSTING
//mode为其他则直接在当前线程执行
subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void invokeInSubThread(Object eventObject, SubscriberMethod subscriberMethod) {
if (fixedExecutorService == null) {
fixedExecutorService = Executors.newFixedThreadPool(3);
}
fixedExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
private void invokeInMainThread(Object eventObject, SubscriberMethod subscriberMethod) {
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
try {
subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
/***
* @param subscriber 订阅者
*/
public void unregister(Object subscriber){
Set<Class> keys = eventMethodMap.keySet();
HashMap<Class, ArrayList<SubscriberMethod>> tempMap = new HashMap<>();
for (Class key:keys) {
ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(key);
ArrayList<SubscriberMethod> tempMethods = new ArrayList<>();
for (SubscriberMethod item:subscriberMethods) {
if (item.subscriber == subscriber) {
tempMethods.add(item);
}
}
subscriberMethods.removeAll(tempMethods);
if (subscriberMethods.size() > 0) {
tempMap.put(key, subscriberMethods);
}
}
eventMethodMap = tempMap;
}
public void unregister(Object subscriber, String methodName, Class eventClass) {
ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(eventClass);
if (subscriberMethods != null) {
ArrayList<SubscriberMethod> removeList = new ArrayList<>();
for (SubscriberMethod subscriberMethod : subscriberMethods) {
if (methodName.equals(subscriberMethod.getMethod().getName()) && subscriberMethod.subscriber == subscriber) {
removeList.add(subscriberMethod);
}
}
subscriberMethods.removeAll(removeList);
if (subscriberMethods.size() == 0) {
eventMethodMap.remove(eventClass);
}
}
}
}
EventBusException代码:
package com.david.core.event;
/**
* An {@link RuntimeException} thrown in cases something went wrong inside EventBus.
*
* @author Markus
*
*/
public class EventBusException extends RuntimeException {
private static final long serialVersionUID = -2912559384646531479L;
public EventBusException(String detailMessage) {
super(detailMessage);
}
public EventBusException(Throwable throwable) {
super(throwable);
}
public EventBusException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
}
package com.david.core.event;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
SubscriberMethod代码:
package com.david.core.event;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 每个订阅事件信息类
* */
class SubscriberMethod {
/**订阅回调的方法*/
Method method;
/**回调执行线程信息*/
ThreadMode threadMode;
/**订阅者,调用{@link com.david.core.event.MyEventBus#register(Object)}的方法的类*/
Object subscriber;
public SubscriberMethod() {
}
public SubscriberMethod(Method method, ThreadMode threadMode, Object subscriber) {
this.method = method;
this.threadMode = threadMode;
this.subscriber = subscriber;
}
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 Object getSubscribeObject() {
return subscriber;
}
public void setSubscribeObject(Object subscribeObject) {
this.subscriber = subscribeObject;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SubscriberMethod)) return false;
SubscriberMethod that = (SubscriberMethod) o;
return Objects.equals(getMethod(), that.getMethod()) &&
getThreadMode() == that.getThreadMode() &&
Objects.equals(subscriber, that.subscriber);
}
@Override
public int hashCode() {
return Objects.hash(getMethod(), getThreadMode(), subscriber);
}
}
ThreadMode代码:
package com.david.core.event;
/**
* Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.
* EventBus takes care of threading independently from the posting thread.
*
* @see MyEventBus#register(Object, String, Class)
* @author Markus
*/
public enum ThreadMode {
/**
* Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED,
/**
* On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,
/**
* Subscriber will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
*/
ASYNC
}
不足之处敬请指出,万分感谢🙏。