前言:当我们进行项目开发的时候,往往是需要应用程序的各组件,组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过处理程序或者是广播通知UI,而两个片段之家可以通过监听器进行通信等等。当我们的项目越来越复杂,使用意向,处理程序,广播进行模块间通信,模块与后台线程进行通信时,代码量大,而且高度耦合。现在就让我们来学习一下EventBus 3.0吧。

首先使用EventBus之前,我们先要了解一下EventBus:

  • 简化组件之间的通信
    解耦事件发送器和接收器
    与活动,片段和后台线程良好地运行,
    避免了复杂且容易出错的依赖关系和生命周期问题
  • 使您的代码更简单
  • 很快
  • 很小(约50k罐)
  • 已经通过100,000,000+安装的应用程序在实践中得到证实
  • 具有交付线程,用户优先级等高级功能。
  1. Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。
  2. Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING
  3. Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。

1.2 四种线程模型

EventBus3.0有四种线程模型,分别是:

  1. POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
  2. MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
  3. BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  4. ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

2、EventBus 使用

2.1 引入依赖

在使用之前先要引入如下依赖:

implementation 'org.greenrobot:eventbus:3.1.1'

2.2 定义事件

然后,我们定义一个事件的封装对象。在程序内部就使用该对象作为通信的信息:

public class MessageWrap {public final String message;public static MessageWrap getInstance(String message) {return new MessageWrap(message);}private MessageWrap(String message) {this.message = message;}
}

2.3 发布事件

然后,我们定义一个Activity:

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY1)
public class EventBusActivity1 extends CommonActivity<ActivityEventBus1Binding> {@Overrideprotected void doCreateView(Bundle savedInstanceState) {// 为按钮添加添加单击事件getBinding().btnReg.setOnClickListener(v -> EventBus.getDefault().register(this));getBinding().btnNav2.setOnClickListener( v ->ARouter.getInstance().build(BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2).navigation());}@Overrideprotected void onDestroy() {super.onDestroy();EventBus.getDefault().unregister(this);}@Subscribe(threadMode = ThreadMode.MAIN)public void onGetMessage(MessageWrap message) {getBinding().tvMessage.setText(message.message);}
}

这里我们当按下按钮的时候向EventBus注册监听,然后按下另一个按钮的时候跳转到拎一个Activity,并在另一个Activity发布我们输入的事件。在上面的Activity中,我们会添加一个监听的方法,即onGetMessage,这里我们需要为其加入注解Subscribe并指定线程模型为主线程MAIN。最后,就是在Activity的onDestroy方法中取消注册该Activity。

下面是另一个Activity的定义,在这个Activity中,我们当按下按钮的时候从EditText中取出内容并进行发布,然后我们退出到之前的Activity,以测试是否正确监听到发布的内容。

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
public class EventBusActivity2 extends CommonActivity<ActivityEventBus2Binding> {@Overrideprotected void doCreateView(Bundle savedInstanceState) {getBinding().btnPublish.setOnClickListener(v -> publishContent());}private void publishContent() {String msg = getBinding().etMessage.getText().toString();EventBus.getDefault().post(MessageWrap.getInstance(msg));ToastUtils.makeToast("Published : " + msg);}
}

根据测试的结果,我们的确成功地接收到了发送的信息。

2.4 黏性事件

所谓的黏性事件,就是指发送了该事件之后再订阅者依然能够接收到的事件。使用黏性事件的时候有两个地方需要做些修改。一个是订阅事件的地方,这里我们在先打开的Activity中注册监听黏性事件:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {String txt = "Sticky event: " + message.message;getBinding().tvStickyMessage.setText(txt);
}

另一个是发布事件的地方,这里我们在新的开的Activity中发布黏性事件。即调用EventBus的postSticky方法来发布事件:

private void publishStickyontent() {String msg = getBinding().etMessage.getText().toString();EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));ToastUtils.makeToast("Published : " + msg);
}

按照上面的模式,我们先在第一个Activity中打开第二个Activity,然后在第二个Activity中发布黏性事件,并回到第一个Activity注册EventBus。根据测试结果,当按下注册按钮的时候,会立即触发上面的订阅方法从而获取到了黏性事件。

2.5 优先级

Subscribe注解中总共有3个参数,上面我们用到了其中的两个,这里我们使用以下第三个参数,即priority。它用来指定订阅方法的优先级,是一个整数类型的值,默认是0,值越大表示优先级越大。在某个事件被发布出来的时候,优先级较高的订阅方法会首先接受到事件。

为了对优先级进行测试,这里我们需要对上面的代码进行一些修改。这里,我们使用一个布尔类型的变量来判断是否应该取消事件的分发。我们在一个较高优先级的方法中通过该布尔值进行判断,如果未true就停止该事件的继续分发,从而通过低优先级的订阅方法无法获取到事件来证明优先级较高的订阅方法率先获取到了事件。

这里有几个地方需要注意

  1. 只有当两个订阅方法使用相同的ThreadMode参数的时候,它们的优先级才会与priority指定的值一致;
  2. 只有当某个订阅方法的ThreadMode参数为POSTING的时候,它才能停止该事件的继续分发。

所以,根据以上的内容,我们需要对代码做如下的调整:

// 用来判断是否需要停止事件的继续分发
private boolean stopDelivery = false;@Override
protected void doCreateView(Bundle savedInstanceState) {// ...getBinding().btnStop.setOnClickListener(v -> stopDelivery = true);
}@Subscribe(threadMode = ThreadMode.POSTING, priority = 0)
public void onGetMessage(MessageWrap message) {getBinding().tvMessage.setText(message.message);
}// 订阅方法,需要与上面的方法的threadMode一致,并且优先级略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 1)
public void onGetStickyEvent(MessageWrap message) {String txt = "Sticky event: " + message.message;getBinding().tvStickyMessage.setText(txt);if (stopDelivery) {// 终止事件的继续分发EventBus.getDefault().cancelEventDelivery(message);}
}

即我们在之前的代码之上增加了一个按钮,用来将stopDelivery的值置为true。该字段随后将会被用来判断是否要终止事件的继续分发,因为我们需要在代码中停止事件的继续分发,所以,我们需要将上面的两个订阅方法的threadMode的值都置为ThreadMode.POSTING

按照,上面的测试方式,首先我们在当前的Activity注册监听,然后跳转到另一个Activity,发布事件并返回。第一次的时候,这里的两个订阅方法都会被触发。然后,我们按下停止分发的按钮,并再次执行上面的逻辑,此时只有优先级较高的方法获取到了事件并将该事件终止。

简单走一遍流程:

首先新建一个实体类:

/**
* @desc 这个类就是一个Bean的类,里面定义用来传输的数据的类型。
* @author lijianguo
* @date 2019/3/8
* @version
*/
public class MessageEvent {private String message;public MessageEvent(String message) {this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}

OneActivity:

public class MainActivity extends AppCompatActivity  {private TextView tvText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//在活动的的onCreate()方法里去注册EventBus,在onDestory()方法里,去解除注册。tvText = (TextView) findViewById(R.id.tv_text);tvText.setText("你好");tvText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent=new Intent(MainActivity.this,MyTwoActivity.class);startActivity(intent);}});EventBus.getDefault().register(this);}@Subscribe(threadMode = ThreadMode.MAIN)public void initData(MessageEvent messageEvent) {tvText.setText(messageEvent.getMessage());}@Overrideprotected void onDestroy() {super.onDestroy();EventBus.getDefault().unregister(this);}

TwoActivity:

public class MyTwoActivity extends AppCompatActivity {private TextView tvText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvText = (TextView) findViewById(R.id.tv_text);tvText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {EventBus.getDefault().post(new MessageEvent("你来了"));finish();}});}
}

案例1:

在A界面想B界面传递消息

一般情况下使用intent直接传递就可以了,但是如果不存在跳转,就可以使用粘性事件发送消息:

EventBusUtils 类:封装发送方法以及注册方法

public class EventBusUtils extends AppCompatActivity {public EventBusUtils() {}/*** 注册 EventBus* @param subscriber*/public static void register(Object subscriber) {EventBus eventBus = EventBus.getDefault();if (!eventBus.isRegistered(subscriber)) {eventBus.register(subscriber);}}/*** 解除注册 EventBus* @param subscriber*/public static void unregister(Object subscriber) {EventBus eventBus = EventBus.getDefault();if (eventBus.isRegistered(subscriber)) {eventBus.unregister(subscriber);}}/*** 发送事件消息** @param event*/public static void post(MessageEvent event) {EventBus.getDefault().post(event);}/*** 发送粘性事件消息** @param event*/public static void postSticky(MessageEvent event) {EventBus.getDefault().postSticky(event);}}

---------------------------------------------------------------------

MessageEvent:是消息Bean类,传递消息类型

public class MessageEvent<T> {//消息内容private T message;//消息类型标识private int code;public int getCode() {return code;}public void setCode(int code) {this.code = code;}public MessageEvent(int code, T message) {this.message = message;this.code = code;}public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}
}

---------------------------------------------------------------------

MainActivity :A界面,向B界面进行传递,这里跳转是为了展示B数据,通过postSticky发送粘性事件,

public class MainActivity extends EventBusUtils {@BindView(R.id.bt_bus)Button btBus;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);//第一个是要传递的标记   第二个参数是数据postSticky(new MessageEvent<>(1000, "EventData"));btBus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this, TwoActivity.class);startActivity(intent);}});}
}

---------------------------------------------------------------------

TwoActivity :B界面,用来接收A界面发送的消息,通过 @Subscribe订阅者来订阅该方法,并且进行注册与解注册。

package com.example.ljg.myeventbus;import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;import butterknife.BindView;
import butterknife.ButterKnife;public class TwoActivity extends EventBusUtils {@BindView(R.id.tv_text)TextView tvText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_two);ButterKnife.bind(this);register(this);}//标记和发送消息的标记一样的,包括类型和值都必须一样@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void onEvent(MessageEvent event) {Toast.makeText(this, "" + event.getMessage(), Toast.LENGTH_SHORT).show();}@Overrideprotected void onDestroy() {super.onDestroy();unregister(this);}}

案例结束。

提示:当A直接发送B界面时,如果B界面没有注册,是收不到消息的。即使发布者发了消息,但订阅者还未产生(一般消息的处理逻辑是先注册订阅,后接收),这样没有收到消息当然无法响应操作,所以就需要用到粘性广播。如果在发送之前,接受界面已经注册,那么就可以直接使用Post来直接发送。这样就可以更好的理解EventBus。走过的坑,记录~

案例2:

案例情况:在A界面已经注册,在B界面通过Post发送,在A界面进行接收。

MainActivity :A界面已经启动,在B界面发送后在A界面接收

public class MainActivity extends EventBusUtils {@BindView(R.id.bt_bus)Button btBus;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);register(this);btBus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this, TwoActivity.class);startActivity(intent);}});}//标记和发送消息的标记一样的,包括类型和值都必须一样@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void onEvent(MessageEvent event) {Toast.makeText(this, "" + event.getMessage(), Toast.LENGTH_SHORT).show();Log.e("TAG", "event=" + event.getMessage());}@Overrideprotected void onDestroy() {super.onDestroy();unregister(this);}
}

------------------------------------------------------

TwoActivity :B界面发送

public class TwoActivity extends EventBusUtils {@BindView(R.id.tv_text)TextView tvText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_two);ButterKnife.bind(this);//第一个是要传递的标记   第二个参数是数据post(new MessageEvent<>(1000, "EventData"));}
}

EventBus踩坑:

在Demo中是没有问题的,加到项目就报错了,加入官方文档的混淆代码就可以了。报错信息:

报错代码:

EventBusUtils.registerEvent(this);//注册时候报错
java.lang.NoSuchFieldError: No static field MAIN of type Lorg/greenrobot/eventbus/ThreadMode; in class Lorg/greenrobot/eventbus/ThreadMode; or its superclasses (declaration of 'org.greenrobot.eventbus.ThreadMode' appears in /data/app/cn.com.ngds.gamestore.oem-1/base.apk)at java.lang.reflect.Method.getAnnotationNative(Native Method)at java.lang.reflect.Method.getAnnotation(Method.java:557)

变量找不到,很明显,又是被ProGuard给去掉了。于是在progrard-rules.pro加一句:

加到:proguard-rules文件中

-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# EventBus 3.0
-keepclassmembers class ** {
    public void onEvent*(**);
}

# EventBus 3.0 annotation
-keepclassmembers class * {
    @de.greenrobot.event.Subscribe <methods>;
}

添加完后重新编译一下项目,就可以了。

EventBus使用、案例、代码混淆报错相关推荐

  1. 信鸽推送集成采坑之代码混淆报错/XINGE: [Util] please add wup-1.0.0.E-SNAPSHOT.jar in your libs

    转载请注明:https://blog.csdn.net/u012854870/article/details/80546448 信鸽推送接入刚开始按照官方推荐加入一下混淆代码: ########### ...

  2. oracle ogg00423,【案例】Oracle报错PLS-00378 PLS-00439产生原因和MOS官方解决办法

    [案例]Oracle报错PLS-00378 PLS-00439产生原因和MOS官方解决办法 时间:2016-11-12 21:31   来源:Oracle研究中心   作者:代某人   点击: 次 天 ...

  3. oracle创建实例ora28040,【案例】Oracle报错ORA-28040 No matching authentication

    天萃荷净 运维DBA反映在连接Oracle数据库时报错:ORA-28040: No matching authentication protocol,结合MOS分析原因为多版本共存导致 电脑上面安装了 ...

  4. oracle pls 00905,【案例】Oracle报错PLS-00714 PLS-00951原因和解决办法笔记

    [案例]Oracle报错PLS-00714 PLS-00951原因和解决办法笔记 时间:2016-11-14 11:07   来源:Oracle研究中心   作者:代某人   点击: 次 天萃荷净 P ...

  5. oracle 10035 err 942,案例:Oracle日志报错 Fatal NI connect error 12170 TNS-12535 TNS-00505

    天萃荷净 Oracle数据库alert日志文件报错Fatal NI connect error 12170,通过查看mos相关文章找到解决办法 今天在一台服务器的日志文件中,发现如下信息: Fatal ...

  6. SAP QM中阶执行事务代码QDB1,报错- Inspection severity 001 AQL 0.650 not in sampling schema A01-

    SAP QM中阶执行事务代码QDB1,报错- Inspection severity 001 AQL 0.650 not in sampling schema A01- 对于sampling proc ...

  7. SAP QM初阶执行事务代码QDB1,报错- Sampling procedure NM000001 has no sampling scheme-

    SAP QM初阶执行事务代码QDB1,报错- Sampling procedure NM000001 has no sampling scheme- SAP QM模块里,事务代码QDB1用于维护取样策 ...

  8. SAP QM 执行事务代码QA11 报错- Selected set code does not exist, or data entered is incomplete-

    SAP QM 执行事务代码QA11 报错- Selected set code does not exist, or data entered is incomplete- 执行事务代码QA11试图为 ...

  9. SAP MM 执行事务代码MRRL报错-No message was found for partner 100065 company code 0001-

    SAP MM 执行事务代码MRRL报错-No message was found for partner 100065 company code 0001- 1, 执行事务代码MRRL 触发invoi ...

最新文章

  1. 怎样用python绘制简单的图形_使用python绘制图形并使用HTML显示它
  2. mysql数据冗余_MySQL冗余数据的三种方案
  3. Netflix推荐系统(Part two)-系统架构
  4. Ribbon的初始化源码
  5. kali linux 模板文件夹,详解kali linux 常用文件与指令路径
  6. table合并单元格_element ui el-table 合并单元格
  7. unity3D协程(Coroutine)原理深入剖析
  8. iOS弹窗UIAlertController的使用
  9. HttpClient相关
  10. 【前端 教程】详解 立即执行函数
  11. 前端面试系列-JavaScript作用域和作用域链
  12. 查询京东快递物流状态,快速筛选出代收的单号
  13. 关于AI输电线路在线监测多目4G摄像头低功耗解决方案
  14. OraDump导出套件
  15. php朋友圈九宫格怎么做,微信朋友圈九宫格视频怎么做 图片背景加九宫格视频随机播放的效果制作|微信九宫格视频...
  16. 力扣 LCP 42. 玩具套圈 (数学公式反推)
  17. 7牛-qshel的一些使用
  18. Spring框架知识
  19. webman apidoc安装、生成接口文档
  20. 完美解决windows10系统磁盘占用100%并出现卡顿、假死无反应

热门文章

  1. C语言程序设计——结构体的运用 求复数之积。利用结构变量求解如下两组复数之积。
  2. 深度学习-----缺乏可解释性
  3. 解压压缩的文件没有管理员权限
  4. 【头歌】Pandas进阶
  5. 自动驾驶感知——导航与定位
  6. 基于C语言的传输协议封包、发包实现
  7. python序列的元素可以相乘吗_python – 有效地将每行的元素相乘
  8. 00后太卷了,公司新来的一位卷王,表示我们这帮老油条真干不过.....
  9. 有什么图片自动去水印的软件?看完这篇文章你就知道啦
  10. java毕业设计企业间信息交互系统源码+lw文档+mybatis+系统+mysql数据库+调试