简介
事件总线库,极大地简化了 Activities, Fragments, Threads, Services等各组件之间的通信。更少的代码,更好的体检
EventBus的github地址
优点
- 简化组件之间的通信,对事件的发送者和接收者进行解耦;在Activity、Fragment、以及后台线程中运转良好;避免复杂和容易出错的依赖关系以及生命周期问题
- 使你的代码更简单,代码可读性更好
- 快
- 小(~ 50K的jar包)
- 在安装量多达100,000,000+ 的应用中实践,表现优异
- 独具特色的功能,如线程分发,优先级订阅等。
项目实战
需求背景
- 登录、登出成功之后,涉及的相关页面要即时刷新登录数据,做出相应的调整
- 多个页面涉及比赛预约的状态,一个页面预约或者取消预约成功,要即时更新其它页面的比赛预约状态
步骤
部分概念相关的可以参考下面的相关介绍穿插理解
1、首先需要定义消息类,该类可以不继承任何基类也不需要实现任何接口
这里以LoginEvent(用户登录退出场景) 和AppointmentStateEvent (见注释)为例来介绍
public class OnEventBusInterface {
public interface OnLoginListener {
void onEventMainThread(LoginEvent event);
}
public interface OnTopicRefreshListener {
void onEventMainThread(FollowTopicRefreshEvent event);
}
public interface OnAppointmentStateListener {
void onEventMainThread(AppointmentStateEvent event);
}
public static class LoginEvent {
private boolean hasLoginSucc;
public LoginEvent(boolean loginSucc) {
this.hasLoginSucc = loginSucc;
}
public boolean hasLoginSucc() {
return hasLoginSucc;
}
public void setHasLoginSucc(boolean hasLoginSucc) {
this.hasLoginSucc = hasLoginSucc;
}
}
//FollowFragment刷新事件
public static class FollowTopicRefreshEvent {
}
/**
* 在比赛详情页
* 比赛未开始
* 从首页直播tab进入或者从球队详情页的比赛tab进入
* 在比赛未开始时,可以预约或者取消预约比赛,这个操作完成后再返回上面两个入口时,需要刷新预约状态
* 这里传入了matchId作为参数,目前没有用到,因为现在比赛的预约与否是存在本地数据库的
* 如果以后预约比赛的状态和用户绑定在服务器,那么可能就需要使用这个matchId了
* <p/>
* 需要3个有关注状态的地方 互相通知最新的关注状态
* add By SuS
*/
public static class AppointmentStateEvent {
private String matchId;
public AppointmentStateEvent(String matchId) {
this.matchId = matchId;
}
public String getMatchId() {
return matchId;
}
public void setMatchId(String matchId) {
this.matchId = matchId;
}
}
2、在需要订阅事件的地方注册事件,在需要取消消息订阅的地方取消消息订阅
public class BaseLoginActivity extends BaseActivity implements OnEventBusInterface.OnLoginListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);//注册EventBus
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//反注册EventBus
}
protected void onLoginSuccess(){
}
protected void onLoginOut(){
}
public void onEventMainThread(OnEventBusInterface.LoginEvent event){
if(event.hasLoginSucc()){
onLoginSuccess();
}else{
onLoginOut();
}
}
}
public class BaseLoginFragment extends BaseFragment implements IHandlerMessage,OnEventBusInterface.OnLoginListener,OnEventBusInterface.OnTopicRefreshListener,OnEventBusInterface.OnAppointmentStateListener {
protected Handler handler;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
handler = new CommonHandler<BaseLoginFragment>(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);//注册EventBus
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//反注册EventBus
}
@Override
public void handlerCallback(Message msg) {
}
protected void showLoginWindow(){
StormUtils2.startLoginActivity(getActivity());
}
protected void onLoginSuccess(){
}
protected void onLoginOut(){
}
protected void onFollowStatusChanged() {}
protected void onAppointmentStateChanged(String matchId){
}
public void onEventMainThread(OnEventBusInterface.LoginEvent event){
if(event.hasLoginSucc()){
onLoginSuccess();
}else{
onLoginOut();
}
}
public void onEventMainThread(OnEventBusInterface.FollowTopicRefreshEvent event){
onFollowStatusChanged();
}
@Override
public void onEventMainThread(OnEventBusInterface.AppointmentStateEvent event) {
onAppointmentStateChanged(event.getMatchId());
}
}
3、分发事件,即触发消息
这里对BaseActivity小做解释:当通过插件化的方式加载暴风体育的时候,启动登录,通过startActivityForResult的方式调用主版的登录(之所以调用主版的登录是因为主应用和以插件化方式加载的暴风体育第三方登录(QQ,微信)的签名不同),当登录成功获取用户信息后,分发用户登录成功的消息
代码很简洁,具体相关操作如下:
EventBus.getDefault().post(new OnEventBusInterface.LoginEvent(true));//登录成功
EventBus.getDefault().post(new OnEventBusInterface.LoginEvent(false));//登出成功
EventBus.getDefault().post(new OnEventBusInterface.AppointmentStateEvent(String.valueOf(matchInfo.getId())));//比赛预约状态改变成功
4、消息处理
这里使用了最普通的方式,没有使用EventBus的注解模式,而且考虑到收到消息后的处理都是在主线程中完成,所以采用了onEventMainThread方法。
继承了BaseLoginActivity的Activity的相关处理:
继承了BaseLoginFragment的Fragment的相关处理:
注意:
- 在3.0之前,EventBus还没有使用注解方式。消息处理的方法也只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分别代表四种线程模型。而在3.0之后,消息处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为PostThread)
- 事件处理函数的访问权限必须为public,否则会报异常
相关代码如下:
public void onEventMainThread(OnEventBusInterface.LoginEvent event){
if(event.hasLoginSucc()){
onLoginSuccess();
}else{
onLoginOut();
}
}
public void onEventMainThread(OnEventBusInterface.FollowTopicRefreshEvent event){
onFollowStatusChanged();
}
@Override
public void onEventMainThread(OnEventBusInterface.AppointmentStateEvent event) {
onAppointmentStateChanged(event.getMatchId());
}
相关介绍
线程模型
主要包括如下四种:
POSTING:
- 发布事件和接收事件在同一个线程
- 避免执行耗时操作,否则会阻塞事件的传递,有可能会引起ANR
@Subscribe(threadMode = ThreadMode.POSTING) // ThreadMode is optional here
public void onMessage(MessageEvent event) {
log(event.message);
}
MAIN:
- 无论事件从哪里发布,接收事件都在UI线程
- 可以用来更新UI,但是不能处理耗时操作
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
BACKGROUND:
- 发布事件来自主线程,接收事件则在新的线程中运行;发布事件来自子线 程,接收事件也在该子线程完成
- 禁止进行UI更新操作
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
ASYNC:
- 无论事件从哪里发布,接收事件都在新的子线程
- 禁止进行UI更新操作
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
订阅优先级以及事件取消
尽管大多数情况下eventbus是不需要设置订阅的优先级和事件取消,但是某些特殊的场景可能派上用场。例如,当应用程序在前台,存在一个事件可能会触发一些用户界面相关逻辑,但当应用不可见时应该有不同的反应
优先级设置:
- 默认优先级是0,同样的线程分发模式,优先级更高的订阅者会先收到消息
- 不同线程模式的订阅者接收消息的顺序呢不受优先级影响
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
…
}
事件取消:
- 事件取消一般都是被高优先级的订阅者调用
- 严格限制在线程模式为POSTING的消息处理方法中
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
…
EventBus.getDefault().cancelEventDelivery(event) ;
}
粘性事件
EventBus还支持发送黏性事件,就是在发送事件之后再订阅该事件也能收到该事件,能够收到订阅之前发送的消息。但是它只能收到最新的一次消息。
这里不做过多介绍,详情请参考Sticky Events
总结
- 简单强大
- 实战验证
- 高性能
- 基于API的简洁注解
- 主线程和子线程均可进行消息发布和订阅
- 事件以及订阅者继承特点
- 零配置且可配
码字!排版!画图!终于写完了!