前言
EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发,Gihub地址是:EventBus。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。
像这样的框架如何进行实现呢,我们就手写一个。
工程目录结构
一,定义枚举类
枚举类,用于线程切换。
public enum ThreadMode {
MAIN,BACKGROUND
}
二,注解类
注解@Target:用于描述注解的使用范围,ElementType.METHOD,用于描述方法。
注解@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。RetentionPolicy.RUNTIME,代表在运行时有效。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.MAIN;
}
三,实体类
用于存放方法和类
public class SubscribeMethod {
//回调方法
private Method mMethod;
//线程模式
private ThreadMode mThreadMode;
//方法中的参数
private Class<?> type;
public SubscribeMethod(Method mMethod, ThreadMode mThreadMode, Class<?> type) {
this.mMethod = mMethod;
this.mThreadMode = mThreadMode;
this.type = type;
}
public Method getmMethod() {
return mMethod;
}
public void setmMethod(Method mMethod) {
this.mMethod = mMethod;
}
public ThreadMode getmThreadMode() {
return mThreadMode;
}
public void setmThreadMode(ThreadMode mThreadMode) {
this.mThreadMode = mThreadMode;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
}
发送消息实体类
public class EventBean {
private int state;
private String message;
public EventBean(int state, String message) {
this.state = state;
this.message = message;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "EventBean{" +
"state=" + state +
", message='" + message + '\'' +
'}';
}
}
四,消息发送和接收实现类
public class EventBus {
private Map<Object, List<SubscribeMethod>> cacheMap;
private Handler mHander;
private static volatile EventBus instance;
private EventBus() {
cacheMap = new HashMap<>();
mHander=new Handler();
}
public static EventBus getDefault() {
if (instance == null) {
synchronized (EventBus.class) {
if (instance == null) {
instance = new EventBus();
}
}
}
return instance;
}
public void register(Object object) {
List<SubscribeMethod> list = cacheMap.get(object);
if (list == null) {
list = findSubscribeMethods(object);
cacheMap.put(object, list);
}
}
private List<SubscribeMethod> findSubscribeMethods(Object object) {
List<SubscribeMethod> list = new ArrayList<>();
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
while (clazz != null) {
//找父类的时候,需要先判断是否是系统级别父类
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.")
|| name.startsWith("android.")) {
break;
}
for (Method method : methods) {
//找到带有Subcribe注解的方法
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe == null) {
continue;
}
Class<?>[] types = method.getParameterTypes();
if (types.length != 1) {
Log.e("EventBus", "findSubscribeMethods: 错误");
}
ThreadMode threadMode = subscribe.threadMode();
SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, types[0]);
list.add(subscribeMethod);
}
clazz = clazz.getSuperclass();
}
return list;
}
public void post(final Object type) {
//直接循环map里的方法
Set<Object> set = cacheMap.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
final Object obj = iterator.next();
List<SubscribeMethod> list = cacheMap.get(obj);
for (final SubscribeMethod subscribeMethod : list) {
if (subscribeMethod.getType().isAssignableFrom(type.getClass())) {
switch (subscribeMethod.getmThreadMode()) {
case MAIN:
if (Looper.myLooper() == Looper.getMainLooper()) {
invoke(subscribeMethod, obj, type);
}else{
mHander.post(new Runnable() {
@Override
public void run() {
invoke(subscribeMethod, obj, type);
}
});
}
break;
case BACKGROUND:
break;
}
}
}
}
}
private void invoke(SubscribeMethod subscribeMethod, Object obj, Object type) {
Method method = subscribeMethod.getmMethod();
try {
method.invoke(obj, type);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
五,使用方法
在MainActivity 中注册并接收消息
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册
EventBus.getDefault().register(this);
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessage(EventBean bean) {
Log.i("MainActivity", "getMessage: bean=" + bean.toString());
}
}
在SecondActivity 中发送消息
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity);
findViewById(R.id.second_tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new EventBean(1001,"你好"));
}
});
}
}
很简单的实现方式,当然在实际开发中还需要更多的代码,去完善EventBus。这里只说明了,用最简单的方式去实现一个EventBus,核心思想就是注解和反射,很好理解。
六,EventBus 实现原理
https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650264045&idx=1&sn=08ec3b416144a435fb557d51007c137c&chksm=88632082bf14a994b3bb10b63185c32b39e855e40103434a52fe6066254e96ae7718c29eccd4&scene=27
EventBus 是一种事件发布/订阅框架,它实现了发布/订阅模式,提供了一种异步的、松散耦合的消息传递机制。
EventBus 的主要实现机制还是基于反射和编译时注解(APT),一句话概括就是:
- 注册时传入目标类对象,然后利用反射筛选出 @Subscribe 的方法,然后以相同的参数类型为 key,将不同的方法合并为 list 作为
value,得到一个 map 集合。 - 当用户 post 数据时,再以数据类型为 key,从 map 中取出对应的方法 list,然后遍历
list,再利用反射机制对指定的方法执行 invoke 操作,完成调用。
EventBus 如何切回主线程的:
EventBus在初始化的时候会初始化一个MainThreadSupport对象,它会去获取主线程的Looper对象并存起来。这个mainThreadPoster其实是Handler的子类,它利用Handler的消息机制切换线程。
APT(Annotation Processing Tool)
是一种编译时注解处理工具,它可以在编译 Java 代码时扫描指定的注解,并根据注解生成相应的代码。APT 可以帮助开发者在编译时自动生成代码,减少重复劳动,提高代码的复用性和可维护性。
APT 的工作原理如下:
- 在编译 Java 代码时,APT 会扫描源代码文件,并检查源代码中的注解。
- 当 APT 检测到注解时,它会根据注解的定义生成相应的代码,并将生成的代码保存到指定的输出目录中。
- 编译器会将生成的代码编译成字节码文件,与源代码一起打包成一个 JAR 文件或类文件。
- 在运行时,应用程序可以使用生成的代码,实现相应的功能。
APT 可以帮助开发者自动生成很多有用的代码,例如自动生成代码检查器、数据库映射器、事件监听器等。APT 还可以与其他框架和工具结合使用,例如与 Dagger、ButterKnife 等依赖注入框架结合使用,可以大大简化代码的编写和维护工作。
七,JAVA注解原理
Java注解是一种用于提供元数据的Java语言特性。它们允许在Java代码中添加额外的信息,这些信息可以用于编译时检查、运行时处理或者生成代码。注解可以应用于类、方法、字段和其他程序元素。
Java注解的原理可以通过以下步骤来理解:
1.定义注解:使用@interface关键字定义一个注解。注解本质上是一个接口,其中的方法称为成员。成员可以有默认值,也可以没有。
public @interface MyAnnotation {
String value() default "default value";
int count() default 0;
}
2.注解的使用:在代码中使用注解,可以用于修饰类、方法、字段等。可以为注解的成员赋值。
@MyAnnotation(value = "Hello", count = 5)
public class MyClass {
@MyAnnotation(count = 2)
private int myField;
@MyAnnotation
public void myMethod() {
// 方法体
}
}
3.元数据的获取:使用Java的反射机制可以在运行时获取注解的信息。可以获取注解类型、成员值等。
Class<?> clazz = MyClass.class;
MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(classAnnotation.value()); // 输出:Hello
Field field = clazz.getDeclaredField("myField");
MyAnnotation fieldAnnotation = field.getAnnotation(MyAnnotation.class);
System.out.println(fieldAnnotation.count()); // 输出:2
Method method = clazz.getDeclaredMethod("myMethod");
MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
System.out.println(methodAnnotation.value()); // 输出:default value
编译时注解和运行时注解是在使用时机和处理方式上有所区别的。
编译时注解(Compile-time Annotations):
- 定义:编译时注解是在编译阶段处理的注解,它们在源代码被编译成字节码时被读取和处理。
- 使用:编译时注解通常用于在编译过程中进行静态检查、生成额外的代码或者生成资源文件等。
- 处理方式:编译时注解的处理是由编译器或自定义的注解处理器负责的,它们会在编译过程中扫描源代码中的注解并进行相应的处理。
- 示例:常见的编译时注解包括@Override、@Deprecated、@SuppressWarnings等。
运行时注解(Runtime Annotations):
- 定义:运行时注解是在程序运行时通过反射机制读取和处理的注解。
- 使用:运行时注解通常用于在运行时进行动态的配置、处理或逻辑判断。
- 处理方式:运行时注解的处理是通过反射机制来获取注解信息,并根据注解信息进行相应的操作和逻辑判断。
- 示例:常见的运行时注解包括依赖注入框架中的@Autowired、持久化框架中的@Entity、Web框架中的@RequestMapping等。