Android EventBus3.0实例使用详解及封装调用

EventBus是一款Android下的发布/订阅事件总线机制。可以代替Intent、Handler、Broadcast等在Fragment、Activity之间传递消息。

优点:开销小,代码优雅。将发送者和接受者解耦。

3.0与2.x的区别

主要区别在订阅函数的不同

EventBus2.x中只暴露了四个方法供用户调用,分别是

  • onEvent:该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
  • onEventMainThread:不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
  • onEventBackgroundThread:如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
  • onEventAsync:无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

EventBus3.0中必须使用注解,例如:

@Subscribe(threadMode = ThreadMode.Async, sticky = true, priority = 1)
public void firstEvent(FirstEvent event) {
    Log.e("TAG", "Async" + Thread.currentThread().getName());
}

好处在于订阅函数可以随便起名字,其他与2.x没什么不同。
这里Subscribe中的key需要解释一下含义,Subscribe中可以传三种值:

  • ThreadMode:这是个枚举,有四个值,决定订阅函数在哪个线程执行

    • PostThread:事件发送者在哪个线程就执行在哪个线程。同2.x中的onEvent方法,默认值就是这个
    • MainThread:订阅函数一定执行在主线程。同onEventMainThread方法
    • BackgroundThread:如果是事件从子线程发出,订阅函数就执行在那个子线程,不会创建新的子线程;如果主线程发出事件,则创建子线程。同onEventBackgroundThread方法
    • Async:一定创建子线程。同onEventAsync方法。
  • sticky:默认为false,如果为true,当通过postSticky发送一个事件时,这个类型的事件的最后一次事件会被缓存起来,当有订阅者注册时,会把之前缓存起来的这个事件直接发送给它。使用在比如事件发送者先启动了,订阅者还没启动的情况。

  • priority:默认值为0。订阅了同一个事件的订阅函数,在ThreadMode值相同的前提下,收到事件的优先级。

Configuration用法:

当默认的EventBus不足以满足需求时,EventBusBuilder就上场了,EventBusBuilder允许配置各种需求的EventBus

例如1:
当没有subscribers的时候,eventbus保持静默:
EventBus eventBus = EventBus.builder().logNoSubscriberMessages(false)
    .sendNoSubscriberEvent(false).build();

例如2:
配置异常:
默认情况下:eventbus捕获onevent抛出的异常,并且发送一个SubscriberExceptionEvent 可能不必处理

EventBus eventBus = EventBus.builder().throwSubscriberException(true).build();

配置默认的eventbus单例

官方推荐:在application类中,配置eventbus单例,保证eventbus的统一

例如:配置eventbus 只在DEBUG模式下,抛出异常,便于自测,同时又不会导致release环境的app崩溃
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();

Sticky Events用法:

前面说的一般的post event,正常流程,必须是先register,然后post event,handle event才能起作用。但是当我们post event时,还没有subcriber怎么办?但是又想后注册的subscriber,能收到消息,这时sticky event就开始大显身手了。

Warning:EventBus会把最后一个sticky event记录在内存中,然后分发给subscriber

来看下官方的Demo

1 post sticky event:

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

2 启动一个新的Activity,准备subscriber,注册后,
所有的sticky subscriber的回调方法,会马上获得之前的sticky event

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

//注意这里sticky=true
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
    // UI updates must run on MainThread
    textField.setText(event.message);
}

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

Getting and Removing sticky Events manually

如上所述,当subscriber注册后,最后一个sticky event会自动匹配。但是,有些时候,主动去检查sticky event会更方便,并且 sticky event 需要remove,阻断继续传递。

官方demo

//返回的是之前的sticky event
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // Now do something with it
}

2中sticky event使用方法都可行,看业务需要和个人习惯,选择合适的使用方法

Priorities and Event Cancellation用法:

大多数情况下,eventbus不需要改变线程优先级,或者取消event,但是不排除少数情况,需要改变线程优先级。

Subscriber Priorities

官方demo

//priority默认为0,不同ThreadMode下的分发流程不受优先级的影响
//优先级高的可以优先获得消息
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
…
}

Cancelling event delivery

如果,不希望后续的subcriber收到消息,可以在收到消息后,调用cancelEventDelivery(Object event) 取消消息的后续传递。

官方demo

@Subscribe
public void onEvent(MessageEvent event){
// Process the event
…

EventBus.getDefault().cancelEventDelivery(event) ;
}

Note:一般情况下,event的取消发生在高优先级的subscriber


具体使用

创建事件:

public class FirstEvent {
    private String msg;
    public FirstEvent(String msg){
        this.msg = msg;
    }
    public String getMsg(){
        return msg;
    }
}

使用场景一:在Activity之间通信传值

在MainActivity的onCreate方法中注册,onDestroy方法中反注册

EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);

订阅函数,此处用来查看执行在哪个线程(注意,这里的函数名可以任取):

@Subscribe(threadMode = ThreadMode.Async, sticky = true, priority = 1)
public void onAsync(FirstEvent event) {
    Log.e("TAG", "Async: " + Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.BackgroundThread)
public void onBackgroundThread(FirstEvent event) {
    Log.e("TAG", "BackgroundThread: " + Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.MainThread)
public void onMainThread(FirstEvent event) {
    Log.e("TAG", "MainThread: " + Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.PostThread)
public void onPostThread(FirstEvent event) {
    Log.e("TAG", "PostThread: " + Thread.currentThread().getName());
}

在SecondActivity发送事件(主线程中发送):

EventBus.getDefault().post(new FirstEvent("啦啦啦"));

执行结果如下:

Async: pool-1-thread-1
MainThread: main
PostThread: main
BackgroundThread: pool-1-thread-2

如果是在子线程中发送:

new Thread(new Runnable() {

    @Override
    public void run() {
        EventBus.getDefault().post(new FirstEvent("啦啦啦"));
    }
}).start();

执行结果如下:

BackgroundThread: Thread-450
PostThread: Thread-450
Async: pool-1-thread-1
MainThread: main

如果先启动SecondActivity,后启动MainActivity:

@Override
public void onClick(View v) {
    EventBus.getDefault().postSticky(new FirstEvent("啦啦啦"));
    startActivity(new Intent(SecondActivity.this,MainActivity.class));
}

确实只有onAsync方法收到了消息。

至此对EventBus的试验基本就结束了,各位可以自行选择不同的ThreadMode值,让订阅函数执行在不同的线程。根据需要选择是否需要sticky=true。

完整的MainActivity和SecondActivity代码如下(xml文件就不贴了,就一个Button):

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.getDefault().register(this);
        findViewById(R.id.btn).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,
                        SecondActivity.class));
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.Async, sticky = true, priority = 1)
    public void onAsync(FirstEvent event) {
        Log.e("TAG", "Async: " + Thread.currentThread().getName());
    }

    @Subscribe(threadMode = ThreadMode.BackgroundThread)
    public void onBackgroundThread(FirstEvent event) {
        Log.e("TAG", "BackgroundThread: " + Thread.currentThread().getName());
    }

    @Subscribe(threadMode = ThreadMode.MainThread)
    public void onMainThread(FirstEvent event) {
        Log.e("TAG", "MainThread: " + Thread.currentThread().getName());
    }

    @Subscribe(threadMode = ThreadMode.PostThread)
    public void onPostThread(FirstEvent event) {
        Log.e("TAG", "PostThread: " + Thread.currentThread().getName());
    }

}


public class SecondActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        findViewById(R.id.second).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                EventBus.getDefault().postSticky(new FirstEvent("Lai自星星的我"));
                startActivity(new Intent(SecondActivity.this,MainActivity.class));
            }
        });
    }

}
使用场景二:完美替换Handler,在主线程更新UI,如下示例-模拟下载显示进度条
public class MainActivity extends Activity {

    public ProgressBar progressBar = null;
    public int time = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (time<100){
                            time += 15;
                            EventBus.getDefault().post(new TestEvent(time));
                            try {
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        });
        progressBar = (ProgressBar) findViewById(R.id.progressbar);

        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEventMainThread(TestEvent event){
        progressBar.setProgress(event.getMsg());
    }
}

当然EventBus的功能并不局限这两种,还可以替换各种CallBack回调接口、StrartActivityForResult()等功能。
EventBus封装类:
/**
 * @author 
 * @version 1.0
 * @date 2016-3-31 上午10:59:22
 * @describe EventBusUtils工具类
 */
public class EventBusUtils {
    public static void register(Object subscriber) {
        if (!EventBus.getDefault().isRegistered(subscriber)) {
            EventBus.getDefault().register(subscriber);
        }
    }

    public static void unregister(Object subscriber) {
        if(EventBus.getDefault().isRegistered(subscriber)){
            EventBus.getDefault().unregister(subscriber);
        }
    }

    public static void sendEvent(EventBusEvent event) {
        EventBus.getDefault().post(event);
    }

    public static void sendStickyEvent(EventBusEvent event) {
        EventBus.getDefault().postSticky(event);
    }

    /**
     * 通过code码区分事件类型
     */
    public static final class EventCode {
        public static final int A = 0x111111;
        public static final int B = 0x222222;
        public static final int C = 0x333333;
        public static final int D = 0x444444;
        public static final int E = 0x555555;
        // other more
    }
}
/**
 * @author 
 * @version 1.0
 * @date 2017/5/12 17:01
 * @describe EventBus事件类
 *
 * 通过泛型指定事件通信过程中的数据类型,code为事件码,使用的时候给不同的事件类型指定不同的code */
public class EventBusEvent<T> {
    private int code;
    private T data;

    public EventBusEvent(int code) {
        this.code = code;
    }

    public EventBusEvent(int code, T data) {
        this.code = code;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}


最后,使用EventBus框架时候记得添加ProGuard 混淆规则

-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    (java.lang.Throwable);
}

你可能感兴趣的:(框架)