您的位置:首页 > 移动开发 > Android开发

Android开源框架EventBus事件总线详解

2016-02-17 16:45 447 查看

一、    什么是EventBus

EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.。其实就是观察者模式的实现。

EventBus牛逼的优点:是开销小,代码更优雅。以及将发送者和接收者解耦。是一个屌炸天的Android开源框架。

EventBus有如下三个要素:

1、Event:事件

2、Subscriber:事件订阅者,接收特定的事件。

3、Publisher:事件发布者,用于通知Subscriber有事件发生。

其中,Event可以使任意类型对象。Subscriber都是以约定的onEvent开头的函数,具体是 onEvent,onEventMainThread,onEventBackgroundThread,onEventAsync这四个(注意:从3.0.x开始不是必须的。而是用@Subscriber注解来指明执行的线程的。)。 Publisher可以通过post(Object)在任意线程任意位置发送事件。



二、    使用EventBus

首先需要在工程中添加依赖:(目前最新版本是3.0.0)

compile 'org.greenrobot:eventbus:3.0.0'

 

3步使用EventBus:

1、        定义事件

public class MessageEvent {/*
添加需要的成员变量*/ }


 


2、        创建订阅者:

在onCreate或者构造方法中注册订阅者

eventBus.register(this);


实现订阅方法:

@Subscribe

public voidonEvent(AnyEventType event) {/* Do something */};


 


3、        发送事件

eventBus.post(event);


三、    第一个EventBus案例

案例:MainActivity中有一个Button和一个TextView,当点击Button的时候跳转到第二个Activity,第二个Activity中只有一个按钮,点击按钮给MainActivity发送信息,当MainActivity接收到信息之后,把收到的信息内容在TextView中展示。

 

MainActivity布局:

<?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">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startActivity"
        android:text="打开第二个Activity" />

    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

 

SecondActivity布局:

 

<?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">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="postMsg"
        android:text="给第一个Activity发送信息" />
</LinearLayout>

 

重点来了:

 

1、创建消息事件类

package com.qianfeng.eventbusdemo;

/**
 * 事件是简单的java对象(POJO---plain old Java object),
 * 没有任何特殊需求
 */
public class MessageEvent{
    private String message;

    public MessageEvent(String message){
        this.message = message;
    }

    public String getMessage(){
        return message;
    }
}

 

2、在接收消息的页面中注册EventBus。因为我们本例是在Activity中接收消息,所以在Activity中注册。

 

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //获取默认的EventBus对象(单例),并把当前对象注册为Subscriber。
    //注意:当销毁当前实例的时候必须注销这个Subscriber。一般与Activity的生命周期绑定
    EventBus.getDefault().register(this);

    content = (TextView) findViewById(R.id.content);
}

 

@Override
protected void onDestroy(){
    super.onDestroy();
    //注销当前Subscriber
    EventBus.getDefault().unregister(this);
}

 

3、在SecondActivity中发送消息。

 

/**
 * 给第一个Activity发送信息
 * @param view
 */
public void postMsg(View view){
    EventBus.getDefault().post(new MessageEvent("这个是第二个Activity发出的消息"));
}

 

4、在MainActivity中接收消息

 

/*
   如果发送的消息类型是MessageEvent类型,则此方法会被回调。
   这种方法被称为订阅方法或者订阅函数。
   注意:从3.0.0版本开始,订阅方法必须添加注解@Subscribe

   该方法的参数类型是MessageEvent类型,所以只要发送的消息的类型是MessageEvent
   则不管方法名是什么都会回调。

   换句话说:订阅方法的回调只和参数类型有关,与方法名无关。
  
   但是出于可读性考虑,订阅方法一般用onXxxEvent来命名。
 */
@Subscribe
public void onEvent(MessageEvent event){
    Log.d("MainActivity", "onEvent....");
    content.setText(event.getMessage() + "hahahahah");
}


四、    EventBus深入

4.1       线程模式

考虑一个问题:我们的订阅方法是在哪个线程中运行的?能不能指定订阅方法的线程。

很多时候我们需要一些代码在UI线程中执行,有些时候我们又需要一些代码在非UI线程中执行。这些问题,EventBus都做了考虑,使用EventBus可以非常优雅的满足我们的需要。如果实现?这就用到了一个注解:@Subscribe

 

先看@Subscribe这个注解的定义:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

分析:

第一个属性是ThreadMode类型,ThreadMode是一个枚举类型,这个属性就决定了订阅方法到底应该允许在什么样的线程中。

ThreadMode共有4个值:

public enum ThreadMode {
    POSTING,
    MAIN,
    BACKGROUND,
ASYNC
}

四个值的含义如下:

POSTING: 表示订阅方法在发送消息所在的线程中运行。并且他也是默认值。

MAIN:表示订阅放在在主线程也就是UI线程中执行。

BACKGROUND:表示订阅方法会在子线程中运行。补充:如果在主线程中发送消息,则EventBus会开启一个新的线程去执行订阅方法。如果发送消息本身已经是在某个子线程中,则EventBus不会开启新的线程,直接在发送消息的线程中执行订阅方法。

ASYNC:异步模式。总是开启一个新的线程去回调订阅方法。

 

注意:如果发送线程和订阅方法所在线程在同一个线程的时候,当消息发送结束(即:post方法执行完),立即执行订阅方法,订阅方法执行结束后再去执行发送方其余的代码。 所以在涉及到UI线程的时候,务必小心不要阻塞UI线程。

 

 

再思考一个问题:如果有两个(或多个)订阅方法,并且他们的参数是一样的(接收同样的消息)。那么这两个订阅方法怎么执行?是都执行还是执行其中一个?如果都执行执行顺序是怎样的?

 

1、只要参数能够对应上,则两个或多个订阅方法都会执行。

2、如果多个订阅方法需要在同一个线程中执行(同步执行),则执行顺序是按照方法名的字母顺序来执行。

例如下面两个方法,则一定是先执行a方法,再执行b方法。

   @Subscribe(threadMode= ThreadMode.POSTING)

   public void a(MessageEvent event){

       

    }

 

   @Subscribe(threadMode = ThreadMode.POSTING)

   public void b(MessageEvent event){

 

    }

4.1       Sticky Event(粘性事件)

我们用post()发送的信息是一种实时的,也就是说只有发送信息前注册的Subscriber的订阅方法才可以收到,发送之后再注册Subscriber的订阅方法是无法收到的。

但是,有时候我们需要保存最后一个信息,比如手机的当前位置信息。如果我们自己手动保存相对比较麻烦,EventBus的Sticky  Event则帮助我们解决了这个问题。

使用Sticky Event则可以非常简介的帮助我们保存最后一个信息(某个类型),并且方便的获取。而且我们也快手动获取最后一个粘性事件(不用在订阅方法中获取)

 

1、发送Sticky Event

EventBus.getDefault().postSticky(new MessageEvent("这个是发送的粘性事件" + i++));

2、接收Sticky Event

回头看注解@Subscribe注解,第二个属性sticky就表明这个订阅方法是否处理sticky event。默认是false不处理。 

@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
public void onEvent(MessageEvent event){
    Toast.makeText(this, "虽说我启动的晚,但是我也能收到信息:" + event.getMessage(), Toast.LENGTH_SHORT).show();
}

 

注意:如果fasle并不是代表如果发送的是粘性事件这个订阅方法就收不到这个事件,只是表示如果发送粘性服务前没有注册,那么这个订阅方法就再也不会收到这次这个事件了。换句话说:false的时候,对这个订阅方法来说,sticky event和普通的event的没有任何区别。

 

代码片段:

public void postMsg(View view){
    EventBus.getDefault().postSticky(new MessageEvent("这个是发送的粘性事件" + i++));
    Log.d("SecondActivity", "发送方所在线程:" + Thread.currentThread().getName());
    Intent intent = new Intent(this, ThirdActivity.class);
    startActivity(intent);
}

 

 

另外:我们可以在任何地方手动获取某个类型的最后一个sticky event

public void get(View view){
    MessageEvent stickyEvent =

EventBus.getDefault().getStickyEvent(MessageEvent.class);
    Toast.makeText(this, "我是手动获取的粘性事件" + stickyEvent.getMessage(),

Toast.LENGTH_SHORT).show();
}

 

也可以删除某个类型的粘性事件,删除的同时会返回这个粘性事件。

MessageEvent stickyEvent =EventBus.getDefault().removeStickyEvent(MessageEvent.class);

// Better check that an event was actually posted before

if(stickyEvent !=null) {

    // Now do something with it

}

注意:如果没有粘性事件,上面两个方法都会返回null。所以使用的时候最好做非空判断。

4.3    Event优先级和取消Event

参考官方文档:

http://greenrobot.org/eventbus/documentation/priorities-and-event-cancellation/

 

其他一些不太核心的特点均可以参考官方文档。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: