EventBus(二)使用反射的方式的原理

系列文章

EventBus(一) 简单使用,不使用反射,使用注解处理器
EventBus(二)使用反射的方式的原理
EventBus(三)手写EventBus3.1.1

提要

按照官网的配置方式,默认就是走的反射的方式实现的,所有有必要将使用反射实现的方式详细学习一下。

思路

注册的时候,将注册的对象object传入,通过反射获取到此对象object所在Class的所有方法, 遍历所有方法,找到@Subscribe注解的方法,经过判断保护,然后将每个有@Subscribe注解的方法分别封装成一个JavaBean对象,此JavaBean对象的字段包括方法的参数类型,线程模式,订阅的方法等信息。然后将注册的对象作为key,List作为值缓存到map中。

发送时间的时候, 将遍历缓存map对象,找到方法参数类型与发送方一致的多个方法,然后判断线程模式,根据不同的线程模式,做不同的逻辑判断,但是最后都是通过反射mathod.invoke方法,来调用注册方的方法。

逻辑思路就是这样,下面是撸码。

线程状态枚举类

public enum ThreadMode {
//事件的处理在和事件的发送在相同的进程
POSTING,
//事件的处理会在UI线程中执行
MAIN ,
//后台进程,处理如保存到数据库等操作
BACKGROUND ,
//异步执行,另起线程操作。事件处理会在单独的线程中执行:主要用于在后台线程中执行耗时操作
ASYNC

}

用于@Subscribe注解中的处理线程
默认为POSTING
主线程发布-主线程接收,子线程发布-子线程接收

订阅方法封装类(JavaBean)


//订阅方法(注解方法)的参数类型
private Class<?> type;
//订阅方法(注解方法)的线程模式
private ThreadMode th readMode ;
//订阅方法(注解方法)
private Method method;

提供构造方法
提供getter / setter方法

收集所有订阅方法

//获取类
Class<?> clazz = getter . getClass();
//获取所有方法
Method[] methods = clazz. getMethods();
//循环方法
for (Method method : methods) {
//获取方法的注解
Subscribe subscribe = method. getAnnotation ( Subscribe.class) ;
//获取订阅方法参数
Class<?>[] parameterTypes = method. getParameterTypes();
//完全符合要求、规范的方法,保存到方法对象中MethodManager (3个重要成员: 方法、参数、线程)
MethodManager manager = new MethodManager (pa rameterTypes[0], subscribe. threadMode(),method);
}


保存到Map<Object, List> cacheMap缓存中

发布事件

//订阅者已经登记,从登记表中找出
Set<0bject> set = cacheMap. keySet();
//比如获取MainActivity对象
for (final 0bject getter : set) {	
	//获取MainAc tivity中所有注解的方法
	List<MethodManager> methodL ist = cacheMap . get (getter);
	if (methodList != null) {

	//循环每个方法
	for (final MethodManager method : methodL ist) {
		//通过EventBean来判断是否匹配上
		if (method . getType() . isAssignableF rom( setter . getClass())) {
		//线程调度
		switch ( method . getThreadMode()) {
			case POSTING: 
			//...
			break;
			case MAIN:
			break;
			case BACKGROUND :
			break;
		}
}

总控制类


public class EventBus {

    // volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存
    private static volatile EventBus instance;
    // 用来保存这些带注解的方法(订阅者的回调方法)
    private Map<Object, List<MethodManager>> cacheMap;

    private Handler handler;
    private ExecutorService executorService;

    private EventBus() {
        cacheMap = new HashMap<>();

        // Handler高级用法:将handler放在主线程使用
        handler = new Handler(Looper.getMainLooper());
        // 创建一个子线程(缓存线程池)
        executorService = Executors.newCachedThreadPool();
    }

    public static EventBus getDefault() {
        if (instance == null) {
            synchronized (EventBus.class) {
                if (instance == null) {
                    instance = new EventBus();
                }
            }
        }
        return instance;
    }

    // 找到MainActivity所有带符合注解的方法
    public void register(Object getter) {
        // 获取MainActivity所有的方法
        List<MethodManager> methodList = cacheMap.get(getter);
        if (methodList == null) { // 不为空表示以前注册完成
            methodList = findAnnotationMethod(getter);
            cacheMap.put(getter, methodList);
        }
    }

    // 获取MainActivity中所有注解的方法
    private List<MethodManager> findAnnotationMethod(Object getter) {
        List<MethodManager> methodList = new ArrayList<>();
        // 获取类
        Class<?> clazz = getter.getClass();
        // 获取所有方法
        Method[] methods = clazz.getMethods();

        // 性能优化。N个父类不可能有自定义注解。排除后再反射
        while (clazz != null) {
            // 找出系统类,直接跳出,不添加cacheMap(因为不是订阅者)
            String clazzName = clazz.getName();
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.")
                    || clazzName.startsWith("android.")) {
                break;
            }

            // 循环方法
            for (Method method : methods) {
                // 获取方法的注解
                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                // 判断注解不为空,切记不能抛异常
                if (subscribe == null) {
                    continue;
                }
                // 严格控制方法格式和规范
                // 方法必须是返回void(一次匹配)
                Type returnType = method.getGenericReturnType();
                if (!"void".equals(returnType.toString())) {
                    throw new RuntimeException(method.getName() + "方法返回必须是void");
                }
                // 方法参数必须有值(二次匹配)
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 1) {
                    throw new RuntimeException(method.getName() + "方法有且只有一个参数");
                }

                // 完全符合要求、规范的方法,保存到方法对象中MethodManager(3个重要成员:方法、参数、线程)
                MethodManager manager = new MethodManager(parameterTypes[0], subscribe.threadMode(), method);
                methodList.add(manager);
            }

            // 不断循环找出父类含有订阅者(注解方法)的类。直到为空,比如AppCompatActivity没有吧
            clazz = clazz.getSuperclass();
        }
        return methodList;
    }

    // SecondActivity发送消息
    public void post(final Object setter) {
        // 订阅者已经登记,从登记表中找出
        Set<Object> set = cacheMap.keySet();
        // 比如获取MainActivity对象
        for (final Object getter : set) {
            // 获取MainActivity中所有注解的方法
            List<MethodManager> methodList = cacheMap.get(getter);
            if (methodList != null) {
                // 循环每个方法
                for (final MethodManager method : methodList) {
                    // 有可能多个方法的参数一样,从而都同时收到发送的消息
                    // 通过EventBean来判断是否匹配上
                    if (method.getType().isAssignableFrom(setter.getClass())) {
                        // 通过方法的类型匹配,从SecondActivity发送的EventBean对象(参数)
                        // 匹配MainActivity中所有注解的方法符合要求的,都发送消息

                        // class1.isAssignableFrom(class2) 判定此 Class 对象所表示的类或接口
                        // 与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口

                        // 线程调度
                        switch (method.getThreadMode()) {
                            case POSTING:
                                invoke(method, getter, setter);
                                break;

                            case MAIN:
                                // 先判断发送方是否在主线程
                                if (Looper.myLooper() == Looper.getMainLooper()) {
                                    invoke(method, getter, setter);
                                } else { // 子线程 - 主线程,切换线程(用到Handler)
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(method, getter, setter);
                                        }
                                    });
                                }
                                break;

                            case BACKGROUND:
                                // 先判断发送方是否在主线程
                                if (Looper.myLooper() == Looper.getMainLooper()) {
                                    // 主线程 - 子线程,创建一个子线程(缓存线程池)
                                    executorService.execute(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(method, getter, setter);
                                        }
                                    });
                                } else { // 子线程 到 子线程,不用切换线程
                                    invoke(method, getter, setter);
                                }
                                break;
                        }
                    }
                }
            }
        }
    }

    // 找到匹配方法后,通过反射调用MainActivity中所有符合要求的方法
    private void invoke(MethodManager method, Object getter, Object setter) {
        Method execute = method.getMethod();
        try {
            execute.invoke(getter, setter);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EventBus是一种发布-订阅模式的事件总线框架,可以让不同组件之间进行解耦,使得它们能够在不同的线程中进行通信,从而避免了很多复杂的依赖关系。以下是EventBus使用方法: 1. 集成EventBus库 在项目中引入EventBus库,可以通过Gradle或Maven进行集成。 2. 定义事件类 定义事件类,用于传递数据。 ```java public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } } ``` 3. 注册/注销事件 在需要接收事件的组件中注册/注销事件。 ```java @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { EventBus.getDefault().unregister(this); super.onStop(); } ``` 4. 发布事件 在需要发布事件的地方发送事件。 ```java EventBus.getDefault().post(new MessageEvent("Hello EventBus!")); ``` 5. 接收事件 在事件订阅者中,使用@Subscribe注解来接收事件。 ```java @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { // do something } ``` 其中,@Subscribe注解的参数有以下几种: - threadMode:事件的线程模式。可选值为ThreadMode.MAIN(主线程)、ThreadMode.BACKGROUND(后台线程)、ThreadMode.ASYNC(异步线程)。 - priority:事件的优先级。值越大,优先级越高。 - sticky:是否为粘性事件。粘性事件会在订阅之前发送,订阅者可以在订阅之后收到这个事件。 6. 粘性事件 粘性事件是一种特殊的事件,它会在订阅者注册之前发送,并且订阅者注册后仍然可以收到这个事件,直到订阅者处理它为止。 ```java EventBus.getDefault().postSticky(new MessageEvent("Hello EventBus!")); ``` 订阅时,需要使用@Subscribe注解的sticky参数来接收粘性事件。 ```java @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) public void onMessageEvent(MessageEvent event) { // do something } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值