框架手写系列---通过反射手写EventBus框架
Posted 战国剑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了框架手写系列---通过反射手写EventBus框架相关的知识,希望对你有一定的参考价值。
一、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原理类似,功能一致。百多行代码即实现了整体的通讯总线。
以上是关于框架手写系列---通过反射手写EventBus框架的主要内容,如果未能解决你的问题,请参考以下文章
EventBus手写实现事件通信框架 ( 订阅类-订阅方法缓存集合 | 事件类型-订阅者集合 | 订阅对象-事件类型集合 )
EventBus手写实现事件通信框架 ( 实现几个关键的封装类 | 消息中心 | 订阅注解 | 订阅方法封装 | 订阅对象-方法封装 | 线程模式 )