解锁管理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 事件泛型
* @return 返回给定事件类型的最近粘性事件
*/
fun getStickyEvent(stickyEventType: Class): T = EventBus.getDefault().getStickyEvent(stickyEventType)
/**
* 手动删除粘性事件
* @param stickyEventType 粘性事件
* @param 事件泛型
* @return 返回给定事件类型的最近粘性事件
*/
fun removeStickyEvent(stickyEventType: Class): T = EventBus.getDefault().removeStickyEvent(stickyEventType)
}
结语
其实核心代码就那么几行,大多数开发者在日常工作中都没有用过自定义注解,本文借EventBus向大家展示了自定义注解+反射进行初始化的一个操作,其实还是十分的简单的。
技巧都是慢慢积累起来的,也许以后的开发过程中,就可以用这种方式去解决一些问题,这也是我一年前看博客发现的一个操作,当时我还在实习,心里想着竟然还有这种骚操作,今后一直到现在我都将这让种方法沿用至今,今天拿出来水了一篇,也是想分享下这个小技巧。
code 不只是工作,也会是热爱
另外有兴趣的可以看一下我刚毕业时写的第一篇文章:一个 Android MVVM 组件化架构框架