EventBus是一个Android端优化的publish/subscribe消息总线,它简化了应用程序内各组件间、组件与后台线程间的通信。比如请求网络,等网络返回时通过Handler或Broadcast通知UI,两个Fragment之间需要通过Listener通信,这些需求都可以通过 EventBus 实现,最常见的就是它可以代替Intent,Handler,BroadCase在Activity,Service,Fragment间传递消息
首先我们来看一张图,图来自github(https://github.com/greenrobot/EventBus/blob/master/EventBus-Publish-Subscribe.png)
我们可以看到Publisher(事件发布)通过post方法将消息发出去,Event事件在接受到会通知事件发布者,然后会在事件发布者中,查找OnEvent函数来执行事件。
说到这里,你一定会想到,这不就是观察者模式。感兴趣的同学可以阅读EventBus的源码,由于本人功力不够,这里了暂且不对源码做分析了。关于EventBus有4个订阅函数:
如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync。
compile ‘org.greenrobot:eventbus:2.4.0’
我们在ListView点击了某一首音乐,在底部播放条需要显示正在播放的节目节目信息,需求就是这么简单。但是由于当前页面是用Listview+gridview布局,点击的是gridview子项的Item。一提到Item,你可能会说这不就是个点击事件嘛,太简单了,写个接口回调不就行了。是的确实如此,但是今天我们将用更简便的方法来实现它。
import java.util.Map;
public class PlayEventMsg {
//这里post的信息为Map类型
Map map;
public PlayEventMsg(Map Map) {
map=Map;
}
public Map getMsg(){
return map;
}
}
//注册订阅者
EventBus.getDefault().register(this);
//这是对应的Adapter的代码
viewholder.gridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
//返回listview的下标
int currpisition=(int) viewholder.gridView.getTag();
Map Map=new HashMap();
Map.put("currpisition", currpisition+"");
Map.put("position", position+"");
//把消息psot出去
EventBus.getDefault().post(new PlayEventMsg(Map));
}
});
// 处理点击事件
public void onEvent(PlayEventMsg msg) {
map = msg.getMsg();
currpisition = Integer.parseInt(map.get("currpisition"));
position = Integer.parseInt(map.get("position"));
}
最后我们根据Listview下标和gridview下标取出自己实体类中的对象在OnEvent事件中改变底部播放条的信息就行了,效果如上,哦,提醒一下,别忘记取消事件
//取消注册订阅者
EventBus.getDefault().unregister(this);
老规矩先贴下效果图,然后再介绍:
这里的需求是在主页面显示歌曲信息然后进入播放器的页面,需要在播放器页面显示正在播放的音乐的信息,然后在播放器页面改变正在播放的音乐的信息,同时回到主页面底部播放条按照要求同样也应该改变播放器的信息。
1.定义Event事件,和上面步骤一样
import java.util.Map;
public class OnBackEventMsg {
Map map;
public OnBackEventMsg(Map Map) {
map=Map;
}
public Map getMsg(){
return map;
}
}
2.注册观察者
//注册订阅者
EventBus.getDefault().register(this);
3.向订阅者发送消息,这里主要看在从播放器页面回到主页面首先我从主页面进入播放器页面,这里很简单直接携带对象到播放器页面就行我们在播放器页面改变的歌曲信息回到主页面,注意回到主页面,我们有两种方法回去,一是直接按back键,二是自己定义的返回按钮。好,接下来我们和上面步骤一样向注册者发送消息
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
然而这却出现了一个问题,我在自己写的返回按钮的点击事件中发送了消息,在主页面却收不到,这样直接导致了播放信息没有改变,通过错误日志,这和Activity的生命周期有关,播放器页面是处于栈顶,按下返回键已经把当前的Activity销毁了,然后定义返回按钮我也是直接finish,为了是防止这页面重新加载一遍,耗费流量。于是我修改了下代码:
我直接把post消息写在OnDestory里 了
@Override
protected void onDestroy() {
Map BackMap = new HashMap();
BackMap.put("Name", MusicName);
BackMap.put("Duration", MusicDuration);
BackMap.put("imgUrl", MusicImgUrl);
SharedPreferenceUtil.set(GaiyaMusic.this, "PlayState", "State", isplay + "");
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
super.onDestroy();
}
4.在主页面的Event函数接受消息
public void onEventMainThread(OnBackEventMsg msg) {
Map bcakmap = msg.getMsg();
controlName = bcakmap.get("Name");
controlDuration = bcakmap.get("Duration");
controlimgUrl = bcakmap.get("imgUrl");
music_durations.setText(TimeSizeChange.generateTime(Long.parseLong(controlDuration)));
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
music_durations.setVisibility(View.GONE);
main_progress.setProgress(0);
currentime.setText("正在加载中...");
}
}, 100);
music_names.setText(controlName);
imgTools.getImgFromNetByUrl(Check.getImg_300_300(controlimgUrl), img_plays, R.drawable.gai);
}
}
5.取消订阅者
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
现在EventBus升级到了3.0.0了,相比2.4.0版本可以不用约定OnEvent方法开头了,我们为其可以添加注解:
//发送消息
Map BackMap = new HashMap();
BackMap.put("Name", MusicName);
BackMap.put("Duration", MusicDuration);
BackMap.put("imgUrl", MusicImgUrl);
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
//接受消息
注解@Subscribe,翻译过来为订阅者,在内部传入了threadMode,我们定义为ThreadMode.MainThread,译为该方法在UI线程完成
@Subscribe(threadMode = ThreadMode.MainThread)
public void MyEventBus(OnBackEventMsg msg){
Map bcakmap = msg.getMsg();
controlName = bcakmap.get("Name");
controlDuration = bcakmap.get("Duration");
controlimgUrl = bcakmap.get("imgUrl");
music_durations.setText(TimeSizeChange.generateTime(Long.parseLong(controlDuration)));
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
music_durations.setVisibility(View.GONE);
main_progress.setProgress(0);
currentime.setText("正在加载中...");
}
}, 100);
music_names.setText(controlName);
imgTools.getImgFromNetByUrl(Check.getImg_300_300(controlimgUrl), img_plays, R.drawable.gai);
}
}
}
好了,EventBus的基本使用就到这里了,由于当中部分内容涉及到公司项目这里就不贴代码了,更多的高级的用法可以阅读EventBus的源码:https://github.com/greenrobot/EventBus