2017-06-25
EventBus是一个事件总线框架,使用订阅/发布模式让组件间的通信变得简单。
官网:http://greenrobot.org/eventbus/
官方教程:http://greenrobot.org/eventbus/documentation/
Github地址:https://github.com/greenrobot/EventBus
原理很简单,就是订阅/发布模式的一个实现。好处是什么?解耦。
这篇文章从示例的角度解释EventBus的使用方法和原理。
示例展示了两个fragment之间如何通过EventBus通信。在fragment1中点击按钮发送事件(说成消息也可以),fragment2接收事件,并展示事件内容。效果如下:
1. 示例一:fragment之间通信
fragment之间的通信可以通过直接调用对象方法、广播等形式实现。这里有做些讨论。不过如果使用EventBus实现,既解耦,又优雅。
创建项目,在build.gradle中添加:
compile 'org.greenrobot:eventbus:3.0.0'
添加代码,最终结构如下:
1.1 MainActivity和布局文件
这个是程序入口:
package com.example.letian.eventbusapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
对应的布局文件activity_main.xml
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.letian.eventbusapplication.MainActivity">
<fragment android:name="com.example.letian.eventbusapplication.Example01Fragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment android:name="com.example.letian.eventbusapplication.Example02Fragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这里指定了两个fragment。对应Example01Fragment
类和Example02Fragment
类。
1.2 MessageEvent事件类
事件类的本质就是封装消息,根据需要自定义即可。
package com.example.letian.eventbusapplication.event;
public class MessageEvent {
private String data;
public MessageEvent(String data) {
this.data = data;
}
public void setData(String data) {
this.data = data;
}
public String getData() {
return data;
}
@Override
public String toString() {
return "MessageEvent{" +
"data='" + data + '\'' +
'}';
}
}
1.3 Example01Fragment和布局文件
Example01Fragment类内容如下:
package com.example.letian.eventbusapplication;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.example.letian.eventbusapplication.event.MessageEvent;
import org.greenrobot.eventbus.EventBus;
public class Example01Fragment extends Fragment {
private View rootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
// return super.onCreateView(inflater, container, savedInstanceState);
rootView = inflater.inflate(R.layout.example01_fragment, container, false);
Button sendBtn = rootView.findViewById(R.id.send);
sendBtn.setOnClickListener(new View.OnClickListener() { // 点击按钮,发布事件
@Override
public void onClick(View view) {
EventBus.getDefault().post(new MessageEvent("Hello everyone!")); // 发布事件
}
});
return rootView;
}
}
EventBus.getDefault()
得到EventBus的默认实例,post方法用于发布事件。
布局文件example01_fragment.xml
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送消息"/>
</LinearLayout>
1.4 Example02Fragment和布局文件
Example02Fragment类内容如下:
package com.example.letian.eventbusapplication;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.letian.eventbusapplication.event.MessageEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class Example02Fragment extends Fragment {
private View rootView;
private TextView textView;
private static String TAG = "Example02Fragment";
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
// return super.onCreateView(inflater, container, savedInstanceState);
rootView = inflater.inflate(R.layout.example02_fragment, container, false);
textView = rootView.findViewById(R.id.msg);
return rootView;
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this); // 将该对象注册到 EventBus
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); // 解除注册关系
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) { // 监听 MessageEvent 事件
Log.v(TAG, "GET event: "+event);
textView.setText(event.getData());
}
}
EventBus.getDefault().register(this);
是将Example02Fragment
作为订阅者注册到EventBus中;EventBus.getDefault().unregister(this);
用于解除订阅关系。onMessage
方法监听MessageEvent
类型的事件,在MAIN
线程(也就是主线程)收到事件后,将数据显示在textView中。
布局文件example02_fragment.xml
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="接收消息"/>
<TextView
android:id="@+id/msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
2. 示例二:使用索引类提升性能
示例1中使用EventBus基于反射收集订阅者的信息,但是反射机制在性能上表现不佳,一个优化是将订阅者的信息生成索引写入一个Java类里。也就是生成一个包含订阅者信息的Java类,运行时就不需要用反射了。这里涉及到注解处理器的概念。EventBus的注解处理器实现在这里。
2.1 生成索引文件
修改build.gradle添加下面的内容:
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.letian.eventbusapplication.MyEventBusIndex' ]
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
首先通过编译参数指定注解处理器的参数是eventBusIndex : 'com.example.letian.eventbusapplication.MyEventBusIndex'
然后在dependencies中通过gradle内置的annotationProcessor指令指定注解处理器: 'org.greenrobot:eventbus-annotation-processor:3.0.1'
。
如此,在编译过程中会生成类com.example.letian.eventbusapplication.MyEventBusIndex
。这个类生成后在不是放在代码目录,而是在build目录里。根据文件名可以搜索到这个文件,这里晒下它的内容:
package com.example.letian.eventbusapplication;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
// 重点在这里
putIndex(new SimpleSubscriberInfo(Example02Fragment.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessage", com.example.letian.eventbusapplication.event.MessageEvent.class,
ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
2.2 使用索引文件
要使用索引文件,就不需要用EventBus.getDefault()
去做注册订阅者、取消注册订阅者、发送消息等事情了。而要使用自定义的EventBus对象。
首先编写类MyEventBus,如下:
package com.example.letian.eventbusapplication;
import org.greenrobot.eventbus.EventBus;
public class MyEventBus {
public final static EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
}
将其他代码里的EventBus.getDefault()
替换成MyEventBus.eventBus
即可。
3. 更多
EventBus支持多种ThreadMode,支持事件优先级,支持sticky模式,支持事件中断,这些内容都可以在简洁易懂的官方教程中找到。
下面的文章值得一读:
- 官方教程
- EventBus3.0详解
- Android事件总线(一)EventBus3.0用法全解析
- Android事件总线(二)EventBus3.0源码解析
- Java注解处理器
- Android注解使用之注解编译android-apt如何切换到annotationProcessor
当然,除了EventBus,事件总线有很多实现,例如otto、guava、AndroidEventBus等。有时间了,搞一把。