app开发中,我们常遇见不同页面之间要保持数据同步。送最初的onActivityResult,到后面的第三方库EventBus,RxBus,LiveEventBus。现在我们需要利用livedata自己写一个简单的消息总线。
对比过去:
onActivityResult:使用较为繁琐,并且在多页面下(比如栈:A,B,C。C的事件要传递给A就使用不方便了)。
EventBus:时代的眼泪,使用方便,但性能内存体积都是缺陷。
RxBus:不是库,是个文件,实现只有短短30行代码。结合Rxjava可以很方便的使用。
LiveEventBus:利用系统的livedata。体积小。且可以感知生命周期。
消息总线 | 延迟发送 | 有序接收消息 | Sticky | 生命周期感知 | 跨进程/APP | 线程分发 |
---|---|---|---|---|---|---|
EventBus | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
RxBus | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ |
LiveEventBus | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
它们都很优秀。但我们不用它们。自己写个简单的实现方案。主要参考LiveEventBus的核心逻辑。
代码:
短短50多行代码(去掉注释和空行也就30行左右)。
object EventHelper {
private val map = HashMap<String, EventLiveData<*>>()
@Suppress("UNCHECKED_CAST")
fun <T> with(key: String, type: Class<T>? = null): EventLiveData<T> {
if (!map.containsKey(key)) {
map[key] = EventLiveData<T>()
}
return map[key] as? EventLiveData<T>
?: throw RuntimeException("cannot cast EventLiveData of " + type.toString())
}
/**
* 记录版本的 MutableLiveData。
*/
class EventLiveData<T> : MutableLiveData<T>() {
var version: Int = 0
private set
/**
* postValue 最终也会调 setValue ,所以只需要在这里统计 version。
*/
override fun setValue(value: T) {
version++
super.setValue(value)
}
/**
* 订阅事件。绑定生命周期。返回 Observer 方便主动移除观察。
*/
fun observeEvent(owner: LifecycleOwner, onEvent: (T) -> Unit): Observer<T> {
return EventObserver(onEvent).apply { super.observe(owner, this) }
}
/**
* 订阅事件。永久有效。返回 Observer 方便主动移除观察。
*/
fun observeForeverEvent(onEvent: (T) -> Unit): Observer<T> {
return EventObserver(onEvent).apply { super.observeForever(this) }
}
/**
* 不接收创建之前的消息。
*/
inner class EventObserver<T>(val onEvent: (T) -> Unit) : Observer<T> {
private var observerVersion = this@EventLiveData.version
override fun onChanged(t: T) {
if (observerVersion < this@EventLiveData.version) {
observerVersion = this@EventLiveData.version
onEvent(t)
}
}
}
}
}
使用姿势:
// 获取:(一般会把这步封装起来)
EventHelper.with<Event>(Key) //or
EventHelper.with(Key,Event::class.java)
// 订阅:
observeEvent(owner){ event -> do something } // 绑定生命周期。
observeForeverEvent{ event -> do something } // 永久有效。
// 发布:
setValue(event) // 主线程。
postValue(event) // 子线程。
// 移除:
同liveData。
实战:
- 封装:同一模块的事件放在一起。key 与 event 对应。
- key 命名规范:模块名+子模块名+具体事件。
- event类上加上ID:例如:ChangeGroupNameEvent加上groupCode明确是那个群名称变了。
// 示例
object IMEventHelper {
private const val CHANGE_GROUP_NAME = "im/group/changeGroupName"
private const val CHANGE_NOTIFICATION = "im/group/changeNotification"
private const val CHANGE_MEMBER_LIST = "im/group/changeMemberList"
private const val CHANGE_GROUP_LIST = "im/group/changeGroupList"
private const val QUIT_GROUP = "im/group/quitGroup"
private const val CHOOSE_AT = "im/group/chooseAt"
/**
* 群名称修改。
*/
fun changeGroupName() = EventHelper.with<ChangeGroupNameEvent>(CHANGE_GROUP_NAME)
/**
* 公告修改。
*/
fun changeNotification() = EventHelper.with<ChangeNotificationEvent>(CHANGE_NOTIFICATION)
/**
* 成员列表变换。
*/
fun changeMemberList() = EventHelper.with<ChangeMemberListEvent>(CHANGE_MEMBER_LIST)
/**
* 群聊列表变换。
*/
fun changeGroupList() = EventHelper.with<ChangeGroupListEvent>(CHANGE_GROUP_LIST)
/**
* 退出群聊。
*/
fun quitGroup() = EventHelper.with<QuitGroupEvent>(QUIT_GROUP)
/**
* 选择AT 成员。
*/
fun chooseAt() = EventHelper.with<AtMemberEvent>(CHOOSE_AT)
}
event类:
class ChangeGroupNameEvent(val groupCode: String, val newName: String)
使用:
// 在 fragment 初始化里订阅事件。
IMEventHelper
.changeGroupName()
.observeEvent(this) { vm.changeName(it) }
// 在 其它地方发布事件。
IMEventHelper
.changeGroupName()
.postValue(ChangeGroupNameEvent(groupCode, newName))
总结:
要点:
- 利用LiveData,感知生命周期的特性。
- 不接收注册观察之前的消息。
配一张图