场景
- 模块间相互依赖,耦合度高。
- 后期维护起来困难。
- 代码缺少设计,不利于拓展。
Android中Activity、Service、Fragment之间的相互通信比较麻烦,主要有以下几种方式:
(1)使用广播,发送者发出广播,接收者接收广播后进行处理;
(2)使用Handler和Message,如下载线程完成下载任务后,给UI发送Message,UI收到Message后更新界面。
以上两种比较繁琐,接下来给大家介绍一种比较简单有效的方法事件总线(定义:针对事件提供统一订阅,发布以达到组件间通信的解决方案。 原理:观察者模式。发送者把消息发送到事件总线,然后事件总线在接收者中查找哪些方法注册了这个事件,如果某个方法注册了这个事件,就触发该方法。)。事件总线框架提供了一种更灵活更强大而又更加完美解耦的解决方案,在很多场合,从开发效率,执行性能和设计思路上都要优于BroadcastReceiver。
常用的事件总线框架
一、greenrobot的EventBus(官方参考:https://github.com/greenrobot/EventBus 最新版本 3.0.0)
1.EventBus概述
EventBus的三要素
- Event:事件,可以是任意类型的对象。
- Subscriber:事件订阅者,在EventBus3.0之前消息处理的方法只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,他们分别代表四种线程模型。而在EventBus3.0之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)。
- Publisher:事件发布者,可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法。可以自己实例化。EventBus对象,但一般使用EventBus.getDefault()就好了,根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
EventBus的四种ThreadMode(EventBus3.0)
- POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
- MAIN: 事件的处理会在UI线程中执行。事件处理时间不能太长,长了会ANR的。
- BACKGROUND:如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
- ASYNC:无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
2.EventBus基本用法
EventBus使用起来很简单,分为以下五个步骤:
自定义一个事件类
在需要订阅事件的地方注册事件
发送事件
处理事件(前面说过,消息处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型,默认为POSTING。)
取消事件订阅
添加依赖库
android Studio 配置gradle:
compile 'org.greenrobot:eventbus:3.0.0'
package com.xiaolajiao.technologyset;
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;
}
}
package com.xiaolajiao.technologyset;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MainActivity extends AppCompatActivity {private TextView tv_message;
private Button bt_message,bt_subscription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_message = (TextView) this.findViewById(R.id.tv_message);
tv_message.setText("MainActivity");
bt_message=(Button)this.findViewById(R.id.bt_message);
bt_message.setText("跳转到SecondActivity");
bt_message.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
//注册事件
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册事件
EventBus.getDefault().unregister(this);
}
}
事件订阅者处理事件
这里我们的ThreadMode设置为MAIN,事件的处理会在UI线程中执行,用TextView来展示收到的事件消息
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMoonEvent(MessageEvent messageEvent){
tv_message.setText(messageEvent.getMessage());
}
package com.xiaolajiao.technologyset;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.greenrobot.eventbus.EventBus;
public class SecondActivity extends AppCompatActivity {
private Button bt_message;
private TextView tv_message,bt_subscription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_message = (TextView) this.findViewById(R.id.tv_message);
tv_message.setText("SecondActivity");
bt_message=(Button)this.findViewById(R.id.bt_message);
bt_message.setText("发送事件");
bt_message.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("欢迎关注siwen的博客"));
finish();
}
});
}
}
在MainActivity中我们将注册事件添加到button的点击事件中:
bt_subscription.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注册事件
EventBus.getDefault().register(MainActivity.this);
}
});
订阅者处理粘性事件
在MainActivity中新写一个方法用来处理粘性事件:
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
public void ononMoonStickyEvent(MessageEvent messageEvent){
tv_message.setText(messageEvent.getMessage());
}
发送黏性事件
在SecondActivity中我们定义一个Button来发送粘性事件:
bt_subscription.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().postSticky(new MessageEvent("粘性事件"));
finish();
}
});
运行效果,首先我们在MainActivity中并没有订阅事件,而是直接跳到SecondActivity中点击发送粘性事件按钮,这时界面回到MainActivity,我们看到TextView仍旧显示着MainActivity的字段,这是因为我们现在还没有订阅事件。
接下来我们点击订阅事件,TextView发生改变显示“粘性事件”。
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
参考资料
Android EventBus框架(一)之使用详细介绍
Android EventBus实战 没听过你就out了
Android事件总线(一)EventBus3.0用法全解析
Android事件总线(二)EventBus3.0源码解析
二、Otto(官方参考:http://square.github.io/otto/ 最新版本 1.3.8)
2.1 添加依赖库
android Studio 配置gradle:
compile 'com.squareup:otto:1.3.8'
public class BusData {
public String message;
public BusData(String message){
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class OttoBus extends Bus{
private volatile static OttoBus bus;
private OttoBus (){
}
public static OttoBus getInstance() {
if (bus == null) {
synchronized (OttoBus.class){
if(bus==null){
bus = new OttoBus();
}
}
}
return bus;
}
}
public class MainActivity extends AppCompatActivity {
private Button bt_jump;
private TextView tv_message;
private Bus bus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_message= (TextView) this.findViewById(R.id.tv_message);
bt_jump= (Button) this.findViewById(R.id.bt_jump);
bt_jump.setText("跳转到SecondActivity");
bt_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
});
bus=OttoBus.getInstance();
bus.register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册订阅事件
bus.unregister(this);
}
otto同样需要注册和取消注册订阅事件,通过OttoBus得到Bus对象,调用Bus的register和unregister方法来注册和取消注册。
2.5 事件订阅者处理事件
@Subscribe
public void setContent(BusData data) {
tv_message.setText(data.getMessage());
}
和EventBus一样用@Subscribe来订阅事件,同样的用textView来显示接收到的消息。
2.6 使用post发送事件
public class SecondActivity extends AppCompatActivity {
private Button bt_jump;
private OttoBus bus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_jump= (Button) this.findViewById(R.id.bt_jump);
bt_jump.setText("发送事件");
bt_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
OttoBus.getInstance().post(new BusData("siwen的博客更新了"));
finish();
}
});
}
}
public class SecondActivity extends AppCompatActivity {
private Button bt_jump;
private OttoBus bus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_jump= (Button) this.findViewById(R.id.bt_jump);
bt_jump.setText("发送事件");
bt_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
bus=OttoBus.getInstance();
bus.register(this);
}
@Produce
public BusData setInitialContent() {
return new BusData("siwen的博客更新了");
}
@Override
protected void onDestroy() {
super.onDestroy();
bus.unregister(this);
}
}
三、AndroidEventBus(官方参考:https://github.com/hehonghui/AndroidEventBus 最新版本1.0.5)
类似于观察者模式,通过register函数将需要订阅事件的对象注册到事件总线中,然后根据@Subcriber注解来查找对象中的订阅方法,并且将这些订阅方法和订阅对象存储在map中。当用户在某个地方发布一个事件时,事件总线根据事件的参数类型和tag找到对应的订阅者对象,最后执行订阅者对象中的方法。这些订阅方法会执行在用户指定的线程模型中,比如mode=ThreadMode.ASYNC则表示该订阅方法执行在子线程中
AndroidEventBus的ThreadMode
它指定这个事件接收函数执行在哪个线程中,具体有如下三个选项:
- ThreadMode.MAIN,事件接收函数执行在UI线程;
- ThreadMode.POST,事件在哪个线程发布,接收函数就执行在哪个线程;
- ThreadMode.ASYNC,事件执行在一个独立的异步线程中。
AndroidEventBus使用分为以下步骤:
public class MainActivity extends Activity {
// 代码省略
@Subcriber(tag = "csuicide")
private void csuicideMyself(String msg) {
// do sth
finish();
}
@Subcriber(mode = ThreadMode.MAIN)
private void toastMsgFromEvent(String msg) {
// do sth
}
@Subcriber(tag = "async", mode = ThreadMode.ASYNC)
private void executeAsync(final String msg) {
// do sth
}
// 代码省略
}
为MainActivity添加了以下三个订阅函数:
- csuicideMyself:该订阅函数执行在主线程,接收事件的类型为String,tag为csuicide。当用户发布一个事件类型为String,且tag为csuicide的事件时将会触发该方法。
- toastMsgFromEvent:该订阅函数也是执行在主线程,事件类型为String,且tag为默认。当用户发布一个事件类型为String,且tag为默认的事件时将会触发该方法。
- executeAsync:该订阅函数也是执行在一个异步线程,事件类型为String,且tag为async。当用户发布一个事件类型为String,且tag为async的事件时将会触发该方法。
总结: