EventBus的简单使用
github地址
EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内组件间、组件与后台线程间的通信
使用之前先添加依赖:
compile 'org.greenrobot:eventbus:3.0.0'//事件总线
定义消息实体类:
/** * 消息实体类 注意:传基本数据类型的值,事件函数的参数类型都是对应的包装类,如果你写成基本类型,就执行不到了 */ public class MyMessageEvent { public String message; public MyMessageEvent(String message) { this.message = message; } }
代码如下:
import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EventBus.getDefault().register(this);//注册监听 findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送事件:只要接受者的方法的参数 与 post提交的参数一致,就可以收到消息(结论) EventBus.getDefault().post(new MyMessageEvent("大家好!我是消息")); } }); findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,MainActivity2.class)); } }); } /** * 接收事件的方法 * 注意:方法名随便写,因为是根据参数来判断接收者的 * * @Subscribe : 该注解,代表该方法订阅Event发出来的事件 (不然怎么它知道 哪些方法是接收方呢) * 指定该方法执行的线程: * threadMode = ThreadMode.MAIN:不论事件是在哪个线程中发布出来的,该方法都会在UI线程中运行 * ThreadMode.POSTING:发布事件和接收事件线程在同一个线程执行 (默认模式) * ThreadMode.BACKGROUND:如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。 * ThreadMode.ASYNC:无论事件在哪个线程发布,都会创建新的子线程再执行 */ @Subscribe(threadMode = ThreadMode.MAIN) public void receiveEventMethod(MyMessageEvent event){ Log.e("tag","我是第一个界面--" + event.message + ",当前线程:" + Thread.currentThread().getName()); } /** * 当然,你也可以在onStart()方法注册,在onStop()方法移除 */ @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this);//移除监听 } }
说明:当我点击按钮执行发送事件,就会执行receiveEventMethod()方法
到这一步:事件的发送与接收就已经清楚了,现在测试,我在另一个界面发送事件,在MainActivity里能接收到吗?
说明:可以自己测试,效果如上,总之只要知道上面的结论就行了
到这里,你可能就有疑问了,参数一致,就都可以得到执行,但我想指定方法,只想其中一个方法执行,那又咋搞呢?
由于EventBus的设计就像观察者模式,它只管该执行怎么的方法,不管你的业务需求,不然咋叫消息总线呢,这样也违背了它的初衷吧。如果硬要执行指定的方法,就实现不了了吗?
我们先稍微修改下上面的消息实体类:
/** * 消息实体类 */ public class MyMessageEvent { public String message; public String tag;//消息标记 public MyMessageEvent(String message) { this.message = message; } public MyMessageEvent(String message, String tag) { this.message = message; this.tag = tag; } }
然后在发送消息的时候设置tag:
EventBus.getDefault().post(new MyMessageEvent("大家好!我是消息", MainActivity2.class.getName()));
现在就只需要在接收的方法中加个判断了
@Subscribe(threadMode = ThreadMode.MAIN) public void receiveEventMethod(MyMessageEvent event){ //如果 tag一致 就说明希望执行该方法 if(MainActivity.this.getClass().getName().equals(event.tag)){ Log.e("tag","tag一致:" + event.message); }else{ Log.e("tag","tag不一致"); } }
这样,也就实现了想执行哪个类的接收方法就能执行哪个类的接收方法了
注意点:
由于注册多次会报错,所以你也可以这样写
if(!EventBus.getDefault().isRegistered(MainActivity.this)){ EventBus.getDefault().register(MainActivity.this);//没有注册才注册监听 同理 反注册也一样可加判断 }else{ Toast.makeText(MainActivity.this, "已经注册了,再注册会报错", Toast.LENGTH_SHORT).show(); }
上面讲的都是先注册了才能收到事件,下面介绍发送粘性事件(发送粘性事件后,再去注册也能收到参数一致的粘性事件,即 先发送,后注册再接收)
第一个界面:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送事件,只要接受者的方法的参数 与 post提交的参数一致,就可以收到消息 EventBus.getDefault().post(new MyMessageEvent("大家好!我是消息")); } }); findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,MainActivity2.class)); } }); findViewById(R.id.btn_3).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(!EventBus.getDefault().isRegistered(MainActivity.this)){ EventBus.getDefault().register(MainActivity.this);//没有注册才注册监听 }else{ Toast.makeText(MainActivity.this, "已经注册了,再注册会报错", Toast.LENGTH_SHORT).show(); } } }); } @Subscribe(threadMode = ThreadMode.ASYNC)//非粘性事件 public void receiveEventMethod(MyMessageEvent event){ Log.e("tag","我是第一个界面的普通事件--" + event.message + ",当前线程:" + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.ASYNC ,sticky = true)//粘性事件 public void receiveStickyEventMethod(MyMessageEvent event){ Log.e("tag","我是第一个界面的粘性事件--" + event.message + ",当前线程:" + Thread.currentThread().getName()); } /** * 当然,你也可以在onStart()方法注册,在onStop()方法移除 */ @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this);//移除监听 } }
第二个界面:
/** * 第二个界面 */ public class MainActivity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); EventBus.getDefault().register(this);//注册监听 findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送粘性事件 EventBus.getDefault().postSticky(new MyMessageEvent("粘性事件")); //移除粘性事件 MyMessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MyMessageEvent.class); if(stickyEvent != null){ EventBus.getDefault().removeStickyEvent(stickyEvent);//移除 Toast.makeText(MainActivity2.this, "移除了粘性事件", Toast.LENGTH_SHORT).show(); } } }); } @Subscribe(threadMode = ThreadMode.MAIN) public void receiveEventMethod(MyMessageEvent event){ Log.e("tag","我是第二个界面的普通事件--" + event.message + ",当前线程:" + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)//sticky 代表粘性事件 public void receiveStickyEventMethod(MyMessageEvent event){ Log.e("tag","我是第二个界面的粘性事件--" + event.message + ",当前线程:" + Thread.currentThread().getName()); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this);//移除监听 } }
第一个界面 第二个界面
当我点击第一个界面的“跳转到第二个ACTIVITY”按钮(第一个界面还没注册监听),然后点击第二个界面的“发送事件”按钮,效果如下:
如果我先在第一个界面注册,再点击第二个界面的发送事件按钮,日志如下:
结论1:发送粘性事件:如果发送之前注册了监听,那么不论是普通接受者,还是粘性事件接受者,都可以得到执行
当我点击第一个界面的“跳转到第二个ACTIVITY”按钮(第一个界面还没注册监听),然后点击第二个界面的“发送事件”按钮(注意:我把点击事件里移除事件的方法去掉了),再返回到第一个界面点击“注册监听”,效果如下:
结论2:发送粘性事件:如果发送粘性事件之后,再去注册事件的,只会执行接收粘性事件的方法,普通接收者方法不会执行
注意点:如果粘性事件不移除,当你再次注册的时候,还会接受到该粘性事件,所以最好还是接收到就移除
缺陷
1.反射,性能打折,效率低
2.代码维护困难(找不到接收方)
3.数据无法返回,单向传送
使用非粘性事件需要注意的问题:
1.如果是activity与Activity之间传递数据,当activity由于内存不足被销毁了后就接收不到事件了
2.如果是fragment与fragment所在的activity传递数据,肯定不会出现接收不到事件的情况(因为fragment存活,activity肯定也是存活的)
3.fragment与fragment通信,如果都属于同一个activity,那么肯定也不会出现接收不到事件的情况,所以很适合属于同一个activity的fragment之间进行数据传递
记录:
在父类注册,子类也可以收到事件,但是父类或者是子类必须有接收事件的方法