动手来写一个EventBus吧~~~~

最近在项目拍错过程中,使用的Eventbus出现了一个问题,然后翻了一下源码,感觉理解得差不多了,然后我动手实现了一个,当然是最简单的,为此,我希望通过本篇文章能将Eventbus的原理说清楚,应该不是很难。
我实现的效果如下:

首先在首页注册我的eventbus事件:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_hx_event_bus)

        HxEventBus.getDefault().register(this)
    }
    
    @SuppressWarnings("unused")
    @HxSubscribe
    fun onEventData(data: MyEventData) {
        id_tv_content.text = "姓名为${data.name} , 年龄为${data.age}"
    }

    override fun onDestroy() {
        super.onDestroy()
        HxEventBus.getDefault().unRegister(this)
    }

然后在下一个页面,发送事件代码:

    fun sendData(view : View) {
        val data = MyEventData()
        data.name = "tom"
        data.age = 18

        HxEventBus.getDefault().post(data)
    }

大致代码和结果大家也看清楚了,不看官方EventBus源码的情况下,我们思考一下这种方式是怎么实现的,为什么我register了事件,在项目的任意位置发送事件都可以收到呢?如果这个页面退出了,为什么通过unRegister,可以取消订阅呢? 我也不卖关子了,总结一下自己所得:

  1. EventBus register是记录当前类中被@Subscribe注解修饰的方法,获取该方法中的参数、参数类型、线程调度、方法优先级统一存入静态容器;
  2. EventBus unRegister是将当前类中被@Subscribe 修饰的方法从静态容器中除去;
  3. EventBus 是发射事件 首先是在静态容器中找到和当前事件类型一致的关联方法, 然后通过 反射机制 达到执行方法的目的。

当然官方的EventBus源码中还有很多细节值得我们学习,大家可以翻翻源码。针对上面三条总结,今天我们就来实现一下自己的EventBus。
我大致的方案如下图,比较简单:)
在这里插入图片描述

HxEventBus.getDefault()

这个比较简单,获取一个单例模式,使用双null检查创建:

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


public void register(Object subscriber) {}

  1. 我们首先需要定义一个自己的注解,来标明我们需要获取哪些方法,那么就定义一个@HxSubscribe吧,我们实现的是最简单版,没有线程调度模型、也没有调用优先级,所以@HxSubscribe只是个空标识,用来记录我们需要定义的方法.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface HxSubscribe {

}
  1. 我们已经定义了我们自己的@HxSubscribe,当使用到某个方法上时,如下面:
    @HxSubscribe
    fun onEventData(data: MyEventData) {
        id_tv_content.text = "姓名为${data.name} , 年龄为${data.age}"
    }

那么我们还需要定义一个方法,用来记录我们需要的方法 onEventData, 方法中的参数类型 MyEventData,那么就定义为:

public class HxSubscriberMethod {

    /**
     * 订阅的实际方法
     */
    public Method method;

    /**
     * 订阅方法中实际参数中所指定的class类型
     */
    public Class<?> eventClass;

    public HxSubscriberMethod(Method method, Class<?> eventClass) {
        this.method = method;
        this.eventClass = eventClass;
    }
}

有了注解和自定义的HxSubscriberMethod,我们再来实现一下register():

    public void register(Object subscriber) {
        if(null == subscriber) return;
        List<HxSubscriberMethod> subscriberMethodList = methodFinder.getAllHxSubscriberMethod(subscriber);

        synchronized (this) {
            for(HxSubscriberMethod subscriberMethod : subscriberMethodList) {
                subscribe(subscriber,subscriberMethod);
            }
        }
    }

我们通过methodFinder类来寻找register的Object类中被@HxSubscribe 修饰的类,这个应该很简单,不过我也贴一下代码:

     List<HxSubscriberMethod> subscriberMethodList = new ArrayList<>();

        Method[] methods = null ;
        try {
            methods = object.getClass().getDeclaredMethods();
        }catch (SecurityException e) {
            e.printStackTrace();
        }

        if(null == methods || methods.length == 0) return subscriberMethodList;

        for(Method method : methods) {
            //we just want the public method
            Class<?>[] parameterTypes = method.getParameterTypes();
            if(parameterTypes.length == 1) {  //只获取只有一个参数的
                HxSubscribe hxSubscribe = method.getAnnotation(HxSubscribe.class);
                if(null != hxSubscribe) {
                    Class<?> eventType = parameterTypes[0];
                    subscriberMethodList.add(new HxSubscriberMethod(method,eventType));
                }
            }
        }

        return subscriberMethodList;
    }

上面的代码中,我们首先通过反射获取到所有的Method,然后查看每个MethodHxSubscribe注解,有则说明使我们需要的方法,没有的话我们就直接contiune。这里因为实现得比较简单,没有向Object的super class查找,当然这个Eventbus是实现了的,就是子类注册事件,父类中的方法也是被记录的。

所有被@HxSubsribe修饰的方法都已经被找到的,那么需要我们对每个方法正式注册一下了:

   for(HxSubscriberMethod subscriberMethod : subscriberMethodList) {
        subscribe(subscriber,subscriberMethod);
   }

我们知道,EventBus是基于Data Class传递的,只有@Subcribe方法中的DataClass类型与EventBus发射的DataClass类型一致,注册的事件才会被接受,并被执行。那我们可以想象一下,如果注册了很多DataClass一致的方法,当Event发送一个DataClass事件时,需要所有的注册方法都被执行,那么最快的方案是什么呢? 就是将这些DataClass一致的方法到到容器中,此时只需要从容器中拿到对应的方法集合即可。此时,容器登场:

    /**
     * 通过订阅类型 将订阅者分类
     */
    private Map<Class<?>,CopyOnWriteArrayList<HxSubscription>> subscriptionByEventTypeMap = new HashMap<>();

此时我们需要解释一下,CopyOnWriteArrayList是啥?CopyOnWriteArrayList是一种读写分离的List,是线程安全的,在遍历过程中,如果同时也删除数据,普通的ArrrayList会直接报ConcurrentModificationException,但是CopyOnWriteArrayList不会,它可以算得上比较高级的List了。另外,我们又多了一个HxSubscription,这个比较简单,就是封装了当前注册的对象,与HxSubscriberMethod,大致如下:

public class HxSubscription {

    /**
     * 订阅的对象
     */
    public Object subscriber;

    /**
     * 订阅的方法
     */
    public HxSubscriberMethod subscriberMethod;

    /**
     * 是否活跃
     */
    public volatile boolean active = true;

    public HxSubscription(Object subscriber, HxSubscriberMethod subscriberMethod){
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
    }

了解了CopyOnWriteArrayListHxSubscription之后,我们再来将订阅方法包装分类:

private void subscribe(Object subscriber, HxSubscriberMethod subscriberMethod) {
		//获取DataClass的Class类型,通过Class类型存储
        Class<?> eventType = subscriberMethod.eventClass;
		
		//包装订阅类
        HxSubscription hxSubscription = new HxSubscription(subscriber,subscriberMethod);

		//直接定义缓存
        CopyOnWriteArrayList<HxSubscription> subscriptions = subscriptionByEventTypeMap.get(eventType);
        if(null == subscriptions) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionByEventTypeMap.put(eventType, subscriptions);
        }else{
			//如果已经被注册过,那么直接报错
            if(subscriptions.contains(hxSubscription)) {
                throw new RuntimeException("object:" + subscriber + " has been registered");
            }
        }
		
		//添加hxSubscription类型
        subscriptions.add(hxSubscription);
}

到了这里,我们的思路估计差不多了,其实也没做多少事情,只是找到了我们的HxSubscriberMethod,并将它们根据方法参数的Class类型分类存储。

现在有个问题,我们是否该记录一下当前订阅类subscriber中多少Type类型呢?为什么需要记录呢?因为这样我们在unRegister时会更容易目标Class,remove就容易了。那么也定义一个容器吧:

    /**
     * 订阅类中class 与 订阅方法中集合 方便直接unregister
     */
    private Map<Object, List<Class<?>>> subscriberByMap = new HashMap<>();
    

subscriberByMap的key为当前Object,Value为记录的Classtype:

        List<Class<?>> subscribedEvent = subscriberByMap.get(subscriber);
        if(null == subscribedEvent) {
            subscribedEvent = new ArrayList<>();
            subscriberByMap.put(subscriber, subscribedEvent);
        }
        
        subscribedEvent.add(eventType);



public void post(Object object)

我们首先来聊一聊发送发射事件,由于没有考虑线程调度,都是默认在主线程中作用,那么代码非常简单:

    public void post(Object object) {
        Class<?> eventType = object.getClass();

        CopyOnWriteArrayList<HxSubscription> hxSubscriptions = subscriptionByEventTypeMap.get(eventType);
        if(null != hxSubscriptions) {
            for(HxSubscription subscription : hxSubscriptions) {
                //如果已经不是active状态了 则需要直接跳过
                if(!subscription.active) continue;

                try {
                    subscription.subscriberMethod.method.invoke(subscription.subscriber,object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }

            }
        }
    }

找到我们的Object类对应的ClassType,从缓存中获取到所有的HxSubscription,别忘了HxSubscription封装了订阅对象subscription.Object和订阅方法subscription.subscriberMethod,那么此时调用subscription.subscriberMethod invoke方法,使用反射将代码执行,从而完成我们需要的结果!
所以这里最重要的就是 Method.invoke() 方法了,而EventBus最核心的思想也就是使用了Method.invoke然后达到了各种匪夷所思的目的。



public void unRegister(Object subscriber)

对于反注册,我想代码应该比较简单,大概如下:

	/**
	 反注册
	*/
    public void unRegister(Object subscriber) {
        // 找到当前ClassType类型
        List<Class<?>> subscribedEvent = subscriberByMap.get(subscriber);

        if(null != subscribedEvent) {
            for(Class<?> clazz : subscribedEvent) {
                unRegisterSubscriberByType(clazz, subscriber);
            }

            subscriberByMap.remove(subscriber);
        }else{
            Log.w("","subscriber:" + subscriber + " has not been registered.");
        }
    }

    /**
     * 通过class名找到注册subscriber
     * @param clazz
     * @param subscriber
     */
    private void unRegisterSubscriberByType(Class<?> clazz, Object subscriber) {
        CopyOnWriteArrayList<HxSubscription> hxSubscriptions = subscriptionByEventTypeMap.get(clazz);
        if(hxSubscriptions != null ) {
            int length = hxSubscriptions.size();
            for (int i = 0; i < length; i++) {
                HxSubscription hxSubscription = hxSubscriptions.get(i);

                if(hxSubscription.subscriber == subscriber) {
                    hxSubscription.active = false;
                    hxSubscriptions.remove(i);
                    i--;
                    length--;
                }
            }
        }
    }

思路大概如此,通读了一下EventBus的源码,然后通过自己的理解,写下这个简单实现版,也算是对EventBus的一种感悟吧。
源码为:
https://github.com/Microhx/studyCodes/tree/master/hxeventbus

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值