EventBus这么强大,那么来手写一个

EventBus

今天突然想起了EventBus,做Android的肯定或多或少知道点这东西,EventBus是一个非常强大的事件发布/订阅轻量级框架,我们可以通过他在Activity、Fragment之间进行通信,或者其他组件中,他的API也及其简单,使用也方便,能使得代码更简洁,那么既然EventBus很强大,今天就来手写一个具有基本功能的“EventBus”。

public class TestActivity extends AppCompatActivity {
	protected void onCreate(@Nullable Bundle savedInstanceState) {
	   super.onCreate(savedInstanceState);
        DataBindingUtil.setContentView(this, R.layout.activity_test);
		EventBus.getDefault().register(this);
		EventBus.getDefault().post("msg");
	}


	@Subscribe
	public void  onMsg(String msg){
	    Log.i(TAG, "onMsg: "+msg);
	}
}

原理及实现

没有看过EventBus的源码,所以下面是按照自己思路来写,里面需要用到反射知识,在register的时候,获取这个类下所有带有Subscribe注解的方法,并把他放入集合中,当post发布事件的时候,获取参数类型,并且在现有方法集合查找,如果方法的参数和post时是一模一样的,那么通过invoke去调用,当然为了扩展,需要处理线程的逻辑。

在里面加入了延迟消息,可以在X秒后发布事件,这部分是通过DelayQueue队列来完成。

延迟消息信息:

public class DelayedMsg implements Delayed {
    private Object[] param;
    private long targetTimer;

    public DelayedMsg( long targetTimer,Object ... param) {
        this.param = param;
        this.targetTimer = targetTimer;
    }
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(targetTimer - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return o.getDelay(TimeUnit.MILLISECONDS) - this.getDelay(TimeUnit.MILLISECONDS) > 0 ? -1 : 1;
    }

    public Object[] getParam() {
        return param;
    }
}

订阅注解,可以指明调用时的线程。

@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
    ThreadMode thread() default ThreadMode.MAIN;
}
public enum  ThreadMode {
    MAIN,BACKGROUND;
}

EventBus实现:

package com.example.myapplication.event;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.example.myapplication.Subscribe;
import com.example.myapplication.ThreadMode;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

public class EventBus {
    private int mThreadCount = 0;

    private DelayQueue<DelayedMsg> mDelayedMsgQueue;
    private Thread mDelayedMsgHandlerThread;
    private Handler mHandler;
    private ConcurrentHashMap<Object, List<Method>> mSubscribeMethodMap;

    private volatile static EventBus mDefaultInstance;

    private boolean mExitThread ;

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

    public EventBus() {
        mSubscribeMethodMap = new ConcurrentHashMap<>();
        mHandler= new Handler(Looper.myLooper());
        mDelayedMsgQueue=new DelayQueue<>();
    }

    private  void startDelayedMsgHandlerThread(){
        mExitThread=false;
        mDelayedMsgHandlerThread =new Thread(new Runnable() {
            @Override
            public void run() {
                while (!mExitThread){
                    try {
                        DelayedMsg take = mDelayedMsgQueue.poll(3, TimeUnit.SECONDS);
                        if (take!=null)
                        postMessage(take.getParam());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        mDelayedMsgHandlerThread.start();
    }
    /**
     * 注册
     *
     * @param obj
     */
    public void register(Object obj) {
        if (mSubscribeMethodMap.size()==0){
            startDelayedMsgHandlerThread();
        }
        Method[] declaredMethods = obj.getClass().getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (method.getAnnotation(Subscribe.class) != null) {
                setList(obj);
                mSubscribeMethodMap.get(obj).add(method);
            }
        }

    }

    /**
     * 删除
     * @param obj
     */
    public void unRegister(Object obj){
        mSubscribeMethodMap.remove(obj);
        if (mSubscribeMethodMap.size()==0){
            mExitThread=true;
        }
    }
    private void setList(Object obj) {
        if (mSubscribeMethodMap.get(obj) == null) {
            mSubscribeMethodMap.put(obj, new ArrayList<>());
        }
    }

    public void postMessage(Object... msg) {
        List<Class> msgParamClass = Arrays.stream(msg).map((Function<Object, Class>) o ->
                o.getClass()).collect(Collectors.toList());

        Iterator<Object> iterator = mSubscribeMethodMap.keySet().iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            List<Method> methods = mSubscribeMethodMap.get(next);
            for (Method method : methods) {
                if (equalsParameterType(method.getParameterTypes(), msgParamClass.toArray(new Class[msgParamClass.size()]))) {
                    method.setAccessible(true);
                    if (method.getAnnotation(Subscribe.class).thread() == ThreadMode.BACKGROUND) {
                        invokeMethodForBackground(next, method, msg);
                        continue;
                    }
                    invokeMethodForMain(next, method, msg);

                }
            }

        }
    }


    public void postMessageDelayed(int delayedSecond , Object... msg) {
        if (delayedSecond==0){
            postMessage(msg);
            return;
        }
        mDelayedMsgQueue.offer(new DelayedMsg(System.currentTimeMillis()+(1000*delayedSecond),msg));
    }
    private void invokeMethodForMain(Object obj, Method method, Object... param) {
        try {
            if (Thread.currentThread().getName().equals("main")){
                method.invoke(obj, param);
                return;
            }
            mHandler.post(() -> invokeMethodForMain(obj, method, param));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private void invokeMethodForBackground(Object obj, Method method, Object... param) {
        mThreadCount++;
        new Thread(() -> {
            try {
                method.invoke(obj, param);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            mThreadCount--;
        }, getThreadName()).start();
    }

    private String getThreadName() {
        return "eventbus-" + mThreadCount;
    }

    private boolean equalsParameterType(Class<?>[] clz1, Class<?>[] clz2) {
        if (clz1.length != clz2.length) {
            return false;
        }
        for (int i = 0; i < clz1.length; i++) {
            if (!clz1[i].getName().equals(clz2[i].getName())) {
                return false;
            }
        }
        return true;
    }
}


测试效果


package com.example.myapplication;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.example.myapplication.databinding.ActivityMainBinding;
import com.example.myapplication.event.EventBus;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding mActivityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        EventBus.getInstance().register(this);


    }

    public void postInt(View view) {
        EventBus.getInstance().postMessage(1);
    }

    public void postString(View view) {
        EventBus.getInstance().postMessage("String Msg");
    }

    public void postObject(View view) {
        EventBus.getInstance().postMessage(new Msg("1"));
    }

    public void postMultiParamMsg(View view){
        EventBus.getInstance().postMessage(new Msg("听风逝夜"),"String msg");
    }
    public void postDelayMsg(View view){
        EventBus.getInstance().postMessageDelayed(2,"听风逝夜");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getInstance().unRegister(this);
    }


    @Subscribe(thread = ThreadMode.MAIN)
    public void onPostMultiMsg(Msg msg ,String str) {
        Log.i("TAG", "onPostDelayed: " + msg.getName()+ "  "+"  "+str + Thread.currentThread().getName());
    }
    @Subscribe(thread = ThreadMode.BACKGROUND)
    public void onIntMsg(Integer msg) {
        Log.i("TAG", "onIntMsg: " + msg + "  " + Thread.currentThread().getName());
    }

    @Subscribe
    public void onStringMsg(String msg) {
        Log.i("TAG", "onStringMsg: " + msg);
    }

    @Subscribe
    public void onMsg(Msg msg) {
        Log.i("TAG", "onMsg: " + msg.getName());
    }

    static class Msg {
        private String name;

        public Msg(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}

基本测试:

在这里插入图片描述

延迟&多参数测试:

在这里插入图片描述

当然还有很多细节没处理,比如类型判断,如果订阅时候是List类型,那么发布使用ArrayList则接收不到,另外基本类型需要使用包装类去接收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值