手写EventBus

前言

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等。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值