解锁管理EventBus注册新姿势——自定义注解+反射

8B40D4E0-C581-43E0-860A-6C9082052E41_1_201_a.jpeg

解锁管理EventBus注册新姿势——自定义注解+反射

开局一张图,装备全靠捡
本文旨在分享code生涯当中的一些小技术

EventBus简介

官网对EventBus的介绍
EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.

翻译过来就是:
EventBus是一个Android和Java的开源库,使用发布者/订阅者模式进行松散耦合。EventBus使中央通信能够仅用几行代码解耦类——简化代码,减少依赖项,并加快应用程序开发。

从介绍中我们可以了解到这是一个事件发布/订阅框架

使用EventBus有什么好处?

  • 简化组件之间的通信
  • 事件发送方和接收方解耦
  • 能够很好地使用UI组件(例如Activities, Fragments)和后台线程
  • 避免复杂且容易出错的依赖关系和生命周期问题
  • 速度快;专门为高性能而优化
  • 很小(~60k jar)
  • 实际安装量超过10,000,000个的应用程序证明了这一点
  • 具有切换线程、订阅者优先级等高级功能

EventBus自2012-07由greenrobot发布第一个版本,历经7年之久,现版本已更新至3.2。从事Android开发的伙伴就算没用过也一定听说过这个框架,因为它实在是太普及了。尽管目前也有许多新的事件总线框架出现,但是依旧阻挡不住我对它的热爱,作为经典的观察者模式实现,以及在Android平台的普及度,它也深受面试官们宠爱。

EventBus简单使用

这里不作EventBus的全面解析,只罗列下简单的使用,想必大家对于EventBus的使用已经是滚瓜烂熟了,但是这里还是要水一下

注册与解除注册

使用该框架进行事件的发布需要进行注册,不再使用时需要进行解除注册

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 注册成为订阅者
        EventBus.getDefault().register(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 解除注册 不再接收事件
        EventBus.getDefault().unregister(this)
    }
}

发布事件

/**
 * 自定义的事件类
 *
 * @property message String 消息
 *
 * @author Qu Yunshuo
 * @since 3/29/21 8:05 PM
 */
data class SimpleEvent(val message: String)

/**
 * 发布一个事件
 */
fun postEvent() {
    EventBus.getDefault().post(SimpleEvent("Hello EventBus!"))
}

绑定接受事件的方法

/**
 * 通过[Subscribe]注解进行注册,并且可以指定[ThreadMode]该方法执行的线程类型
 */
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: SimpleEvent) {
    Log.d("qqq", "onEvent: ${event.message}")
}

使用自定义注解管理EventBus注册

至此就是发布与订阅的简单实用,更详细的使用在这里就不详细说明,不是本文的重点。
到这里就能发现,我们每个订阅者都需要进行手动的注册与解除注册,否则会产生异常。
那最理想的状态就是不用每一次都手动的注册与解除注册,而是能够自动完成。

常见封装方式:

/**
 * 最常见的方式就是写在基类中,在[onCreate]方法中进行注册,在[onDestroy]方法中进行解除注册
 * 这样实现类就可以自动的完成注册与解除注册,完全不用考虑忘记解除注册这种低级错误🙅
 */
open class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        EventBus.getDefault().register(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        EventBus.getDefault().unregister(this)
    }
}

这种方式简单粗暴有效,但是弊端也很明显,不管子类需不需要注册都会自动帮你注册完,简直快乐的一批,当然这不是我们想要的效果。
那其实可以在这种方式上进行优化,比如写一个hook方法,让子类重写决定是否进行初始化,这也是一个不错的方案。

open class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 判断子类是否需要进行注册和解除注册
        if (isRegisterEventBus()) EventBus.getDefault().register(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 判断子类是否需要进行注册和解除注册
        if (isRegisterEventBus()) EventBus.getDefault().unregister(this)
    }

    /**
     * 是否注册EventBus
     * @return Boolean
     */
    open fun isRegisterEventBus(): Boolean = false
}

这也是一个不错的方案,让子类决定是否进行注册,避免一刀切全部注册的情况,代价只是多重写一个方法。下面介绍另外一种方案,使用自定义注解+反射。

自定义注解+反射

整体思路就是我们自定义一个注解,在需要进行注册的类上添加注解,在基类里进行判断当前子类是否使用了该注解从而决定是否进行注册

/**
 * 进行标记需要进行注册EventBus
 *
 * @author Qu Yunshuo
 * @since 3/29/21 8:33 PM
 */
@Target(AnnotationTarget.CLASS)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class EventBusRegister


/**
 * Activity基类
 * 
 * @author Qu Yunshuo
 * @since 3/29/21 8:36 PM
 */
open class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 获取到Class对象判断是否有EventBusRegister注解
        if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) {
            EventBus.getDefault().register(this)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 获取到Class对象判断是否有EventBusRegister注解
        if (javaClass.isAnnotationPresent(EventBusRegister::class.java)) {
            EventBus.getDefault().unregister(this)
        }
    }
}

以上是注解和基类的逻辑,十分的简单,需要注册时,只需要在添加该注解就ok

@EventBusRegister
class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {...}
}

是不是使用起来也是十分的简单,并不是说这种方案是最优的,本文只是介绍通过自定义注解+反射来实现封装EventBus注册的逻辑,get到以后就可以进行举一反三。

简单封装Utils

下面也放上简单封装的EventBus工具类

/**
 * EventBus工具类
 * 
 * @author Qu Yunshuo
 * @since 3/29/21 8:42 PM
 */
object EventBusUtils {

    /**
     * 订阅
     * @param subscriber 订阅者
     */
    fun register(subscriber: Any) = EventBus.getDefault().register(subscriber)

    /**
     * 解除注册
     * @param subscriber 订阅者
     */
    fun unRegister(subscriber: Any) = EventBus.getDefault().unregister(subscriber)

    /**
     * 发送普通事件
     * @param event 事件
     */
    fun postEvent(event: Any) = EventBus.getDefault().post(event)

    /**
     * 发送粘性事件
     * @param stickyEvent 粘性事件
     */
    fun postStickyEvent(stickyEvent: Any) = EventBus.getDefault().postSticky(stickyEvent)

    /**
     * 手动获取粘性事件
     * @param stickyEventType 粘性事件
     * @param <T>             事件泛型
     * @return 返回给定事件类型的最近粘性事件
     */
    fun <T> getStickyEvent(stickyEventType: Class<T>): T = EventBus.getDefault().getStickyEvent(stickyEventType)

    /**
     * 手动删除粘性事件
     * @param stickyEventType 粘性事件
     * @param <T>             事件泛型
     * @return 返回给定事件类型的最近粘性事件
     */
    fun <T> removeStickyEvent(stickyEventType: Class<T>): T = EventBus.getDefault().removeStickyEvent(stickyEventType)
}

结语

其实核心代码就那么几行,大多数开发者在日常工作中都没有用过自定义注解,本文借EventBus向大家展示了自定义注解+反射进行初始化的一个操作,其实还是十分的简单的。
技巧都是慢慢积累起来的,也许以后的开发过程中,就可以用这种方式去解决一些问题,这也是我一年前看博客发现的一个操作,当时我还在实习,心里想着竟然还有这种骚操作,今后一直到现在我都将这让种方法沿用至今,今天拿出来水了一篇,也是想分享下这个小技巧。
code 不只是工作,也会是热爱
另外有兴趣的可以看一下我刚毕业时写的第一篇文章:一个 Android MVVM 组件化架构框架

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容