移動架構<第六篇>:EventBus事件總線框架

在Android的實際開發(fā)中赃绊,消息的通信是非常頻繁的既峡。結合Android的基礎知識,常用的通信方式有Intent碧查、Handler运敢、Broadcast,它們都可以在Activity忠售、Fragment传惠、Service之間相互通信,但是呢稻扬?從架構的思維考慮卦方,Android本身的通信方式會增加代碼架構的復雜度。為了解決這個問題泰佳,必須要重新定制一個新的通信方案盼砍。
通信方式有兩種:線程間通信跨進程通信
本文主要講解EventBus框架的使用,它只能實現線程間的通信逝她,不支持跨進程通信衬廷。

[github地址]

首先,EventBus對應的github地址需要記住汽绢,如下:

https://github.com/greenrobot/EventBus

EventBus的版本會被更新的吗跋,所以,想要知道EventBus的最新版本,需要打開對應的github地址查看跌宛。

[簡單演示]

首先貼一下演示代碼:

public class MainActivity extends AppCompatActivity {

    private Button button;

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

        //注冊
        EventBus.getDefault().register(this);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //發(fā)送消息
                        EventBus.getDefault().post(new MessageEvent(100));
                    }
                }).start();
            }
        });
    }

    /**
     * 接收消息
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        /* Do something */
        Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
    }

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

EventBus的使用非常簡單酗宋,如上代碼所示叁熔,在onCreate方法里面注冊悼尾,在onDestroy方法里面解注冊植康,代碼EventBus.getDefault().post發(fā)送消息蹭越,以及onMessageEvent方法接收消息射赛。這種寫法類似于Android的BroadCast鲜戒,但和Android的BroadCast有本質的區(qū)別境钟。

在一些項目中植酥,我經呈浚看到Activity和Fragment之間的通信使用廣播來實現翔烁,我對廣播的理解分為以下幾點:

  • 常用的廣播分為Broadcast(常規(guī)廣播)和LocalBroadcast(本地廣播)。前者發(fā)送的消息旨涝,整個系統都能收到蹬屹,后者發(fā)送的消息只能當前應用可以收到。但是白华,由于大型項目中可能存在無數消息的傳遞慨默,這樣會導致廣播的泛濫,可讀性變差弧腥,如果非要使用廣播的話建議使用后者厦取;
  • 廣播的注冊方式有兩種:動態(tài)注冊和靜態(tài)注冊,本人不推薦靜態(tài)注冊方式管搪,因為靜態(tài)注冊方式使廣播常駐內存虾攻,廣播是非常消耗內存的,靜態(tài)注冊方式不可扰自椤台谢;
  • Android的廣播機制不僅可以跨線程也能跨進程,但是廣播的代碼實在不能用優(yōu)雅這個詞來形容岁经,從后期項目的維護成本出發(fā)朋沮,廣播的代碼還是比較紊亂的,加大了項目的維護成本缀壤。

所以樊拓,EventBus完全可以替代Android的廣播,不僅如此塘慕,任何跨線程的通信都可以使用EventBus筋夏。那么,跨進程怎么玩图呢?Android的跨進程常常使用AIDL機制条篷,但考慮到代碼的簡潔性骗随,推薦使用Hermes框架,Hermes框架不是本章的重點赴叹,所以把思路重新回到EventBus上鸿染。

要使用EventBus,一般需要注冊乞巧,register方法的參數傳遞一個對象涨椒,大部分情況下,這個對象是當前Activity對象绽媒。

    //注冊
    EventBus.getDefault().register(this);

為了防止內存泄漏蚕冬,對應的還有反注冊,代碼如下:

    EventBus.getDefault().unregister(this);

發(fā)送消息(發(fā)布者Publisher)是辕,post方法后面的參數可以傳遞基本數據類型或者對象囤热,建議傳遞一個對象。

         EventBus.getDefault().post(new MessageEvent(100));

接收消息(發(fā)布者Subscriber)免糕,onMessageEvent是接收消息的方法赢乓,這個方法必須添加注解@Subscribe忧侧,不然發(fā)送消息時找不到內存中注解對應的onMessageEvent方法石窑,其次onMessageEvent方法必須有且只有一個形式參數,這個參數表示要接收的消息類型蚓炬,如果發(fā)送的消息對象非MessageEvent對象松逊,那么onMessageEvent方法是無法被執(zhí)行的。

/**
 * 接收消息
 * @param event
 */
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    /* Do something */
    Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
}

[線程模式]

接收消息的方法被@Subscribe修飾肯夏,它是EventBus框架的自定義注解经宏,源碼如下:

@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、sticky徊都、priority

priority:消息接收的優(yōu)先級沪斟,默認為0,優(yōu)先級越高就越快收到消息暇矫;
sticky:粘性事件的開關主之,默認值是false
threadMode:線程模式,默認模式是ThreadMode.POSTING李根,線程模式分為:POSTING槽奕、MAINMAIN_ORDERED房轿、BACKGROUND粤攒、ASYNC

  • POSTING:
    默認的模式,開銷最小的模式所森,因為聲明為POSTING的訂閱者會在發(fā)布的同一個線程調用,發(fā)布者在主線程那么訂閱者也就在主線程夯接,反之亦必峰,避免了線程切換,如果不確定是否有耗時操作钻蹬,謹慎使用吼蚁,因為可能是在主線程發(fā)布。

  • MAIN
    主線程調用问欠,視發(fā)布線程不同處理不同肝匆,如果發(fā)布者在主線程那么直接調用(非阻塞式),如果發(fā)布者不在主線程那么阻塞式調用顺献。

  • MAIN_ORDERED
    和MAIN差不多旗国,主線程調用,和MAIN不同的是他保證了post是非阻塞式的(默認走MAIN的非主線程的邏輯注整,所以可以做到非阻塞)

  • BACKGROUND
    在子線程調用能曾,如果發(fā)布在子線程那么直接在發(fā)布線程調用,如果發(fā)布在主線程那么將開啟一個子線程來調用肿轨,這個子線程是阻塞式的,按順序交付所有事件寿冕,所以也不適合做耗時任務,因為多個事件共用這一個后臺線程椒袍。

  • ASYNC
    在子線程調用驼唱,總是開啟一個新的線程來調用,適用于做耗時任務驹暑,比如數據庫操作玫恳,網絡請求等,不適合做計算任務优俘,會導致開啟大量線程京办。

[粘性事件]

EventBus的一般用法是:先注冊,再定義接收事件的方法帆焕,之后發(fā)送的事件才能接收到惭婿。
但是,在Android中有寫場景不符合EventBus的一般用法视搏,比如:Fragment之間切換時审孽,將數據從一個Fragment傳遞到另一個Fragment。還有一個場景是浑娜,從一個Activity跳轉到另一個Activity佑力,將數據從一個Activity傳遞到另一個Activity。

為了解決這個問題筋遭,EventBus推出了粘性事件打颤。假如ActivityA跳轉到ActivityB時暴拄,將數據從A發(fā)送到B,那么該如何實現呢编饺?

第一步乖篷,在ActivityA中發(fā)送粘性事件

            //發(fā)送消息
            EventBus.getDefault().postSticky(new MessageEvent(100));

postSticky方法發(fā)送一個粘性事件。

第二步透且,在ActivityB中注冊和反注冊

    //注冊
    EventBus.getDefault().register(this);

    //反注冊
    EventBus.getDefault().unregister(this);

第三步撕蔼,在ActivityB中定義接收事件的方法

/**
 * 接收消息
 * @param event
 */
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onMessageEvent(MessageEvent event) {
    /* Do something */
    Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
}

其中,sticky屬性必須設置為true秽誊。

[代碼混淆]

根據EventBus作者的意思鲸沮,如果項目開啟混淆功能的話,還需要添加以下混淆代碼:

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

# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

[本章完...]

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末锅论,一起剝皮案震驚了整個濱河市讼溺,隨后出現的幾起案子,更是在濱河造成了極大的恐慌最易,老刑警劉巖怒坯,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異藻懒,居然都是意外死亡剔猿,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門束析,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艳馒,“玉大人憎亚,你說我怎么就攤上這事员寇。” “怎么了第美?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵蝶锋,是天一觀的道長。 經常有香客問我什往,道長扳缕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任别威,我火速辦了婚禮躯舔,結果婚禮上,老公的妹妹穿的比我還像新娘省古。我一直安慰自己粥庄,他們只是感情好,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布豺妓。 她就那樣靜靜地躺著惜互,像睡著了一般布讹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上训堆,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天描验,我揣著相機與錄音,去河邊找鬼坑鱼。 笑死膘流,一個胖子當著我的面吹牛,可吹牛的內容都是我干的鲁沥。 我是一名探鬼主播睡扬,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼黍析!你這毒婦竟也來了卖怜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤阐枣,失蹤者是張志新(化名)和其女友劉穎马靠,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體蔼两,經...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡甩鳄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了额划。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妙啃。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俊戳,靈堂內的尸體忽然破棺而出揖赴,到底是詐尸還是另有隱情,我是刑警寧澤抑胎,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布燥滑,位于F島的核電站,受9級特大地震影響阿逃,放射性物質發(fā)生泄漏铭拧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一恃锉、第九天 我趴在偏房一處隱蔽的房頂上張望搀菩。 院中可真熱鬧,春花似錦破托、人聲如沸肪跋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澎嚣。三九已至疏尿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間易桃,已是汗流浹背褥琐。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晤郑,地道東北人敌呈。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像造寝,于是被迫代替她去往敵國和親磕洪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351