EventBus 的运行流程一般都是:先建立 event 和 method 的订阅关系,再 post event,然后在 method 中接收 event 并处理。
但有时我们需要处理这样的情况:先 post event,然后建立 event 和 method 的订阅关系。建立时需要接收之前 post 的 event 并处理。
这样的 event 就叫做 Sticky Event(粘性事件)。
想要实现这个功能,我们需要在 post event 时,将 event 保存起来。在订阅关系建立时,如果发现这个 method 是 sticky 的,则检查是否有已存的 event,如果有,则触发一次 method。
一、新增 sticky 变量
修改 @Subscribe
:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Subscribe(val threadMode: ThreadMode = ThreadMode.MAIN, val sticky: Boolean = false)
二、在 KEventBus 中保存 Sticky 消息
KEventBus 中新增 postSticky 方法,将 Sticky 消息保存到 stickyEvents
中:
object KEventBus {
//...
private val stickyEvents = mutableMapOf<Class<*>, Any>()
fun postSticky(event: Any) {
stickyEvents[event.javaClass] = event
post(event)
}
fun removeSticky(event: Class<*>) {
stickyEvents.remove(event)
}
}
可以看到,stickyEvents 的类型时 MutableMap<Class<*>, Any>()
,这意味着如果 post 多条同一类型的 Sticky 消息,只会保留最后一条。
在 postSticky 方法中将 event 保存后,正常调用 post(event)
方法,使得当前已经订阅的 method 能够正常接收消息。
removeSticky 方法用于移除不需要的 Sticky 消息。
三、register 时处理 Sticky 消息
fun register(obj: Any) {
// Reflect to get all subscribed methods
obj.javaClass.declaredMethods.forEach {
if (it.isAnnotationPresent(Subscribe::class.java)) {
if (it.parameterTypes.size == 1) {
val eventType = it.parameterTypes.first()
//...
if (it.getAnnotation(Subscribe::class.java)!!.sticky) {
if (stickyEvents[eventType] != null) {
it(obj, stickyEvents[eventType])
}
}
}
}
}
}
在 register 时,如果发现此方法是 sticky 的,则从 stickyEvents 中寻找是否有此事件类型的 Sticky 消息,如果存在,则调用一次 method。
四、测试
先 post sticky 消息:
KEventBus.postSticky("It's a sticky message")
接收方法:
@Subscribe(sticky = true)
fun onStickyEvent(message: String) {
Log.d("~~~", message)
}
然后注册订阅关系:
KEventBus.register(this)
注册时,Log 控制台输出如下:
~~~: It's a sticky message
网友评论