EventBus框架的简单介绍

EventBus(事件总线)

历史背景

它是由开源组织greenrobot贡献的Android事件发布/订阅的框架.

主要功能

通过解耦发布者和订阅者来简化Android中事件的传递(通俗来讲,就是一个人发事件的消息告诉另一个人该做什么,或者是某个人想将数据交给另一个人),这个事件传递既可用于四大组件之间的数据传递(也叫通信),也可以用于异步线程和主线程之间的通信.代替了传统的事件传递方式:Handler , BrocastReceiver , 接口回调,在Fragment , Activity , Service , 线程之间传递数据,执行方法.一句话:任何一个java类通可以用这个通讯

概念

事件(Event)

又可称为消息.其实就是一个对象.该对象可以是任意数据类型,不像Intent,Bundle传递数据一样局限.通常这个对象都是通过自定义一个事件类来实例化的

订阅者(Subscriber)

订阅某种事件类型的对象,也就是订阅接收事件信息的对象.当有发布者发布这类事件后,EventBus会执行订阅者的订阅函数,这个订阅函数俗称事件响应函数.订阅者通过注册(register)接口订阅某个事件类型,unregister进行接口退订.注意:订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为0.

发布者(Publisher)

发布某种事件的对象,通过post接口发布事件.

简单的使用案例

分别创建两个Activity,分别是SubscriberActivity(作为订阅者),PublisherActivity(作为发布者),PublisherActivity发布事件类型给SubscriberActivity接收,也就是发布者发布事件类型给订阅者接收,订阅者在订阅方法中做相应的处理.

创建事件类型,通常一个自定义类,这里我们命名为MyEvent

package jansonwang.example.com.myapplication;

/**
 * Created by Jason Wang on 2017/5/22.
 * 这是一个事件类型,它可以携带各种各样的数据类型
 */
public class MyEvent {
    public String msg;
    public MyEvent(String msg){
        this.msg = msg;
    }
}

创建SubscriberActivity(作为订阅者),并且注册订阅接口,自定义订阅函数,反注册订阅接口

package jansonwang.example.com.myapplication;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class SubscriberActivity extends AppCompatActivity {

    private static final String TAG ="SubscriberActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //EventBus.getDefault();创建单个事件总线对象
        EventBus.getDefault().register(this);  //注册事件总线
    }

    public void start(View view) {
        Intent intent = new Intent(this, PublisherActivity.class);
        startActivity(intent);
    }


    /**
     * 接收发布者的事件消息,
     * ThreadMode.MAIN模式:无论发布者在子线程中还是主线程中发送事件信息,最后订阅者方法都会在主线程中执行,所以这种模式下不能在订阅者方法中进行耗时操作
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void receiveEventOnMain(MyEvent event) {
        Log.d(TAG, "receiveEventOnMain: " + event.msg + "    " + Thread.currentThread().getName());
        Toast.makeText(SubscriberActivity.this, "ThreadMode.MAIN", Toast.LENGTH_SHORT).show();
    }

    /**
     * 接收发布者的事件消息
     *  ThreadMode.POSTING模式:发布者在哪个线程中发布事件消息,订阅者方法就会在哪个线程中执行,所以这个模式下也不能进行耗时操作,会延迟分发
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void receiveEventPosting(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(SubscriberActivity.this, "ThreadMode.POSTING", Toast.LENGTH_SHORT).show();
            }
        });

    }

    /**
     * 接收发布者的事件消息
     *  ThreadMode.ASYNC模式:发布者无论在哪个线程中发布事件消息,订阅者方法都会自己重新创建一个子线程执行
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void receiveEventASYNC(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

        Toast.makeText(SubscriberActivity.this, "ThreadMode.ASYNC", Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     *  接收发布者的事件消息
     *   ThreadMode.BACKGROUND模式:发布者如果在子线程中发布事件消息,订阅者方法就会在同一个子线程中执行;
     *                             发布者如果在主线程中发布事件消息,订阅者方法就会自己创建一个子线程执行
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void receiveEventBACKGROUND(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

        Toast.makeText(SubscriberActivity.this, "ThreadMode.BACKGROUND", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //反注册事件总线
        EventBus.getDefault().unregister(this);
    }


}

创建PublisherActivity(作为发布者),并且发布事件

package jansonwang.example.com.myapplication;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import org.greenrobot.eventbus.EventBus;

/**
 * Created by Jason Wang on 2017/5/22.
 */
public class PublisherActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_publisher);
    }

    /**
     * 在主线程中发布消息
     * @param view
     */
    public void send1(View view) {
        EventBus.getDefault().post(new MyEvent("发布者向订阅者发送事件消息"));
    }

    /**
     * 在子线程中发布消息
     * @param view
     */
    public void send2(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {

        EventBus.getDefault().post(new MyEvent("发布者向订阅者发送事件消息"));
            }
        }).start();
    }
}

*主要步骤:

1.订阅者注册订阅接口

 //EventBus.getDefault();创建单个事件总线对象
        EventBus.getDefault().register(订阅者类);  //注册事件总线

2.在订阅者类中自定义订阅函数(记得在函数头上加引用,例如 @Subscribe(threadMode = ThreadMode.MAIN)),用来接收发布者发布事件类型,

    /**
     * 接收发布者的事件消息,
     * ThreadMode.MAIN模式:无论发布者在子线程中还是主线程中发送事件信息,最后订阅者方法都会在主线程中执行,所以这种模式下不能在订阅者方法中进行耗时操作
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void receiveEventOnMain(MyEvent event) {
        Log.d(TAG, "receiveEventOnMain: " + event.msg + "    " + Thread.currentThread().getName());
        Toast.makeText(SubscriberActivity.this, "ThreadMode.MAIN", Toast.LENGTH_SHORT).show();
    }

    /**
     * 接收发布者的事件消息
     *  ThreadMode.POSTING模式:发布者在哪个线程中发布事件消息,订阅者方法就会在哪个线程中执行,所以这个模式下也不能进行耗时操作,会延迟分发
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void receiveEventPosting(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(SubscriberActivity.this, "ThreadMode.POSTING", Toast.LENGTH_SHORT).show();
            }
        });

    }

    /**
     * 接收发布者的事件消息
     *  ThreadMode.ASYNC模式:发布者无论在哪个线程中发布事件消息,订阅者方法都会自己重新创建一个子线程执行
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void receiveEventASYNC(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

        Toast.makeText(SubscriberActivity.this, "ThreadMode.ASYNC", Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     *  接收发布者的事件消息
     *   ThreadMode.BACKGROUND模式:发布者如果在子线程中发布事件消息,订阅者方法就会在同一个子线程中执行;
     *                             发布者如果在主线程中发布事件消息,订阅者方法就会自己创建一个子线程执行
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void receiveEventBACKGROUND(MyEvent event){
        Log.d(TAG, "receiveEventPosting: " + event.msg + "    " + Thread.currentThread().getName());
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

        Toast.makeText(SubscriberActivity.this, "ThreadMode.BACKGROUND", Toast.LENGTH_SHORT).show();
            }
        });
    }

3.创建一个类,作为要发布的事件类型

package jansonwang.example.com.myapplication;

/**
 * Created by Jason Wang on 2017/5/22.
 * 这是一个事件类型,它可以携带各种各样的数据类型
 */
public class 事件类型 {
    public String msg;
    public MyEvent(String msg){
        this.msg = msg;
    }
}

4.发布者发布事件类型

   EventBus.getDefault().post(事件类型对象);

每个步骤的源码分析

 EventBus.getDefault();//创建事件总线对象

这段代码采用的是单例模式:

```

//两个非空,一个加锁

public static EventBus getDefault() {

if (defaultInstance == null) {
    synchronized (EventBus.class) {
        if (defaultInstance == null) {
            defaultInstance = new EventBus();
        }
    }
}
return defaultInstance;

}

```

下面是优化历史

//简单单例,当多线程时,还是会创建多个示例
public static EventBus getDefault() {
    if (defaultInstance == null) {
        defaultInstance = new EventBus();
    }
    return defaultInstance;
}

//加锁单例,每次调用都检查是否加锁
public static synchronized EventBus getDefault() {
    if (defaultInstance == null) {
        defaultInstance = new EventBus();
    }
    return defaultInstance;
}

//两个非空,一个加锁
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

 EventBus.getDefault().register(this);  //注册事件总线

下面是注册的源码

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    //找到订阅者中所有的订阅方法
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //将订阅方法记录下来保存到对应事件的订阅列表中
            //Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
            subscribe(subscriber, subscriberMethod);
        }
    }
}

 EventBus.getDefault().post(事件类型对象)

下面是发布者发布事件的源码:

public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);.//添加到事件队列

    if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                //发布的单个事件
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}


private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        ........
    } else {
        //发布对应事件类型的事件
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    .........
}

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);//获取对应事件的订阅列表
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        //遍历订阅列表
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                //发布事件到一个订阅
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            }
            ........
        }
        return true;
    }
    ......
}

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    //判断订阅方法的线程模型
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            //POSTING线程模型下,直接反射调用订阅方法
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            //MAIN线程模型
            if (isMainThread) {
                //如果当前是主线程,则直接反射调用订阅方法
                invokeSubscriber(subscription, event);
            } else {
                //如果当前不是主线程,则使用绑定主线程Looper的Handler在主线程调用订阅方法
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case BACKGROUND:
            //BACKGROUND线程模型
            if (isMainThread) {
                //如果当前是主线程,则在EventBus内部的线程池中执行
                backgroundPoster.enqueue(subscription, event);
            } else {
                //如果当前是子线程,则直接在该子线程反射调用订阅方法
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            //ASYNC线程模型
            //直接在EventBus的线程池中执行
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • EventBus是一个 发布/订阅 模式的消息总线库,它简化了应用程序内各组件间、组件与后台线程间的通信,解耦了事...
    hanpfei阅读 4,041评论 0 50
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,364评论 25 707
  • 天呐,知识也需要管理 先唠叨唠叨一下个人的教育与职业背景,以及每个阶段对知识的一个态度。大学之前,大家都差不多,为...
    小披阅读 202评论 0 0
  • The best tools for men are machines. I think it is useful...
    Alice墨尔本阅读 141评论 0 0