EventBus相信大多数人都用过,其具有方便灵活、解耦性强、体积小、简单易用等优点,虽然现在也有很多优秀的替代方案如RxBus、LiveDataBus等,但不可否认EventBus开创了消息总线时代,有很多优秀的思想可以供我们来借鉴学习。下面就让我们来撸一个超简单的EventBus,领略其实现方式的主要思想。
先看一下GitHub中官方用法介绍:
1.定义消息实体类
public static class MessageEvent { /* Additional fields if needed */ }
2.准备订阅者:声明并注解订阅方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
注册和注销订阅者
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3.发送事件
EventBus.getDefault().post(new MessageEvent());
EventBus通过register方法,查找注册对象中有Subscribe注解的方法,并把这些方法维护在EventBus自己的Map中,在某处调用post()方法发送消息时,通过消息载体Bean类,在Map中查找订阅了接收该Bean类的消息接收方法,调用该方法,达到消息传递的目的。
1.定义线程枚举类
public enum ThreadMode {
MAIN,
BACKGROUND
}
2.定义注解,注解目标是函数,运行时注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.MAIN;
}
3.定义方法封装类,用来描述使用了subscribe注解的消息接收方法,将维护在EventBus类中。
public class SubscribeMethod {
/**
* 方法本身
*/
private Method method;
/**
* 线程模式
*/
private ThreadMode threadMode;
/**
* 参数类型
*/
private Class<?> type;
public SubscribeMethod(Method method, ThreadMode threadMode, Class<?> type) {
this.method = method;
this.threadMode = threadMode;
this.type = type;
}
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<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
}
private static volatile EventBus instance;
private Map<Object, List<SubscribeMethod>> cacheMap;
private EventBus() {
cacheMap = new HashMap<>();
}
public static EventBus getDefault() {
if (instance == null) {
synchronized (EventBus.class) {
if (instance == null) {
instance = new EventBus();
}
}
}
return instance;
}
定义注册方法,在该方法中,查找订阅的方法,维护在map中,该map的key是注册消息的对象,如Activity、Fragment等
public void register(Object obj) {
//找到消息监听方法,如onMessageEvent,获取信息,统一管理
List<SubscribeMethod> list = cacheMap.get(obj);
if (list == null) {
list = findSubscribeMethods(obj);
cacheMap.put(obj, list);
}
}
其中findSubscribeMethods(obj)
方法是用来查找对象中订阅方法,下面是其实现
/**
* 查找定义的消息接收方法
*
* @param obj 查找对象
* @return
*/
private List<SubscribeMethod> findSubscribeMethods(Object obj) {
List<SubscribeMethod> list = new ArrayList<>();
Class<?> clazz = obj.getClass();
//寻找自身及父类subscribe注解的方法
while (clazz != null) {
//如果是系统的类,过滤掉
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
break;
}
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//找到带有subscribe注解的方法
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe == null) {
continue;
}
Class<?>[] parameterTypes = method.getParameterTypes();
//这里仅考虑只有一个参数的情况
if (parameterTypes.length != 1) {
throw new IllegalArgumentException("消息监听方法有且只能有一个参数!");
}
ThreadMode threadMode = subscribe.threadMode();
SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, parameterTypes[0]);
list.add(subscribeMethod);
}
clazz = clazz.getSuperclass();
}
return list;
}
定义消息发送方法
/**
* 发送消息
*
* @param type 消息实体
*/
public void post(Object type) {
//循环cacheMap,找到对应的方法进行回调
Set<Object> set = cacheMap.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
List<SubscribeMethod> list = cacheMap.get(obj);
for (SubscribeMethod subscribeMethod : list) {
//匹配发送的消息bean和接收的消息bean是否相同
if (subscribeMethod.getType().isAssignableFrom(type.getClass())) {
invoke(subscribeMethod, obj, type);
}
}
}
}
其中invoke(subscribeMethod, obj, type)
反射调用回调的方法,实现如下
/**
* 反射调用回调方法
*
* @param subscribeMethod 方法对象
* @param obj 被反射对象
* @param type 方法参数
*/
private void invoke(SubscribeMethod subscribeMethod, Object obj, Object type) {
Method method = subscribeMethod.getMethod();
try {
method.invoke(obj, type);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
OK!这么一个最简单的EventBus就写完了,现在让我们来测试一下吧
我们打算在MainActivity
中订阅,在SecondActivity
中发送消息来测试
首先定义一个消息实体类
public class MessageBean {
private String param1;
private String param2;
public MessageBean(String param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
public String getParam2() {
return param2;
}
public void setParam2(String param2) {
this.param2 = param2;
}
@Override
public String toString() {
return "MessageBean{" +
"param1='" + param1 + '\'' +
", param2='" + param2 + '\'' +
'}';
}
}
在MainActivity
中注册订阅
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Main";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
startActivity(new Intent(this, SecondActivity.class));
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageBean event) {
if (event != null) {
Log.e(TAG, event.toString());
}
}
}
收到消息后会打印出消息内容
最后在SecondActivity
中发送消息
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.tv_post).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageBean("SecondActivity","消息内容"));
}
});
}
}
运行后,点击SecondActivity
中的发送按钮后,打印log如下:
com.cwq.eventbusdemo E/Main: MessageBean{param1='SecondActivity', param2='消息内容'}
大功告成!
当然了,跟正式的EventBus相比,我们都很多东西没有处理,比如注销、非主线程的消息处理、粘性消息、甚至新版的进程间通讯等等,这里仅仅实现最简单的功能,让大家清晰明白EventBus的工作方式,更多细节还要研习源码来学习,一个优秀的框架就是一个学习宝藏,都值得我们去借鉴和学习。