前言: 迷茫,本就是青春该有的样子,但不要让未来的你,讨厌现在的自己。
一、概述
我们在上一篇文章中介绍了EventBus普通事件和粘性事件的使用,对EventBus也有了一个大概的了解,但是我们对事件和发布者订阅者的关系有没有弄清楚呢?继续来深入了解。
EventBus使用的是发布/订阅者模式:
发布者通过EventBus发布事件,订阅者通过EventBus订阅事件,当发布者发送事件时,订阅该事件的订阅者的事件处理方法将被调用。从图中看出,发布者发送一个事件时,则该事件将会同时传递给一个或多个该事件的订阅者。
什么是发布/订阅者模式?
订阅者(Subscriber)把自己想订阅的事件(Event)注册(register)到调度中心(EventBus),当发布者(Publisher)发布该事件到调度中心时,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码(onEvent())。(发布/订阅者模式与观察者模式并不完全一样)
EventBus的三要素
● Event: 事件, 可以使任意类型;
● Publisher: 事件发布者,可以在任意线程中发布事件,一般情况,通过EventBus.getDefault()
获取EventBus实例,通过post(Object event)
发布事件;
● Subscriber: 事件订阅者,在EventBus3.0之前,订阅者的事件处理方法必须定义以onEvent开头的具体方法名,而在3.0之后方法名可以可以随意取,但是必须加上@Subscribe()注解,并且指定线程模式,默认为ThreadMode.POSTING
。
EventBus的几种线程模式
线程模式 | 含义 | 特点 |
---|---|---|
POSTING | 默认线程,表示订阅者将在发布事件的同一线程中被调用 | 适用于不需要主线程就可以在短时间内完成的简单任务,避免了线程切换,开销更小 |
MAIN | 表示在Android中,订阅者在Android主线程中被调用 | 如果发布事件的线程是主线程,订阅者的事件处理函数将直接被调用。如果发布事件的线程不是主线程,则将事件加入主线程队列中,排队等待执行;因此这里不能进行耗时操作,注意不能阻塞主线程 |
MAIN_ORDERED | 表示在Android中,订阅者在Android主线程中被调用 | 与MAIN不同的是,无论发布事件的线程是在哪个线程,事件都将发送到主线程队列总是排队等待传递。注意不能阻塞主线程 |
BACKGROUND | 表示在Android中,订阅者在后台线程中被调用, | 在Android中,如果发布线程不是主线程,订阅者的事件处理函数直接使用该线程,如果发布线程是主线程,那么事件处理函数会开启一个后台线程,有序分发事件,注意不能阻塞后台线程,这里不能进行UI操作;如果不是在Android上,总是使用一个后台线程 |
ASYNC | 表示无论发布线程是什么线程,订阅者都会创建一个新的子线程执行 | 使用于耗时操作,尽量避免同时触发大量的耗时较长的异步操作,EventBus使用线程池高效的复用已经完成异步操作的线程。 |
那么说明POSTING
模式的订阅者处理函数线程与发布线程一致,MAIN
和MAIN_ORDERED
模式的订阅者处理函数线程为主线程,BACKGROUND
和ASYNC
模式的订阅者处理函数线程为后台线程。
二、项目实践
首先来回顾一下EventBus普通事件的几个步骤:
(1)定义事件对象,事件对象可以是任意java类型,没有特殊要求,比如String、int、自定义类等。
public class MessageEvent {
public String name;
}
(2)在接收消息的页面的生命周期方法中注册和反注册事件。
//注册EventBus
EventBus.getDefault().register(this);
//注销EventBus
EventBus.getDefault().unregister(this);
(3)订阅者实现事件处理方法,也称为"订阅者方法",当发布对应事件类型时,该方法被调用。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent message){
//TODO 接收事件后Do something
}
(4)发布事件。
EventBus.getDefault().post(Object event);
2.1几种线程模式使用
线程模式主要是为了发布事件和处理事件之间的线程切换使用,首先来验证一下不同的线程模式处理事件在哪个线程,创建两个Activity,ModeReceiveActivity中注册EventBus并且实现订阅者事件处理函数,ModeSendActivity负责发布事件。
ModeReceiveActivity.java
/**
* 几种线程模式
* 注册并接收事件
*/
public class ModeReceiveActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "ModeReceiveActivity";
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mode_receive);
findViewById(R.id.btn_skip).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
//1.注册事件
EventBus.getDefault().register(this);
}
//3.订阅者的接收事件处理函数
//处理函数执行线程与发布线程一致
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEventPosting(String str) {
//TODO 接收事件后Do something
mTv_content.setText("onMessageEvent:" + str);
Log.e(TAG, "onMessageEventPosting:" + Thread.currentThread().getName());
}
//处理函数执行线程为主线程
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEventMain(String str) {
Log.e(TAG, "onMessageEventMain:" + Thread.currentThread().getName());
}
//处理函数执行线程为主线程
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessageEventMainOrdered(String str) {
Log.e(TAG, "onMessageEventMainOrdered:" + Thread.currentThread().getName());
}
//处理函数执行线程为后台线程
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEventBackground(String str) {
Log.e(TAG, "onMessageEventBackground:" + Thread.currentThread().getName());
}
//处理函数执行线程为后台线程
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageEventAsync(String str) {
Log.e(TAG, "onMessageEventAsync:" + Thread.currentThread().getName());
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_skip://跳转到ModeSendActivity
startActivity(new Intent(this, ModeSendActivity.class));
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
}
ModeReceiveActivity作为接受消息的页面,是订阅者,首先在在onCreate()
注册EventBus,onDestroy()
中反注册,并且实现几种线程模式的订阅者的事件处理函数,将接收数据的线程通过Thread.currentThread().getName()
线程名字打印log,点击按钮跳转到ModeSendActivity发布者中在主线程发布事件。
ModeSendActivity.java
/**
* 几种线程模式
* 发送事件
*/
public class ModeSendActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mode_send);
findViewById(R.id.btn_send).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send://发布事件
mTv_content.setText("对ModeReceiveActivity发布事件");
//4.发布事件
EventBus.getDefault().post("接收到ModeSendActivity发送过来的事件啦");
break;
}
}
}
点击发布事件按钮ModeSendActivity给ModeReceiveActivity发送事件,这里发布的线程是主线程。打印数据如下:
可以看到,POSTING
模式的线程与发布线程一致,都是主线程;MAIN
和MAIN_ORDERED
模式的线程为主线程,BACKGROUND
和ASYNC
模式线程为后台线程。(源码在文章最后给出)
2.2事件的优先级
EventBus支持定义订阅者事件处理方法时指定事件传递的优先级。默认情况下,事件优先级为0,数值越大,优先级越高,在相同线程模式下,优先级高的比优先级低的先接收到事件。
注意:优先级只有在相同线程模式下才有效。
@Subscribe(threadMode = ThreadMode.MAIN, priority = 0)
public void onMessageEvent(String str) {
//TODO 接收事件后Do something
}
● priority : 事件优先级, int类型,默认为0;数值越大,优先级越高,在相同线程模式下,优先级高的比优先级低的先接收到事件。
我们来创建两个Activity检验一下,PriorityReceiveActivity实现订阅者的事件处理方法,不同优先级的多个方法,PrioritySendActivity负责发布事件。
PriorityReceiveActivity.java
/**
* 事件优先级
* 注册并接收事件
*/
public class PriorityReceiveActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "PriorityReceiveActivity";
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_priority_receive);
findViewById(R.id.btn_skip).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
//1.注册事件
EventBus.getDefault().register(this);
}
//3.订阅者的接收事件处理函数, 事件优先级0,1,2
@Subscribe(threadMode = ThreadMode.MAIN, priority = 0)
public void onMessageEvent(String str) {
//TODO 接收事件后Do something
mTv_content.setText("onMessageEvent:" + str);
Log.e(TAG, "onMessageEvent:priority = 0 " + str);
}
@Subscribe(threadMode = ThreadMode.MAIN, priority = 1)
public void onMessageEvent1(String str) {
Log.e(TAG, "onMessageEvent:priority = 1 "+str);
}
@Subscribe(threadMode = ThreadMode.MAIN, priority = 2)
public void onMessageEvent2(String str) {
Log.e(TAG, "onMessageEvent:priority = 2 " + str);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_skip://跳转到PrioritySendActivity
startActivity(new Intent(this, PrioritySendActivity.class));
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
}
这里定义了priority = 0,1,2三个优先级的处理函数,在onCreate()
注册EventBus,onDestroy()
中反注册,点击按钮跳转到PrioritySendActivity中;
/**
* 事件优先级
* 发送事件
*/
public class PrioritySendActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_priority_send);
findViewById(R.id.btn_send).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send://发布事件
mTv_content.setText("对PriorityReceiveActivity发布事件");
//4.发布事件
EventBus.getDefault().post("接收到PrioritySendActivity发送过来的事件啦");
break;
}
}
}
点击发布事件按钮,给PriorityReceiveActivity发布事件,接收到数据,效果如下:
打印log如下:
从数据可以看到,优先级priority = 0<1<2,优先级高的比优先级低的先接收到数据。(源码在文章最后给出)
三、AndroidEventBus的使用
发布者发送一个事件时,则该事件将会同时传递给一个或多个该事件的订阅者。如果发布事件的事件对象类型与订阅者处理事件方法对象类型匹配,那么匹配的所有已注册的事件都会接收到。
//发布事件
EventBus.getDefault().post(new MessageEvent());
//多个接收事件处理的函数
//接收Activity1事件处理
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEventActivity1(MessageEvent message) {
//TODO 接收事件后Do something
Log.e(TAG, "onMessageEventActivity1 ==");
}
//接收Activity2事件处理
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEventActivity2(MessageEvent message) {
//TODO 接收事件后Do something
Log.e(TAG, "onMessageEventActivity2 ==");
}
onMessageEventActivity1()
和onMessageEventActivity2()
都在同一个Activity中订阅事件,但是发布者来自不同的Activity,发布事件post(Object event)
方法中的event的类型与处理事件onMessageEvent(Object event)
中event的类型一致的话,都会接收到发布的数据。打印数据如下:
当然我们可以指定不同的事件对象类型,或者在事件对象类型添加标志区分,比如在MessageEvent中添加type区分不同。但是如果都是事件对象类型都是String那就区分不了。
3.1AndroidEventBus
我们可以使用AndroidEventBus,EventBus是AndroidEventBus框架的核心类,也是用户的入口类。它存储了用户注册的订阅者信息和方法,事件类型和该事件对应的tag标识一个种类的事件EventType,每一种事件对应有一个或者多个订阅者,订阅者中的订阅函数通过@Subscriber注解来标识tag和线程模型,这样使得用户体检较为友好,代码也更加整洁。
AndroidEventBus与EventBus不同的是,多了一种该事件对应的tag标识和订阅者中的订阅函数通过@Subscriber注解(EventBus的是@Subscribe)。
//发布者发布事件,tag为from_three
EventBus.getDefault().post(new MessageEvent("接收到AndroidBusSendActivity发送过来的事件啦"), "from_three");
//订阅者的事件处理函数, 有tag为from_three
@Subscriber(tag = "from_three", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(MessageEvent message) {
}
● post(Object event, String tag): 发布者发布事件的方法;event表示要发布的事件,任意类型;tag表示事件的标识,用于区分其他事件,String类型(区分大小写);
●@Subscriber:必须使用@Subscriber注解来定义订阅者方法,否则事件处理方法无法生效。与EventBus的@Subscribe类同;
●tag : tag表示事件的标识,用于区分其他事件。与发布事件的tag相对应;
●mode : 线程模式,表示在哪个线程里面执行。与EventBus的threadMode类同。
3.2AndroidEventBus普通事件
AndroidEventBus与EventBus的用法大致相同,首先在build.gradle文件中添加依赖:
implementation 'org.simple:androideventbus:1.0.5.1'
然后创建两个Activity,AndroidBusSendActivity发布者发布事件,AndroidBusReceiveActivity在onCreate()
和onDestroy()
中注册反注册事件,实现不同tag的多个订阅者事件处理方法。
注意,需要使用AndroidEventBus的包org.simple.eventbus
AndroidBusReceiveActivity.java
import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;
import org.simple.eventbus.ThreadMode;
/**
* AndroidEventBus
* 接收消息页面
*/
public class AndroidBusReceiveActivity extends AppCompatActivity implements View.OnClickListener{
public static final String TAG = "AndroidEventBus";
private TextView mTv_content, mTv_content2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_receive);
findViewById(R.id.btn_skip).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
mTv_content2 = findViewById(R.id.tv_content2);
//1.注册事件
EventBus.getDefault().register(this);
}
//3.接收事件处理, 有tag为from_three
@Subscriber(tag = "from_three", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(MessageEvent message) {
mTv_content2.setText("eventAndroidEventBus: tag = from_three | " + message.name);
Log.e(TAG, "eventAndroidEventBus == tag = from_three | " + message.name);
}
//接收事件处理,有tag为FROM_THREE
@Subscriber(tag = "FROM_THREE", mode = ThreadMode.MAIN)
public void eventAndroidEventBus1(MessageEvent message) {
mTv_content.setText("eventAndroidEventBus: tag = FROM_THREE | " + message.name);
Log.e(TAG, "eventAndroidEventBus1 == tag = FROM_THREE | " + message.name);
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
}
AndroidBusSendActivity.java
import org.simple.eventbus.EventBus;
/**
* AndroidEventBus
* 发送消息页面
*/
public class AndroidBusSendActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_send);
findViewById(R.id.btn_send).setOnClickListener(this);
mTv_content = findViewById(R.id.tv_content);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send://发布事件
mTv_content.setText("对AndroidBusReceiveActivity发布事件");
//4.发布事件,tag为from_three
EventBus.getDefault().post(new MessageEvent("接收到AndroidBusSendActivity发送过来的事件啦"), "from_three");
break;
default:
break;
}
}
}
效果如下:
打印数据如下:
可以看到,即使事件类型一致,但是只有发布事件和订阅函数处理的tag相同才能接收到事件。
3.3AndroidEventBus的粘性事件
粘性事件能在发送事件之后再订阅该事件也能接收到该事件。与普通事件不同,普通事件是先注册后发布,粘性事件可以先发布后注册。AndroidEventBus的粘性事件的使用与EventBus的粘性事件有点不一样,AndroidEventBus需要注册粘性事件,订阅者的事件处理函数不需要添加sticky标识。来看看AndroidEventBus的粘性事件的使用步骤:
(1) 在onCreate()
中注册粘性事件registerSticky()
,onDestroy()
中反注册事件。
@Override
protected void onCreate(Bundle savedInstanceState) {
//1.注册粘性事件
EventBus.getDefault().registerSticky(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
(2) 订阅者的事件处理函数。
//3.接收粘性事件处理, 有tag为from_android_sticky
@Subscriber(tag = "from_android_sticky", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(String message) {
Log.e(TAG, "eventAndroidEventBus == tag = from_android_sticky | " + message);
//5.移除粘性事件
EventBus.getDefault().removeStickyEvent(String.class, "from_android_sticky");
}
注意:如果不需要粘性事件了通过抽象removeStickyEvent()
及时移除粘性事件。
(3) 发布者发布粘性事件postSticky()
。
//4.发布粘性事件,tag为from_android_sticky
EventBus.getDefault().postSticky("接收到AndroidStickySendActivity发送过来的事件啦", "from_android_sticky");
粘性事件主要步骤就是这几个,我们来验证一下,创建两个Activity,AndroidStickySendActivity先布粘性事件然后再跳转AndroidStickyReceiveActivity,AndroidStickyReceiveActivity创建并注册EventBus后接收到粘性事件消息。
/**
* AndroidEventBus粘性事件
* 发送消息页面
*/
public class AndroidStickySendActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_send_sticky);
findViewById(R.id.btn_send).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send://发布事件
//4.发布粘性事件,tag为from_three
EventBus.getDefault().postSticky("接收到AndroidStickySendActivity发送过来的事件啦", "from_android_sticky");
startActivity(new Intent(this, AndroidStickyReceiveActivity.class));
break;
}
}
}
AndroidStickySendActivity中点击发布粘性事件按钮,给AndroidStickyReceiveActivity发送事件,再跳转到AndroidStickyReceiveActivity中。
/**
* AndroidEventBus粘性事件
* 接收消息页面
*/
public class AndroidStickyReceiveActivity extends AppCompatActivity {
public static final String TAG = "StickyReceiveActivity";
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_receive_sticky);
mTv_content = findViewById(R.id.tv_content);
//1.注册粘性事件
EventBus.getDefault().registerSticky(this);
}
//3.接收粘性事件处理, 有tag为from_three
@Subscriber(tag = "from_android_sticky", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(String message) {
mTv_content.setText("eventAndroidEventBus == tag = from_android_sticky | " + message);
Log.e(TAG, "eventAndroidEventBus == tag = from_android_sticky | " + message);
//5.移除粘性事件
EventBus.getDefault().removeStickyEvent(String.class, "from_android_sticky");
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
}
AndroidStickyReceiveActivity完成创建并注册EventBus,实现订阅者的粘性事件处理方法,接收到发送过来的粘性事件数据,效果如下:
打印数据如下:
注意:发送事件的参数类型一定要与接收事件的参数类型一致啊,否则无法接收到事件的。
点关注,不迷路
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !
要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!
相关文章:
EventBus3.2详解和使用(一)
● EventBus:普通事件和粘性事件的使用
EventBus3.2详解和使用(二)
● EventBus三要素、线程模式、优先级和AndroidEventBus的使用
EventBus3.2详解和使用(三)
● EventBus内部原理