EventBus框架的簡單介紹

EventBus(事件總線)

歷史背景

它是由開源組織greenrobot貢獻的Android事件發(fā)布/訂閱的框架.

主要功能

通過解耦發(fā)布者和訂閱者來簡化Android中事件的傳遞(通俗來講,就是一個人發(fā)事件的消息告訴另一個人該做什么,或者是某個人想將數(shù)據(jù)交給另一個人),這個事件傳遞既可用于四大組件之間的數(shù)據(jù)傳遞(也叫通信),也可以用于異步線程和主線程之間的通信.代替了傳統(tǒng)的事件傳遞方式:Handler , BrocastReceiver , 接口回調(diào),在Fragment , Activity , Service , 線程之間傳遞數(shù)據(jù),執(zhí)行方法.一句話:任何一個java類通可以用這個通訊

概念

事件(Event)

又可稱為消息.其實就是一個對象.該對象可以是任意數(shù)據(jù)類型,不像Intent,Bundle傳遞數(shù)據(jù)一樣局限.通常這個對象都是通過自定義一個事件類來實例化的

訂閱者(Subscriber)

訂閱某種事件類型的對象,也就是訂閱接收事件信息的對象.當有發(fā)布者發(fā)布這類事件后,EventBus會執(zhí)行訂閱者的訂閱函數(shù),這個訂閱函數(shù)俗稱事件響應(yīng)函數(shù).訂閱者通過注冊(register)接口訂閱某個事件類型,unregister進行接口退訂.注意:訂閱者存在優(yōu)先級,優(yōu)先級高的訂閱者可以取消事件繼續(xù)向優(yōu)先級低的訂閱者分發(fā),默認所有訂閱者優(yōu)先級都為0.

發(fā)布者(Publisher)

發(fā)布某種事件的對象,通過post接口發(fā)布事件.

簡單的使用案例

分別創(chuàng)建兩個Activity,分別是SubscriberActivity(作為訂閱者),PublisherActivity(作為發(fā)布者),PublisherActivity發(fā)布事件類型給SubscriberActivity接收,也就是發(fā)布者發(fā)布事件類型給訂閱者接收,訂閱者在訂閱方法中做相應(yīng)的處理.

創(chuàng)建事件類型,通常一個自定義類,這里我們命名為MyEvent

package jansonwang.example.com.myapplication;

/**
 * Created by Jason Wang on 2017/5/22.
 * 這是一個事件類型,它可以攜帶各種各樣的數(shù)據(jù)類型
 */
public class MyEvent {
    public String msg;
    public MyEvent(String msg){
        this.msg = msg;
    }
}

創(chuàng)建SubscriberActivity(作為訂閱者),并且注冊訂閱接口,自定義訂閱函數(shù),反注冊訂閱接口

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();創(chuàng)建單個事件總線對象
        EventBus.getDefault().register(this);  //注冊事件總線
    }

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


    /**
     * 接收發(fā)布者的事件消息,
     * ThreadMode.MAIN模式:無論發(fā)布者在子線程中還是主線程中發(fā)送事件信息,最后訂閱者方法都會在主線程中執(zhí)行,所以這種模式下不能在訂閱者方法中進行耗時操作
     */
    @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();
    }

    /**
     * 接收發(fā)布者的事件消息
     *  ThreadMode.POSTING模式:發(fā)布者在哪個線程中發(fā)布事件消息,訂閱者方法就會在哪個線程中執(zhí)行,所以這個模式下也不能進行耗時操作,會延遲分發(fā)
     * @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();
            }
        });

    }

    /**
     * 接收發(fā)布者的事件消息
     *  ThreadMode.ASYNC模式:發(fā)布者無論在哪個線程中發(fā)布事件消息,訂閱者方法都會自己重新創(chuàng)建一個子線程執(zhí)行
     * @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();
            }
        });
    }

    /**
     *  接收發(fā)布者的事件消息
     *   ThreadMode.BACKGROUND模式:發(fā)布者如果在子線程中發(fā)布事件消息,訂閱者方法就會在同一個子線程中執(zhí)行;
     *                             發(fā)布者如果在主線程中發(fā)布事件消息,訂閱者方法就會自己創(chuàng)建一個子線程執(zhí)行
     * @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);
    }


}

創(chuàng)建PublisherActivity(作為發(fā)布者),并且發(fā)布事件

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);
    }

    /**
     * 在主線程中發(fā)布消息
     * @param view
     */
    public void send1(View view) {
        EventBus.getDefault().post(new MyEvent("發(fā)布者向訂閱者發(fā)送事件消息"));
    }

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

        EventBus.getDefault().post(new MyEvent("發(fā)布者向訂閱者發(fā)送事件消息"));
            }
        }).start();
    }
}

*主要步驟:

1.訂閱者注冊訂閱接口

 //EventBus.getDefault();創(chuàng)建單個事件總線對象
        EventBus.getDefault().register(訂閱者類);  //注冊事件總線

2.在訂閱者類中自定義訂閱函數(shù)(記得在函數(shù)頭上加引用,例如 @Subscribe(threadMode = ThreadMode.MAIN)),用來接收發(fā)布者發(fā)布事件類型,

    /**
     * 接收發(fā)布者的事件消息,
     * ThreadMode.MAIN模式:無論發(fā)布者在子線程中還是主線程中發(fā)送事件信息,最后訂閱者方法都會在主線程中執(zhí)行,所以這種模式下不能在訂閱者方法中進行耗時操作
     */
    @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();
    }

    /**
     * 接收發(fā)布者的事件消息
     *  ThreadMode.POSTING模式:發(fā)布者在哪個線程中發(fā)布事件消息,訂閱者方法就會在哪個線程中執(zhí)行,所以這個模式下也不能進行耗時操作,會延遲分發(fā)
     * @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();
            }
        });

    }

    /**
     * 接收發(fā)布者的事件消息
     *  ThreadMode.ASYNC模式:發(fā)布者無論在哪個線程中發(fā)布事件消息,訂閱者方法都會自己重新創(chuàng)建一個子線程執(zhí)行
     * @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();
            }
        });
    }

    /**
     *  接收發(fā)布者的事件消息
     *   ThreadMode.BACKGROUND模式:發(fā)布者如果在子線程中發(fā)布事件消息,訂閱者方法就會在同一個子線程中執(zhí)行;
     *                             發(fā)布者如果在主線程中發(fā)布事件消息,訂閱者方法就會自己創(chuàng)建一個子線程執(zhí)行
     * @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.創(chuàng)建一個類,作為要發(fā)布的事件類型

package jansonwang.example.com.myapplication;

/**
 * Created by Jason Wang on 2017/5/22.
 * 這是一個事件類型,它可以攜帶各種各樣的數(shù)據(jù)類型
 */
public class 事件類型 {
    public String msg;
    public MyEvent(String msg){
        this.msg = msg;
    }
}

4.發(fā)布者發(fā)布事件類型

   EventBus.getDefault().post(事件類型對象);

每個步驟的源碼分析

 EventBus.getDefault();//創(chuàng)建事件總線對象

這段代碼采用的是單例模式:

```

//兩個非空旺遮,一個加鎖

public static EventBus getDefault() {

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

}

```

下面是優(yōu)化歷史

//簡單單例陨界,當多線程時顿痪,還是會創(chuàng)建多個示例
public static EventBus getDefault() {
    if (defaultInstance == null) {
        defaultInstance = new EventBus();
    }
    return defaultInstance;
}

//加鎖單例镊辕,每次調(diào)用都檢查是否加鎖
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) {
            //將訂閱方法記錄下來保存到對應(yīng)事件的訂閱列表中
            //Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
            subscribe(subscriber, subscriberMethod);
        }
    }
}

 EventBus.getDefault().post(事件類型對象)

下面是發(fā)布者發(fā)布事件的源碼:

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()) {
                //發(fā)布的單個事件
                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 {
        //發(fā)布對應(yīng)事件類型的事件
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    .........
}

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);//獲取對應(yīng)事件的訂閱列表
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        //遍歷訂閱列表
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                //發(fā)布事件到一個訂閱
                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線程模型下蚁袭,直接反射調(diào)用訂閱方法
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            //MAIN線程模型
            if (isMainThread) {
                //如果當前是主線程丑蛤,則直接反射調(diào)用訂閱方法
                invokeSubscriber(subscription, event);
            } else {
                //如果當前不是主線程,則使用綁定主線程Looper的Handler在主線程調(diào)用訂閱方法
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case BACKGROUND:
            //BACKGROUND線程模型
            if (isMainThread) {
                //如果當前是主線程撕阎,則在EventBus內(nèi)部的線程池中執(zhí)行
                backgroundPoster.enqueue(subscription, event);
            } else {
                //如果當前是子線程,則直接在該子線程反射調(diào)用訂閱方法
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            //ASYNC線程模型
            //直接在EventBus的線程池中執(zhí)行
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碌补,一起剝皮案震驚了整個濱河市虏束,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌厦章,老刑警劉巖镇匀,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袜啃,居然都是意外死亡汗侵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門群发,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晰韵,“玉大人,你說我怎么就攤上這事熟妓⊙┲恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵起愈,是天一觀的道長只恨。 經(jīng)常有香客問我译仗,道長,這世上最難降的妖魔是什么官觅? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任纵菌,我火速辦了婚禮,結(jié)果婚禮上休涤,老公的妹妹穿的比我還像新娘咱圆。我一直安慰自己,他們只是感情好滑绒,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布闷堡。 她就那樣靜靜地躺著,像睡著了一般疑故。 火紅的嫁衣襯著肌膚如雪杠览。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天纵势,我揣著相機與錄音踱阿,去河邊找鬼。 笑死钦铁,一個胖子當著我的面吹牛软舌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牛曹,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼佛点,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了黎比?” 一聲冷哼從身側(cè)響起超营,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阅虫,沒想到半個月后演闭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡颓帝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年米碰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片购城。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吕座,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘪板,到底是詐尸還是另有隱情米诉,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布篷帅,位于F島的核電站史侣,受9級特大地震影響拴泌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惊橱,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一蚪腐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧税朴,春花似錦回季、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至觅廓,卻和暖如春鼻忠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杈绸。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工帖蔓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞳脓。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓塑娇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親劫侧。 傳聞我的和親對象是個殘疾皇子埋酬,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)烧栋,斷路器写妥,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • EventBus是一個 發(fā)布/訂閱 模式的消息總線庫,它簡化了應(yīng)用程序內(nèi)各組件間劲弦、組件與后臺線程間的通信,解耦了事...
    hanpfei閱讀 4,051評論 0 50
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評論 25 707
  • 天吶,知識也需要管理 先嘮叨嘮叨一下個人的教育與職業(yè)背景呼猪,以及每個階段對知識的一個態(tài)度画畅。大學(xué)之前,大家都差不多宋距,為...
    小披閱讀 205評論 0 0
  • The best tools for men are machines. I think it is useful...
    Alice墨爾本閱讀 148評論 0 0