一款Android平台的LivaData的消息总线框架
应用介绍
作者codyer,源码ElegantBus,ElegantBus 是一款 Android 平台,基于 LivaData 的消息总线框架,这是一款非常 优雅 的消息总线框架。
如果对 ElegantBus 的实现过程,以及考虑点感兴趣的可以看看前几节自吹
如果只是想先使用的,可以跳过,直接到跳到使用说明
和常见 LivaData 实现的 EventBus 比较
消息总线 | 使用反射 | 入侵系统包名 | 进程内 Sticky | 跨进程 Sticky | 跨 APP Sticky | 事件可配置化 | 线程分发 | 消息分组 | 跨 App 安全考虑 | 常驻事件 Sticky |
---|---|---|---|---|---|---|---|---|---|---|
LiveEventBus | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
ElegantBus | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
来龙去脉
自吹
ElegantBus 支持跨进程,且支持跨应用的多进程,甚至是支持跨进程间的粘性事件,支持事件管理,支持事件分组,支持自定义事件,支持同名事件等。
之所以称之为最优雅的总线,是因为她不仅实现了该有的功能,而且尽量选用最合适,最轻量,最安全的方式去实现所有的细节。 更值得夸赞的是使用方式的优雅!
前言
随着 LifeCycle 的越来越成熟,基于 LifeCycle 的 LiveData 也随之兴起,业内基于 LiveData 实现的 EventBus 也如雨后春笋一般拔地而起。
出于对技术的追求,看过了无数大牛们的实现,各位大神们思路也是出奇的神通,最基础的 LiveData 版 EventBus 其实大同小异,一个单例类管理所有的事件 LivaData 集合。如果不清楚的可以随便网上找找
反正基本功能 LivaData 都支持了,实现 EventBus 只需要把所有事件管理起来就完事了。
业内基于 LiveData 实现的 EventBus,其实考虑的无非就是下面提到的五个挑战,有的人考虑的少,有的人考虑的多,于是各种方案都有。
ElegantBus 主要是集合各家之优势,进行全方面的考虑而产生的。
五个挑战 之 路途险阻
挑战一 : 粘性事件
背景 LivaData 的设计之初是为了数据的获取,因此无论是观察开始之前产生的数据,还是观察开始之后产生的数据,都是用户需要的数据,只要是有数据,当 LifeCycle 处于激活状态,数据就会传递给观察者。这个我们称之为 粘性数据。 这种设计对于事件来说有时候就不那么友好了,之前的事件用户可能并不关心,只希望收到注册之后发生的事件。
挑战二 : 多线程发送事件可能丢失
背景 同样是因为使用场景的原因,LivaData 设计在跨线程时,使用 post 提交数据,只会保留最后一次数据提交的值,因为作为数据来说,用户只需要关心现在有的数据是什么。
挑战三 : 跨进程事件总线
背景 有时候我们应用需要设置多进程,不同模块可能允许在不同进程中,因为单例模式每个进程都有一份实体,所有无法达到跨进程,这时候设计 IP 方案选择。
说明 这里提一下为什么不选用广播方式,对广播有一定了解的都知道,全局广播会有信息泄露,信息干扰等问题,而且开销也比较大,因此全局广播并不适合这种情况。 也许有人会说可以用本地广播,然而,本地广播目前来说并不是很好的选择。
Google 官方也在 LocalBroadcastManager 的说明里面建议使用 LiveData 替代: 原文地址
原文如下:
2018 年 12 月 17 日
版本 1.1.0-alpha01 中将弃用 <360>x.localbroadcastmanager。
原因
LocalBroadcastManager 是应用级事件总线,在您的应用中使用了层违规行为;任何组件都可以监听来自其他任何组件的事件。 它继承了系统 BroadcastManager 不必要的用例限制;开发者必须使用 Intent,即使对象只存在且始终存在于一个进程中。由于同一原因,它未遵循功能级 BroadcastManager。 这些问题同时出现,会对开发者造成困扰。
替换
您可以将 LocalBroadcastManager 替换为可观察模式的其他实现。合适的选项可能是 LiveData 或被动流,具体取决于您的用例。
更明显的原因是,本地广播好像并不支持跨进程~
挑战四 : 跨应用(权限问题以及粘性问题)
背景 跨进程相对来说还比较好实现,但是有的时候用户会有跨应用的需求,其实这个也是 IPC 范畴,为什么单独提出来呢? 因为跨应用设计信息安全,权限校验问题,开放给其他应用,但是同时又要兼顾不被非法滥用。 因为数据只是进程内共享的,跨应用时,粘性事件将失效,如果要保持和单进程一样支持粘性事件,需要做特殊处理。
挑战五 : 兼容性,简洁性
背景 一个好的事件总线需要很好的兼容,不同事件应该有个很好的管理,不会造成冲突,事件可以进行多种配置,如某事件是否支持跨进程,是否激活,属于什么分组等等。
使用说明
(一)ElegantBus 接入配置
1、项目级别 gradle 添加依赖 目前使用的是 jitPack
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
2、在应用 gradle 文件中添加 ElegantBus 最新版本依赖
def version = "2.0.0" dependencies { implementation "com.github.codyer.ElegantBus:core:$version" // 不需要跨进程时使用 // implementation "com.github.codyer.ElegantBus:ipc-aidl:$version" // 跨进程时使用(方式 1:aidl 实现,已经包含 core) // implementation "com.github.codyer.ElegantBus:ipc-messenger:$version" // 跨进程时使用(方式 2:messenger 实现,已经包含 core) // annotationProcessor "com.github.codyer.ElegantBus:compiler:$version"// 需要事件自动管理时使用 }
如果不需要跨进程,以上两步配置就可以了,如果需要跨进程,第二步选择一个跨进程的方式,并添加第三步配置,且设置第四步。
3、在应用 gradle 文件中的 manifestPlaceholders 配置是否支持跨 App,以及主 App 的 applicationId
manifestPlaceholders = [ BUS_SUPPORT_MULTI_APP : true,// 是否支持跨 App BUS_MAIN_APPLICATION_ID: "com.example.bus" // 肯定会被安装的主 app 的 applicationId ]
为了 App 安全性,必须使用相同的密钥签名的 App 才可以设置为一个公用组,否则 Debug 模式下会抛出异常,Release 模式下会输出 error 信息。
4、分别在应用的 Application 的 onCreate 和 onTerminate 方法中添加开始支持多进程和结束多进程
public class BusApplication extends Application { @Override public void onCreate() { super.onCreate(); ElegantBus.setDebug(true);// 可以打开日志开关 ElegantBusX.supportMultiProcess(this); } @Override public void onTerminate() { ElegantBusX.stopSupportMultiProcess(); super.onTerminate(); } }
以上几步就完成了使用 ElegantBus 的全部配置,下面进入使用环节
(二)ElegantBus 使用说明
1、 发送事件
最简单方式就是直接一句
ElegantBus.getDefault("EventA").post(new Object()); ElegantBus.getDefault("EventA").post("eventA"); ElegantBus.getDefault("EventA").post(888888);
可以在任何线程发送都是 OK 的,考虑大部分是没有跨进程需求的,所以这里默认,这种最简单的方式,这个事件 EventA 是不支持跨进程的。 如果要进行跨进程可以使用重载函数进行设置,重载函数如下:
ElegantBus.getDefault(String group, String event, Class<T> type, boolean multiProcess);
以下说的激活状态指页面处于 RESUMED 情况
2、 接收事件
接收事件也很简单:
常规事件
生命周期相关的事件,只有页面处于激活状态才会收到事件,如果在页面非激活状态时有事件发生,等页面激活(OnResume)时会收到事件。
ElegantBus.getDefault("EventA").observe(this, new ObserverWrapper<Object>() { @Override public void onChanged(final Object value) { ElegantLog.d(value.toString()); } });
粘性事件
如果观察之前有事件发生,也可以收到事件,eg:A 页面发送事件,打开 B 页面,B 页面开始观察,用粘性事件也可以收到。
ElegantBus.getDefault("EventA").observeSticky(this, new ObserverWrapper<Object>() { @Override public void onChanged(final Object value) { ElegantLog.d(value.toString()); } });
常驻事件
和生命周期无关,无论页面是否在激活状态,都可以收到事件,前提是页面已经打开了。
ObserverWrapper<Object> foreverObserverWrapper; ElegantBus.getDefault("EventA").observeForever(foreverObserverWrapper = new ObserverWrapper<Object>() { @Override public void onChanged(final Object value) { ElegantLog.d(value.toString()); } }); // 常驻事件要自己取消注册,避免内存泄露 ElegantBus.getDefault("EventA").removeObserver(foreverObserverWrapper);
其实普通事件和常驻事件都支持粘性事件
只要创建 ObserverWrapper 时设置 sticky = true 就可以; ElegantBus 提供了默认构造函数如下:参数 true 表示粘性事件
new ObserverWrapper<Object>(true) { @Override public void onChanged(Object value) {} })
以上简单的使用就介绍完毕了
高级特性
可以发现,上面的方式,接收的数据类型是 Object 的,因此,只要是同名的事件,无论发送的是什么类型,观察者都可以接收到。 为了对事件进行统一管理,防止事件冲突,事件大小写等拼写错误带来的问题,个人不建议直接使用这种方式
推荐使用事件定义方式
事件定义
先上例子
@EventGroup(value = "TestScope", active = true) public class EventDefine { @Event(description = "eventInt 事件测试", multiProcess = false, active = true) Integer eventInt; @Event(description = "eventString 事件测试", multiProcess = true, active = true) String eventString; @Event(description = "eventBean 事件测试", multiProcess = true, active = true) JavaBean eventBean; }
说明
其实事件定义只用到两个注解
1)、@EventGroup 使用在 class 上,定义事件分组名,是否激活
2)、@Event 使用在变量上,定义具体 事件描述,是否激活,是否支持多进程
定义完注解后,通过前面导入的注解处理器 annotationProcessor ,ElegantBus 会自动生成以 EventGroup 定义的分组名的事件总线 例如上面的定义就会生成一个 TestScopeBus
然后我们所有地方就可以直接使用这个事件总线进行事件管理。
发送事件
TestScopeBus.eventInt().post(888); TestScopeBus.eventString().post("新字符串"); TestScopeBus.eventBean().post(new JavaBean());
接收事件
TestScopeBus.eventInt().observe(owner, new ObserverWrapper<Integer>() { @Override public void onChanged(final Integer value) { ... } });
事件回调在非 UI 线程执行
默认事件是在主线程回调的,如果想在非主线程回调,设置 ObserverWrapper.uiTread = false,同时提供默认构造函数设置是否在 UI 线程回调。
©软件著作权归作者所有。本站所有内容均来源于网络,不得违法使用,仅供学习使用,请支持正版!
转载请注明出处: 662P.COM » 一款Android平台的LivaData的消息总线框架
发表评论 取消回复