您的位置:首页 > 其它

使用EventBus进行组件间通讯

2015-12-24 11:30 393 查看
你是否已经厌倦了在两个Fragment之间传递数据,是否已经厌倦了Service与Activity/Fragment之间的通讯,是否已经厌倦了startActivityForResult中的requestCode, resultCode. 快来试试EventBus吧~

EventBus看名字可以理解成是一个传递事件的公交车,实际上,EventBus也的确是做这个事情。使用的时候,在合适的地方发布一个事件,注册了该事件接收的对象就可以收到通知,进行相应的操作。

EventBus能够很方便的解决上面遇到的麻烦,可以让代码看起来更简洁,写起来更方便,自然也就更少机会的犯错。第一眼看到EventBus的那种解放感已经可以和ButterKnife比肩了。

基本使用,组件之间通讯

这里假设一个上传文件的场景,有一个上传文件的Service: FileUploadService;以及一个显示进度的UI: UploadFragment;以及一个用来传递上传进度的Event: UploadProgressEvent.

先从最简单的UploadProgressEvent看,EventBus并不要求Event对象实现什么接口或者继承什么父类,Event对象可以是任意的POJO. 我们这里用到的UploadProgressEvent只需要有一个progress字段就好了,来标识当前上传的进度,取值范围0-100.

public class UploadProgressEvent {
private int progress;

public int getProgress() {
return progress;
}

public void setProgress(int progress) {
this.progress = progress;
}
}

0
1
2
3
4
5
6
7
8
9
10
11
12

 
public
class UploadProgressEvent
{
    private
int progress;
 
    public
int getProgress()
{
        return
progress;
    }
 
    public
void setProgress(int
progress)
{
        this.progress
= progress;
    }
}
 

再看一下一个模拟上传的FileUploadService,继承自IntentService,可以默认在新的线程运行,并且可以自动结束。

public class FileUploadService extends IntentService {
private UploadProgressEvent uploadProgressEvent;

public FileUploadService(String name) {
super(name);
uploadProgressEvent = new UploadProgressEvent();
}

@Override
protected void onHandleIntent(Intent intent) {

IO.putFile(this, UP_TOKEN, key, uri, extra, new JSONObjectRet() {
@Override
public void onProcess(long current, long total) {
int progress = (int) (current * 100 / total);
uploadProgressEvent.setProgress(progress);
EventBus.getDefault().post(uploadProgressEvent);
}
});
}
}

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

 
public
class FileUploadService
extends IntentService
{
    private
UploadProgressEvent uploadProgressEvent;
 
    public
FileUploadService(String
name)
{
        super(name);
        uploadProgressEvent
= new
UploadProgressEvent();
    }
 
    @Override
    protected
void onHandleIntent(Intent
intent)
{
    …
        IO.putFile(this,
UP_TOKEN,
key,
uri,
extra,
new JSONObjectRet()
{
            @Override
            public
void onProcess(long
current,
long total)
{
                int
progress =
(int)
(current *
100 /
total);
                uploadProgressEvent.setProgress(progress);
                EventBus.getDefault().post(uploadProgressEvent);
            }
        });
    }
}
 

上面可以看出,每次得到onProgress时候,都会设置一下最新的progress数据,发布一个uploadProgressEvent对象到bus. bus则会很勤快的载着我们的Event到它想去的地方。当然是对应的现实UI的UploadFragment.

public class UploadFragment extends BaseFragment{

@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}

public void onEvent(UploadProgressEvent event) {
int progress = event.getProgress();
// TODO 更新UI
}
}

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

 
public
class UploadFragment
extends BaseFragment{
 
    @Override
    public
void onStart()
{
        super.onStart();
        EventBus.getDefault().register(this);
    }
 
    @Override
    public
void onStop()
{
        super.onStop();
        EventBus.getDefault().unregister(this);
    }
 
    public
void onEvent(UploadProgressEvent
event)
{
        int
progress =
event.getProgress();
        // TODO 更新UI
    }
}
 

在Fragment里面的onStart方法和onStop方法分别进行了EventBus的注册与撤销注册。值得注意的是onEvent方法,该方法接受一个UploadProgressEvent参数。EventBus正是通过这个方法来处理UploadProgressEvent事件的。

看了上面的实例是不是很方便?简单来说,就是A组件与B组件,B组件注册了EventBus之后,A组件发生了事件X,则会通知响应的注册了EventBus的B组件的onEvent(X)的方法处理,so easy~

指定处理线程

默认情况下,onEvent方法会在post(Event)的方法的线程中执行,但是可能会post(Event)是一个非UI线程,而onEvent方法需要更新UI,需要在主线程运行。这时候可以写一个post(Runnable)啥啥啥。但是EventBus提供了一个更简洁的处理方式。

public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}

0
1
2
3
4

 
public
void onEventMainThread(MessageEvent
event)
{
    textField.setText(event.message);
}
 

直接使用onEventMainThread方法来处理响应的Event,声明成onEventMainThread的方法会在主机线程运行,这样就可以肆无忌惮的修改UI了。

与onEventMainThread对应的还有onEventBackgroundThread,顾名思义,该方法会在一个Background线程执行,但是这个Background线程是怎么定义的呢?其实Background是指非UI线程,也就是说post(Event)如果是UI线程,那么,onEventBackgroundThread会在另外一个非UI线程执行;如果post(Event)是在一个非UI线程,那么onEventBackgroundThread则和onEvent方法一样,直接在当前线程执行。

除onEvent和onEventBackgroundThread之外,还有一个onEventAsync方法,该方法更简单,不管post(Event)是否是UI线程,onEventAsync都会在一个新的线程执行。

使用Sticky Events

某些时候,我们并不希望发布出来的Event立即被消费掉,而是等到时机成熟。比如说,在一个详情页点赞之后,产生一个VoteEvent,VoteEvent并不立即被消费,而是等用户退出详情页回到商品列表之后,接收到该事件,然后刷新Adapter等。其实这就是之前我们用startActivityForResult和onActivityResult做的事情。

那么怎么用EventBus来模拟这样的情况呢?这就需要另外一个强大的机制,Sticky Event.

和普通的Event不同,当发布一个Sticky Event,需要使用postSticky方法:

EventBus.getDefault().postSticky(new VoteEvent(obj));

0
1
2

 
EventBus.getDefault().postSticky(new
VoteEvent(obj));
 

上面的代码发布了一个点赞的Sticky Event.

@Override
public void onStart() {
super.onStart();
EventBus.getDefault().registerSticky(this);
}

public void onEventMainThread(VoteEvent event) {
// TODO update adapter
}

@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

 
@Override
public
void onStart()
{
    super.onStart();
    EventBus.getDefault().registerSticky(this);
}
 
public
void onEventMainThread(VoteEvent
event)
{
    // TODO update adapter
}
 
@Override
public
void onStop()
{
    EventBus.getDefault().unregister(this);
    super.onStop();
}
 

上面的代码则是在对应的列表页面,onStart方法调用了EventBus的registerSticky方法,该方法在每次执行的时候,都回去查询一次Sticky Event,并调用响应的方法处理。本例中,当列表页面第一次显示的时候,是没有Sticky Event的,所以registerSticky不会触发任何事件;而当列表页面是由详情页退出后显示出来的话,registerSticky会触发在详情页postSticky的VoteEvent,而触发onEventMainThread(VoteEvent)方法,以达到onActivityResult方法的效果。

除了registerSticky方法来触发Sticky Event之外,我们还可以通过getStickyEvent方法来获取响应的Sticky Event来进行处理。比如:

@Override
public void onResume() {
super.onResume();
VoteEvent voteEvent = EventBus.getDefault().getStickyEvent(VoteEvent.class);
// TODO update adapter if vote event is not null.
}

0
1
2
3
4
5
6
7

 
@Override
public
void onResume()
{
    super.onResume();
    VoteEvent
voteEvent =
EventBus.getDefault().getStickyEvent(VoteEvent.class);
    // TODO update adapter if vote event is not null.
}
 

我们可以在每次的onResume方法里面检查EventBus里面是否有VoteEvent的Sticky Event,从而进行处理。

总之,EventBus极大的减轻了我们各种组件之间的通讯复杂度,也大大减少了使用Activity Result的麻烦。真实值得极力推荐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: