• Android应用内组件通讯之EventBus的使用(一)


    这周本来想再输出一篇vsomeip协议栈的服务提供流程源码分析的,无奈流程以及其中的状态太过于复杂,还在梳理中,于是准备水一篇应用内通讯组件EventBus来缓解一下疲累的心情(梳理源码的流程过于心累),之所以挑准了EventBus,是结合了实际工作过程中的场景:车机内部的进程间通信,以及应用内的组件通信的频率很高。准备后续就这方面来进行一些流程的优化开发,所以预先借助分析EventBus的使用方式,设计思想,以及源码实现来构造自己的通信组件SDK。

    说了那么多废话,下面进入正题,看下官网上提供的一个通信架构图,EventBus采用的是一个观察者模式设计,事件发布者提交事件给到总线,总线再将事件分发给订阅者。
    在这里插入图片描述

    集成方式

    在工程模块的build.gradle文件中添加如下依赖配置:

    dependencies {
        implementation 'org.greenrobot:eventbus:3.3.1'
        ...
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    API

    EventBus的源码量很少,API也很少,下面来分别来介绍下:

    • 注册监听与反注册
      每个模块需要监听数据时需要调用EventBus的register传入当前模块的引用,当模块退出时,需要调用unregister方法解注册,不然的话,当前模块无法被回收,后果就是造成内存泄漏。模块中需要定义全域可见的方法,并使用@Subscribe方法来标记,作为模块中接收事件的回调函数。函数定义如下:

    register(注册)

        /**
         * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
         * are no longer interested in receiving events.
         * 

    * Subscribers have event handling methods that must be annotated by {@link Subscribe}. * The {@link Subscribe} annotation also allows configuration like {@link * ThreadMode} and priority. */ public void register(Object subscriber)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    unregister(反注册)

        /** Unregisters the given subscriber from all event classes. */
        public synchronized void unregister(Object subscriber) 
    
    • 1
    • 2

    Subscribe(订阅方法注解)

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Subscribe {
        ThreadMode threadMode() default ThreadMode.POSTING;
    
        /**
         * If true, delivers the most recent sticky event (posted with
         * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
         */
        boolean sticky() default false;
    
        /** Subscriber priority to influence the order of event delivery.
         * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
         * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
         * delivery among subscribers with different {@link ThreadMode}s! */
        int priority() default 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用Activity来举个例子

    class TestActivity : AppCompatActivity() {
        override fun onStart() {
            super.onStart()
            //注册当前模块为监听器
            EventBus.getDefault().register(this)
        }
    
        override fun onStop() {
            super.onStop()
            //解注册模块
            EventBus.getDefault().unregister(this)
        }
    
        //定义监听回调方法,方法可见为全局的pulic方法,方法执行在Main函数中,优先级为1000,且监听的事件类型为粘性事件
        @Subscribe(threadMode = ThreadMode.MAIN, priority = 1000, sticky = true)
        fun eventObserver(event: Event1?){
            Log.i("eventbus", "seconds Received: ${data1.v0}")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上述定义后,只要有其他模块提交数据类型为Data1的事件,eventObserver方法就会在主线程中被执行,我们就可以在函数中处理对应的数据; 当然我们也可以让eventObserver函数运行在子线程中,只需要改变注解中的threadMode值即可。EventBus是根据@SubScribe所注解的函数参数来查找该模块所监听的参数,如果模块需要监听总线上所有的数据,则修改函数入参的类型为Any(Java语言为Object), 如果需要监听某一类型的数据,则需要定义数据类型与子数据,然后修改入参即可。

    data class Event0(val data0 : Int, val data1 : Int)
    open class Event1(data0 : Int, data1 : Int)
    class SubEvent(data0: Int, data1: Int, name:String = "SubEvent") : Event1(data0, data1)
    class SubEvent1(data0: Int, data1: Int, data2:Int, name: String = "SubEvent") : Event1(data0, data1)
    
    • 1
    • 2
    • 3
    • 4

    上述Demo中可以监听类型为Event1, SubEvent, SubEvent1的数据

    • Event处理
      event类型分为两种,普通事件与粘性事件,顾名思义,普通事件就是发布事件,然后给已经订阅的模块消费事件,如果模块订阅发生在事件发布之后,则该模块无法获取到该事件;粘性事件 就是一个长期存在于消息总线的事件,粘性事件发生后,只要模块注册完成,即可得到该事件。EventBus提供两个函数来发布事件: post用来发布普通事件,postSticky用来发布粘性事件。
        /** Posts the given event to the event bus. */
        public void post(Object event) 
    
    • 1
    • 2
        /**
         * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
         * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
         */
        public void postSticky(Object event)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    演示代码:

    private var i : Boolean = false
    findViewById<Button>(R.id.post_data).setOnClickListener {
        EventBus.getDefault().post(if(i) SubEvent(0,0) else SubEvent1(1,1, 1))
        EventBus.getDefault().postSticky(if(i) SubEvent(0,0) else SubEvent1(1,1, 1))
        i = i.not()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 其他操作

    终止事件处理

        /**
         * Called from a subscriber's event handling method, further event delivery will be canceled. Subsequent
         * subscribers
         * won't receive the event. Events are usually canceled by higher priority subscribers (see
         * {@link Subscribe#priority()}). Canceling is restricted to event handling methods running in posting thread
         * {@link ThreadMode#POSTING}.
         */
        public void cancelEventDelivery(Object event)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    移除粘性事件

    
        /**
         * Remove and gets the recent sticky event for the given event type.
         *
         * @see #postSticky(Object)
         */
        public <T> T removeStickyEvent(Class<T> eventType)
        
        
        /**
         * Removes the sticky event if it equals to the given event.
         *
         * @return true if the events matched and the sticky event was removed.
         */
        public boolean removeStickyEvent(Object event)
        
        /**
         * Removes all sticky events.
         */
        public void removeAllStickyEvents()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    获取消息总线中的粘性事件

        /**
         * Gets the most recent sticky event for the given type.
         *
         * @see #postSticky(Object)
         */
        public <T> T getStickyEvent(Class<T> eventType)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    模块订阅状态

     public synchronized boolean isRegistered(Object subscriber)
    
    • 1

    查找某事件类型是否有订阅模块

     public boolean hasSubscriberForEvent(Class<?> eventClass)
    
    • 1

    总结

    回顾一下,在Android应用开发过程中,EventBus解决了Android应用开发过程中各个模块中通信问题,且内部提供了指定线程类型来处理事务的机制,让我们处理事件更加方便,事件类型与订阅者的类型不受限制,更加灵活。 但是它并不是完美的,就类似于注册与反注册这两步来说,如果开发者稍不注意,注册之后,没有调用反注册,那就一定会引起订阅模块的对象无法释放,进而造成内存泄漏等问题,所以我看现在有一个更新的替代方案:LiveDataBus,LiveDataBus结合了jetpack中组件的LifeCycle来规避了监听对象因生命周期而造成内存泄漏的问题,这个后续有空再写一篇聊聊。下一篇去看看EventBus源码,学习优秀的开源库,有利于身心健康。

  • 相关阅读:
    Java三大特性篇之——多态篇(千字详解)
    【考研数学】线性代数第五章 —— 特征值和特征向量(2,特征值与特征向量的性质)
    【web开发】4、JavaScript与jQuery
    华为OD技术面试-最短距离矩阵(动态规划、广度优先)
    Scrum敏捷认证CSM(ScrumMaster)官方课程
    来吧!再谈多线程
    Android学习笔记 1.2.1 下载和安装Gradle && 1.2.2 Gradle构建文件和创建任务
    结构体学习
    快速体验Spring Boot了解使用、运行和打包 | SpringBoot 2.7.2学习系列
    【数据结构初阶-oj】入门二叉树的入门oj
  • 原文地址:https://blog.csdn.net/zjfengdou30/article/details/126061380