实现一个Eventbus框架

1,084 阅读4分钟

一直觉的写个eventbus框架应该很难, 今天自己通过一些资料学习了一下, 发现实现一个简易的也不难.
先说EventBus的用法, 在一个界面注册后, 其他界面发消息, 这个注册的界面就可以收到消息并处理. 下面来一步步的实现一个Eventbus吧~

一. 看看简单用法;

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //先注册
        EventBus.getInstance().register(this)
    }

   // 通过注解来接受消息.  可以指定线程,可以执行该方法.
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun getMessage(s: String) {
        Log.d("收货线程", "" + Thread.currentThread().name)
        Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        EventBus.getInstance().unRegister(this)
    }
}

二. Evevtbus原理解析;

  • 1.首先是注册过程. 注册要把当前类的class文件传入.
  • 2.有了class文件, 可以找到那些方法被指定注解标记过.
  • 3.把这个方法(参数类型, 线程类型, 方法体) 保存在一个容器中.
  • 4.其他界面发送消息就去遍历这个容器, 找到容器中参数类型匹配的方法.
  • 5.在指定的线程中执行这个方法.

三. 手写Evevtbus框架;

一. 写一个Evevtbus 单例. 有注册, 注销方法.

public class EventBus {
    private EventBus() {
    }
    private static EventBus instance;
    public static EventBus getInstance() {
        if (instance == null) {
            instance = new EventBus();
        }
        return instance;
    }
   // 注册
    public void register(Object object) {
    }
   // 注销
    public void unRegister(Object object) {
    }
}

二. 定义线程的枚举类,

public enum ThreadMode {

    /**
     * 在主线程中回调事件
     */
    MAIN,
    /**
     * 在子线程中回调事件
     */
    BACKGROUND,
    /**
     * 在默认线程中回调事件
     */
    POSTING

}

三. 自定义注解Subscribe,

@Target(ElementType.METHOD) //注解标注在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行的时候起作用 .java --> .class --> .dex
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
}

四. 封装方法体

容器中要装入注解标注的方法. 所以封装方法体. 如下;


public class MethodManager {
    /** 方法所在的线程 */
    ThreadMode threadMode;
    /** 方法 */
    Method method;
    /** 形式参数 */
    Class<?> type;

    public MethodManager(ThreadMode threadMode, Method method, Class<?> type) {
        this.threadMode = threadMode;
        this.method = method;
        this.type = type;
    }


    public ThreadMode getThreadMode() {
        return threadMode;
    }
    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }
    public Method getMethod() {
        return method;
    }
    public void setMethod(Method method) {
        this.method = method;
    }
    public Class<?> getType() {
        return type;
    }
    public void setType(Class<Object> type) {
        this.type = type;
    }
}

五. 注册过程和发消息

一个界面注册过程j就是要通过注解找到标记的方法, 存在一个集合中, 因为有很多界面要注册, 所以用map容器存. key对应的界面, value 对应这个界面标记方法的集合. 发消息其实就是对这个集合遍历, 找到合适的方法. 然后去执行

public class EventBus {

    /**
     * 注册后, 其实是类中注解标记的方法 放入集合中
     */
    Map<Object, List<MethodManager>> map;
    ExecutorService executorService;
    Handler mhander = new Handler(Looper.getMainLooper()); //获取主线程hander

    private EventBus() {
        map = new HashMap<>();
        executorService = Executors.newCachedThreadPool();
    }

    private static EventBus instance;

    public static EventBus getInstance() {
        if (instance == null) {
            instance = new EventBus();
        }
        return instance;
    }

    /**
     * 注册
     *
     * @param object
     */
    public void register(Object object) {
        List<MethodManager> methodManagers = map.get(object);
        if (methodManagers == null) {
            findObject(object);
        }
    }

    private void findObject(Object object) {
        Class<?> aClass = object.getClass();
        Method[] declaredMethods = aClass.getDeclaredMethods();
        List<MethodManager> list = new ArrayList<>();
        for (Method declaredMethod : declaredMethods) {
            Subscribe annotation = declaredMethod.getAnnotation(Subscribe.class);
            if (annotation == null) {
                continue;
            }
            ThreadMode threadMode = annotation.threadMode(); //线程标记

            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            if (parameterTypes == null || parameterTypes.length > 1) {
                continue;
            }
            Class<?> parameterType = parameterTypes[0];
            MethodManager methodManager = new MethodManager(threadMode, declaredMethod, parameterType);
            list.add(methodManager);
        }
        map.put(object, list);
    }


  //  发送消息其实就是对集合进行遍历  
    public void post(final Object object) {
        Set<Object> objects = map.keySet();
        Iterator<Object> iterator = objects.iterator();
        while (iterator.hasNext()) {
            final Object next = iterator.next();
            List<MethodManager> list = map.get(next);
            for (final MethodManager methodManager : list) {
                //比较发送端的消息类型和接受者的消息类型是否一致
                if (methodManager.getType().isAssignableFrom(object.getClass())) {
                    ThreadMode threadMode = methodManager.getThreadMode();
                                invok(next, object, methodManager.getMethod());
                }
            }
        }
    }


    /**
     * 找到匹配的方法了, 就去执行改方法
     *
     * @param next
     * @param object
     * @param method
     */
    private void invok(Object next, Object object, Method method) {
        try {
            method.invoke(next, object);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    public void unRegister(Object object) {
        if (map.get(object) != null) {
            map.remove(object);
        }
    }
}

六. 线程切换过程

线程切换,其实就是执行方法的时候, 判断需要在哪个线程执行, 去切换一下就好了. 切换线程如下:

                if (methodManager.getType().isAssignableFrom(object.getClass())) {
                    ThreadMode threadMode = methodManager.getThreadMode();
                    if (threadMode == ThreadMode.MAIN) { //在主线程中执行方法
                        if (Looper.myLooper() == Looper.getMainLooper()) {
                            invok(next, object, methodManager.getMethod());
                        } else {
                            mhander.post(new Runnable() {
                                @Override
                                public void run() {
                                    invok(next, object, methodManager.getMethod());
                                }
                            });
                        }
                    } else if (threadMode == ThreadMode.BACKGROUND) { //在子线程中执行方法
                        if (Looper.myLooper() == Looper.getMainLooper()) {
                            executorService.execute(new Runnable() {
                                @Override
                                public void run() {
                                    invok(next, object, methodManager.getMethod());
                                }
                            });
                        } else {
                            invok(next, object, methodManager.getMethod());
                        }
                    } else if (threadMode == ThreadMode.POSTING) {
                        invok(next, object, methodManager.getMethod());
                    }
                }

七. 完成.

写到这里就完成这个简单的Eventbus框架 . 其实原理和实现其实都不复杂. 其实还有个问题就是在多进程中, 这个手写的eventbus其实是无效的, 因为单例不能夸进程. 如果想夸进程发消息, 下一次再研究.