1.什么是EventBus?
EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。
传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。
详细的解释以及图片显示请看
http://www.trinea.cn/android/eventbus-source-analysis/
使用方法,我们首先要去下载源代码
https://github.com/greenrobot/EventBus
然后copy上面划线的包到我们的工程中,然后就可以使用了:
示例code使用到的函数:
**EventBus.getDefault().register(this);//订阅事件
EventBus.getDefault().post(object);//发布事件
EventBus.getDefault().unregister(this);//取消订阅**
EventBus主要特点
1. 事件订阅函数不是基于注解(Annotation)的,而是基于命名约定的,在Android 4.0之前的版本中,注解解析起来比较慢 , 事件响应函数默认以“onEvent”开始,可以在EventBus中修改这个值,但是不推荐这么干
2. 事件响应有更多的线程选择
EventBus可以向不同的线程中发布事件,在ThreadMode 枚举中定义了4个线程,只需要在事件响应函数名称“onEvent”后面添加对应的线程类型名称,则还事件响应函数就会在对应的线程中执行,比如事件函数“onEventAsync”就会在另外一个异步线程中执行,ThreadMode定义的4个线程类型如下:
PostThread:事件响应函数和事件发布在同一线程中执行。这个是默认值,这样可以避免线程切换。
MainThread:事件响应函数会在Android应用的主线程(大部分情况下都是UI线程)中执行。
BackgroundThread:事件响应函数会在一个后台线程中执行。如果事件发布函数不是在主线程中,则会立即在事件发布线程中执行响应函数。如果事件发布函数在主线程中,EventBus则会在唯一的一个后台线程中按照顺序来执行所有的后台事件响应函数。
上面的3种事件响应函数,应该能够很快的执行完,不然的话会阻塞各自的事件发布。
async:事件响应函数在另外一个异步线程中执行。该线程和发布线程、主线程相互独立。如果事件响应函数需要较长的时间来执行,则应该使用该模式,例如 网络访问等。需要注意的是,由于系统并行的限制,应该避免在同一时间触发大量的异步线程。 EventBus使用一个线程池来提高线程的效率。
下面来看一下具体的实例code(参考hongyang的code):
首先是我们的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:baselineAligned="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="middle"
>
<fragment
android:id="@+id/fragmentTitle"
android:name="com.example.eventbusdemo.ItemListFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
/>
<fragment
android:id="@+id/fragmentContent"
android:name="com.example.eventbusdemo.ItemDetail"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
/>
</LinearLayout>
里面就两个fragment
content的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mytext"
android:textSize="30sp"
android:gravity="center"
/>
</LinearLayout>
然后是我们的主code:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
发现什么都没有,因为我们的列表code和我们的内容code分别是在两个Fragment文件中,那么先来看一下listView中的显示code:
package com.example.eventbusdemo;
import java.util.ArrayList;
import de.greenrobot.event.EventBus;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
//通过EventBus的调用,我们知道首先这个是一个单例模式的类,getDefault是我们的对象获取的一个静态函数
//EventBus.getDefault().register(this);
public class ItemListFragment extends ListFragment{
private ArrayList<String> datas = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
initDatas();
EventBus.getDefault().post("this is a test0");
EventBus.getDefault().post("this is a test1");
EventBus.getDefault().post("this is a test2");
EventBus.getDefault().post("this is a test3");
EventBus.getDefault().post("this is a tes4");
EventBus.getDefault().post("this is a test5");
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
EventBus.getDefault().post(datas);
System.out.println("post Threadid ="+Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
public void onDestroyView() {
super.onDestroyView();
EventBus.getDefault().unregister(this);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
ItemContent itemContent = new ItemContent();
itemContent.setContent(getListView().getItemAtPosition(position)+" Content");
EventBus.getDefault().post(itemContent);
}
//首先我们要明白一点的是onEventMainThread中的参数是和我们post对应起来的,例如,我们上面post了一个ArrayList<String>,要想我们的这个函数work,必须给相同的参数类型,不然是关联不到这个函数的
//其次,如果我们在ItemDetail和ItemListFragment中的这两个函数给的都是一样的值,例如Object 或者String,那么每当我们点击的时候两个类中的这个函数都会被调用,其实这个不是我们想要的结果,
//因此我们得出结论,onEventMainThread函数到底调不调用或者由谁来调用,完全看其参数是不是与post传值对应,如果是就被调用
public void onEventMainThread(ArrayList<String> event)
{
System.out.println("onEventMainThread in list call ThreadId = "+Thread.currentThread().getId());
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, event));
}
//onEvent 它和ThreadModel中的PostThread对应,这个也是默认的类型,当使用这种类型时,回调函数和发起事件的函数会在同一线程中执行
//因此我们可以看到虽然我们的函数是写在主线程中的,但是里面code的执行其实是在post线程中执行的
public void onEvent(ArrayList<String> event)
{
System.out.println("post is in ChildThread,onEventPostThread and threadId ="+Thread.currentThread().getId());
}
//特点是如果我们的post线程是主线程,那么它将会另起一个新的线程,如果post是子线程,那么直接在子线程中执行操作
//这个函数中执行的操作确保不会导致主线程超时
//另外这里面如果我们在主线程post很多个事件,那么BackgroundThread会一个一个执行,并且用的是线程池中的单一线程,也就是说里面所有的操作都是同一个线程ID
public void onEventBackgroundThread(ArrayList<String> event)
{
System.out.println("post is in ChildThread,onEventBackgroundThread and threadId ="+Thread.currentThread().getId());
}
public void onEventBackgroundThread(String item)
{
System.out.println("onEventBackgroundThread item="+item+"and threadId ="+Thread.currentThread().getId());
}
//如果post执行的是在主线程中,我们会new出来一个新的线程,每次都是新的,如果是在子线程中,那么我们会用一个线程池来调用,并且线程池中的线程id是包含了BackgroundThread的,并不会使用池中单一的线程
//使用线程池而不是线程,是Async与BackgroundThread的重要区别
//我们用Object可以接受任何post事件
public void onEventAsync(Object item)
{
System.out.println("onEventAsync item="+item+"and threadId ="+Thread.currentThread().getId());
}
private void initDatas()
{
for(int i=0;i<5;++i)
{
datas.add("item"+i);
}
}
}
在里面我们把EventBus提供的4中模式都实现了一下,然后对比了一下他们之间的区别,然后显示一下我们总的打印结果:
相关特点与注意事项我们在code中已经提出来了。
然后在看一下Content部分的fragment:
package com.example.eventbusdemo;
import de.greenrobot.event.EventBus;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ItemDetail extends Fragment{
private TextView mytext;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root =inflater.inflate(R.layout.item_detail, container,false);
mytext = (TextView) root.findViewById(R.id.mytext);
return root;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public void onEventMainThread(ItemContent item)
{
System.out.println("onEventMainThread-----content="+item.getContent());
if(item!=null )
mytext.setText(item.getContent());
}
}
这里面就做了一个点击的时候显示不同的item,其他的没有,为什么二回有一个ItemContent封装呢?
这个是因为我们的EventBus区别的时候是直接拿着class比对的,如果满足,就调用,例如我post一个String,那么怎么区分调用的是哪个类里面的onEventMainThread这就很重要了,因此我们封装了一下String,不信看code:
package com.example.eventbusdemo;
public class ItemContent {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
EventBus简单的使用方法就到这里了,特别注意的Async 和 BackgroundThread的区别,前者是用的线程池中多个线程,后者使用的是线程池中的单一线程
代码下载