手写简易版EventBus实现方案-源码学习1

1.Subscribe注解,这里没什么好解释的,添加一个Subscribe注解,在接受Event的方法上使用。

/**
 * @Target 该注解的使用范围,当前为方法注解
 * @Retention 该注解作用在运行时状态
 * 注解内容为threadMode
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
    //默认的threadMode为POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
}

2.MEventBus类,该类作为组件间作为消息事件传递的中间方,具体使用方法见注释。

public class MEventBus {
    private static final String TAG = "MEventBus";
    /**
     *  该类为单例,因为要保证全局只有一个MEventBus实例
     */
    private static MEventBus INSTANCE;
    /**
     *  调用register的对象的所属类中所有订阅方法的缓存
     */
    private final Map<Object, List<MethodManger>> CACHES;
    /**
     *  线程池
     */
    private final ExecutorService executorService;

    MEventBus(){
        CACHES = new HashMap<>();
        executorService = Executors.newCachedThreadPool();
    }

    /**
     *  获取单例,没啥好解释的
     */
    public static MEventBus getDefault(){
        if (INSTANCE == null){
            synchronized (MEventBus.class){
                if (INSTANCE == null){
                    INSTANCE = new MEventBus();
                }
            }
        }
        return INSTANCE;
    }


    /**
     *  注册
     */
    public void register(Object object){
        //空指针判断
        AssertNotNullCheck(object, "object is null");
        //1.首先获取缓存中的订阅方法集合
        List<MethodManger> methodMangers = CACHES.get(object);
        //如果步骤1中返回的集合为空,则调用findMethod方法去解析object对象所属的类中所有的订阅方法
        //解析结束后,将结果缓存
        if (methodMangers == null || methodMangers.size() == 0){
            methodMangers = findMethod(object);
            CACHES.put(object, methodMangers);
        }
    }

    /**
     *  遍历Method,封装,存入CACHES
     */
    private List<MethodManger> findMethod(Object object) {
        List<MethodManger> list = new ArrayList<>();
        //1.获取object对象所属的类中所有的方法(不包括父类)
        Method[] methods = object.getClass().getDeclaredMethods();
        //2.对所有方法集合进行遍历,获取订阅方法
        for (Method method : methods){
            //因为只有添加@Subscribe注解的方法才是订阅方法
            //3.所以获取方法的Subscribe注解
            Subscribe subscribe = method.getAnnotation(Subscribe.class);
            //4.若未添加@Subscribe注解,则循环下一个
            if (subscribe == null){
                continue;
            }
            //5.如果获取到了订阅方法,则获取注解的threadMode信息
            ThreadMode threadMode = subscribe.threadMode();

            //6.获取订阅方法的参数类型
            Class<?>[] type =  method.getParameterTypes();
            //7.如果方法参数为空或者参数大于1个,则不满足订阅方法的要求,开始循环下一个
            if (type == null || (type != null && type.length > 1)){
                continue;
            }

            //8.解析好的方法信息(method),@Subscribe注解的线程模式(threadMode)和参数类型(type)封装成一个MethodManager对象,该对象的数据结构后面为大家奉上
            MethodManger methodManger = new MethodManger(method, threadMode, type[0]);
            //9.将获取到的订阅方法信息加入list
            list.add(methodManger);
        }
        //10.返回list
        return list;

    }

    /**
     *  反注册订阅类
     */
    public void unregister(Object object){
        //1.如果该对象所属的类的订阅方法缓存不为空,则remove清空
        if (CACHES.get(object) != null){
            CACHES.remove(object);
        }
    }

    /**
     *  post事件
     */
    public void post(final Object postEvent){
        Log.i(TAG, "post run current thread name is " + Thread.currentThread().getName());
        //1.遍历CACHES缓存
        Iterator<Object> iterable = CACHES.keySet().iterator();
        while (iterable.hasNext()){
            //2.如果缓存不为空,则获取key,这里的key即为调用register方法中的入参object
            final Object keyObj = iterable.next();
            //3.获取2中所述的object对应的订阅方法集合
            List<MethodManger> methodMangerList = CACHES.get(keyObj);
            //4.遍历该集合
            for (MethodManger methodManger : methodMangerList){
                //5.判断订阅方法的入参类型是否和post方法的入参类型一致,如果一致,则进入判断
                if (methodManger.getType() == postEvent.getClass()){
                    //6.获取满足条件5的订阅方法,取出对应的信息
                    final Method method = methodManger.getMethod();
                    //7.获取threadMode,判断线程模式,当前的线程是调用post方法时所在的线程
                    switch (methodManger.getThreadMode()){
                            //8-1 如果是POSTING,则直接在当前线程执行消息事件接收
                        case POSTING:
                            invoke(method, keyObj, postEvent);
                            break;
                        case MAIN:
                            //8-2 如果是MAIN,则判断当前线程是否为主线程,如果否,则通过主线程Looper实例化Handler,post到主线程执行消息事件接受
                            //如果当前线程为主线程,则直接执行消息事件接收
                            if (Looper.myLooper() == Looper.getMainLooper()){
                                invoke(method, keyObj, postEvent);
                            }else{
                                new Handler(Looper.getMainLooper()).post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(method, keyObj, postEvent);
                                    }
                                });
                            }
                            break;
                            //8-3 如果是BACKGROUND,则判断当前线程是否为主线程,如果是,则开一个新的子线程去执行消息事件接收
                            // 如果当前线程为子线程,则直接执行消息事件接收
                        case BACKGROUND:
                            if (Looper.myLooper() == Looper.getMainLooper()){
                                executorService.execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(method, keyObj, postEvent);
                                    }
                                });
                            }else {
                                invoke(method, keyObj, postEvent);
                            }
                            break;
                            //8-4 如果是ASYNC,则不管当前线程是什么,直接开一个新的子线程去执行消息事件接收
                        case ASYNC:
                            executorService.execute(new Runnable() {
                                @Override
                                public void run() {
                                    invoke(method, keyObj, postEvent);
                                }
                            });
                            break;
                        default:
                            break;
                    }

                }
            }
        }
    }

    /**
     *  通过反射调用订阅者方法
     */
    private void invoke(Method method, Object keyObj, Object postEvent) {
        try {
            method.invoke(keyObj, postEvent);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     *  空指针检查
     */

    private void AssertNotNullCheck(Object object, String object_is_null) {
        if (object == null){
            throw new NullPointerException(object_is_null);
        }
    }

}

3.MethodManager封装类,对订阅方法的注解信息,入参类型等信息进行封装。

public class MethodManger {
    /**
     *  通过反射获取的method方法对象 
     */
    private Method method;
    /**
     * @Subscribe注解的threadMode的值
     */
    private ThreadMode threadMode;
    /**
     *  订阅方法的入参类型 
     */
    private Class<?> type;

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

    public Method getMethod() {
        return method;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public Class<?> getType() {
        return type;
    }
}

4. ThreadMode 线程模式,用于@Subscribe注解,直接从源码中复制。

public enum ThreadMode {
    /**
     * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
     * If not on Android, behaves the same as {@link #POSTING}.
     */
    MAIN,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
     * the event will always be queued for delivery. This ensures that the post call is non-blocking.
     */
    MAIN_ORDERED,

    /**
     * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
     */
    BACKGROUND,

    /**
     * Subscriber will be called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
     */
    ASYNC
}

5.在MainActivity中进行接收

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnStart = findViewById(R.id.btn_start_second);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondAcvitity.class);
                startActivity(intent);
            }
        });
        MEventBus.getDefault().register(this);
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void showLog(String value){
        Log.i(TAG, "showLog get value is " + value + " and current thread name is " + Thread.currentThread().getName());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        MEventBus.getDefault().unregister(this);
    }

6.在SecondActivity进行发送消息。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        btnPost = findViewById(R.id.btn_post);
        btnPost.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                new Thread(new Runnable() {
//                    @Override
//                    public void run() {
                        MEventBus.getDefault().post("你好呀,MainActivity,我是SecondActivity");
//                    }
//                }).start();
            }
        });
    }

至此消息的发送和接收已经基本完成了。此文仅探讨EventBus的核心机制,对于一些细节,如strict等没有进行实现,有兴趣的童鞋可以去查看EventBus源码。

参考:网易云课堂,android高级开发工程师公开课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
森林防火应急联动指挥系统是一个集成了北斗定位/GPS、GIS、RS遥感、无线网络通讯、4G网络等技术的现代化智能系统,旨在提高森林火灾的预防和扑救效率。该系统通过实时监控、地图服务、历史数据管理、调度语音等功能,实现了现场指挥调度、语音呼叫通讯、远程监控、现场直播、救火人员生命检测等工作的网络化、智能化、可视化。它能够在火灾发生后迅速组网,确保现场与指挥中心的通信畅通,同时,系统支持快速部署,适应各种极端环境,保障信息的实时传输和历史数据的安全存储。 系统的设计遵循先进性、实用性、标准性、开放性、安全性、可靠性和扩展性原则,确保了技术的领先地位和未来的发展空间。系统架构包括应急终端、无线专网、应用联动应用和服务组件,以及安全审计模块,以确保用户合法性和数据安全性。部署方案灵活,能够根据现场需求快速搭建应急指挥平台,支持高并发视频直播和大容量数据存储。 智能终端设备具备三防等级,能够在恶劣环境下稳定工作,支持北斗+GPS双模定位,提供精确的位置信息。设备搭载的操作系统和处理器能够处理复杂的任务,如高清视频拍摄和数据传输。此外,设备还配备了多种传感器和接口,以适应不同的使用场景。 自适应无线网络是系统的关键组成部分,它基于认知无线电技术,能够根据环境变化动态调整通讯参数,优化通讯效果。网络支持点对点和点对多点的组网模式,具有低功耗、长距离覆盖、强抗干扰能力等特点,易于部署和维护。 系统的售后服务保障包括安装实施服务、系统维护服务、系统完善服务、培训服务等,确保用户能够高效使用系统。提供7*24小时的实时故障响应,以及定期的系统优化和维护,确保系统的稳定运行。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值