[线程模式]
这个比较简单,直接复制ThreadMode源码即可,最后修改文件名和枚举名称。
public enum NoThreadMode {
/**
* 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
}
[自定义注解]
和EventBus类似,保留线程模式以及粘贴事件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoSubscribe {
//线程模式
NoThreadMode threadMode() default NoThreadMode.POSTING;
//粘贴性事件开关
boolean sticky() default false;
}
[方法信息的封装]
定义一个方法,用于存储方法、线程模式以及形式参数类型。
/**
* 记录方法的信息
*/
public class SubscribleMethod {
//注册方法
private Method method;
//线程模式
private NoThreadMode threadMode;
//参数类型
private Class<?> eventType;
public SubscribleMethod(Method method, NoThreadMode threadMode, Class<?> eventType) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public NoThreadMode getThreadMode() {
return threadMode;
}
public void setThreadMode(NoThreadMode threadMode) {
this.threadMode = threadMode;
}
public Class<?> getEventType() {
return eventType;
}
public void setEventType(Class<?> eventType) {
this.eventType = eventType;
}
}
[NoEventBus代码]
NoEventBus是核心代码,封装了注册、反注册、单例、发送事件、发送粘贴事件、事件处理、粘贴性事件处理、线程模式的处理
public class NoEventBus {
//存储多个被NoSubscribe修饰的方法,一个对象可对应多个方法
private Map<Object, List<SubscribleMethod>> cacheMap;
//存储粘贴事件
private Map<Object, Class<?>> stickyEvents;
private Handler handler;
//线程池
private ExecutorService newCachedThreadPool, newSingleThreadExecutor;
private NoEventBus(){
this.cacheMap = new HashMap<>();
this.stickyEvents = new ConcurrentHashMap<>();
handler = new Handler(Looper.getMainLooper());
//并发处理事件
newCachedThreadPool = Executors.newCachedThreadPool();
//并行处理事件
newSingleThreadExecutor = Executors.newSingleThreadExecutor();
}
static class NoEventBusHolder{
public static NoEventBus instance = new NoEventBus();
}
/**
* 获取NoEventBus对象,这个对象必须是单例,因为需要保证cacheMap在内存中唯一
* @return
*/
public static NoEventBus getDefault(){
return NoEventBusHolder.instance;
}
/**
* 注册:其实就是将被NoSubscribe修饰的方法对象存储到cacheMap集合中
* @param subscriber 注册时绑定的对象
*/
public void register(Object subscriber) {
List<SubscribleMethod> subscribleMethods = cacheMap.get(subscriber);
//如果已经注册,就不需要注册
if (subscribleMethods == null) {
subscribleMethods = getSubscribleMethods(subscriber);
cacheMap.put(subscriber, subscribleMethods);
//粘贴事件处理
stickyEvent(subscriber);
}
}
/**
* 获取当前对象下接收事件的方法信息的集合
* @param subscriber
* @return
*/
private List<SubscribleMethod> getSubscribleMethods(Object subscriber) {
List<SubscribleMethod> list = new ArrayList<>();
Class<?> aClass = subscriber.getClass();
while (aClass != null) {
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
NoSubscribe annotation = method.getAnnotation(NoSubscribe.class);
if (annotation == null) {
continue;
}
//校验方法参数的合法性,被NoSubscribe修饰的方法有且只有一个形式参数
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new RuntimeException("NoEventBus只能有一个形式参数!");
}
//获取线程模式
NoThreadMode NoThreadMode = annotation.threadMode();
SubscribleMethod subscribleMethod = new SubscribleMethod(method, NoThreadMode, parameterTypes[0]);
list.add(subscribleMethod);
}
aClass = aClass.getSuperclass();
}
return list;
}
/**
* 粘贴事件处理
*/
private void stickyEvent(Object subscriber){
Class<?> aClass = subscriber.getClass();
while (aClass != null) {
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
NoSubscribe annotation = method.getAnnotation(NoSubscribe.class);
if (annotation == null) {
continue;
}
//校验方法参数的合法性,被NoSubscribe修饰的方法有且只有一个形式参数
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new RuntimeException("NoEventBus只能有一个形式参数!");
}
Class<?> eventType = parameterTypes[0];
//校验粘贴性方法开关是否打开
if(annotation.sticky()){
Set<Map.Entry<Object, Class<?>>> entries = stickyEvents.entrySet();
for (Map.Entry<Object, Class<?>> entry : entries) {
Class<?> candidateEventType = entry.getValue();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getKey();
if(stickyEvent != null){
//发送事件
post(stickyEvent);
}
}
}
}
}
aClass = aClass.getSuperclass();
}
}
/**
* 反注册:其实就是将对应对象的方法集合从集合中删除
* @param subscriber
*/
public void unregister(Object subscriber) {
List<SubscribleMethod> list = cacheMap.get(subscriber);
//如果获取到
if (list != null) {
cacheMap.remove(subscriber);
}
//清除粘贴事件
//stickyEvents.clear();
}
/**
* 发送事件
* @param obj
*/
public void post(final Object obj) {
Set<Object> set = cacheMap.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
//拿到注册类
final Object next = iterator.next();
//获取类中所有添加注解的方法
List<SubscribleMethod> list = cacheMap.get(next);
for (final SubscribleMethod subscribleMethod : list) {
//判断这个方法是否应该接收事件,isAssignableFrom类似于instanceof
if (subscribleMethod.getEventType().isAssignableFrom(obj.getClass())) {
switch (subscribleMethod.getThreadMode()) {
case MAIN://接收方法在主线程种情况,事件的处理是阻塞性的,也就是按照顺序梳理
case MAIN_ORDERED://接收方法在主线程种情况,事件的处理是非阻塞性的,由于在主线程上,技术上应该无法做到让事件的处理非阻塞,所以就默认认为和MAIN一致
//如果接收方法在主线程执行的情况
if(Looper.myLooper() == Looper.getMainLooper()){
invoke(subscribleMethod, next, obj);
} else {
//post方法执行在子线程中,接收消息在主线程中
handler.post(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod, next, obj);
}
});
}
break;
case ASYNC://接收方法在子线程种情况,适合做耗时操作
//post方法执行在主线程中
if(Looper.myLooper() == Looper.getMainLooper()){
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod, next, obj);
}
});
} else {
//post方法执行在子线程中
invoke(subscribleMethod, next, obj);
}
break;
case BACKGROUND://接收方法在子线程种情况,由于消息是按顺序处理的,所以不适合做耗时操作
//post方法执行在主线程中
if(Looper.myLooper() == Looper.getMainLooper()){
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod, next, obj);
}
});
} else {
//post方法执行在子线程中
invoke(subscribleMethod, next, obj);
}
break;
case POSTING:
//默认
break;
}
}
}
}
}
/**
* 发送粘贴性事件
* @param event
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event, event.getClass());
}
post(event);
}
/**
* 通过反射,执行对应的方法
* @param subscribleMethod
* @param next
* @param obj
*/
private void invoke(SubscribleMethod subscribleMethod, Object next, Object obj) {
Method method = subscribleMethod.getMethod();
try {
method.invoke(next, obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
以上代码就是手写EventBus架构的全部代码了,可以直接使用。
另外,如果项目中使用了RxJava,那么可以自己封装一个RxBus架构,和EventBus架构原理一致。
[本章完...]
网友评论