EventBus源碼詳解(一):基本使用

寫在前面

對于Android程序員來說喳张,相信大家都聽過EventBus的大名砚尽。EventBus是一個(gè)Android平臺(tái)上基于事件發(fā)布和訂閱的輕量級框架坯沪,可以對發(fā)布者和訂閱者解耦,并簡化Android的事件傳遞扛芽。正如官方介紹其優(yōu)勢:

  • 簡化了組件之間的通信
    • 解耦事件的發(fā)送者和接收者
    • 在Activity骂蓖、Fragment和后臺(tái)線程表現(xiàn)良好
    • 避免復(fù)雜和易出錯(cuò)的依賴性和生命周期問題
  • 使你的代碼更加簡潔
  • 快速和輕量
圖來自EventBus GitHub.png

接下來,讓我們一起從EventBus的使用到源碼解讀川尖,全方位地解讀這個(gè)輕量但功能強(qiáng)大的開源框架登下。

EventBus項(xiàng)目:EventBus GitHub


正文

本文先從EventBus的簡單使用開始,先介紹EventBus的基本配置和簡單使用叮喳,然后用一個(gè)Demo來舉例EventBus的使用及其優(yōu)勢被芳。

配置和簡單使用

在app下的build.gradle配置:

compile 'org.greenrobot:eventbus:3.0.0'

是的,就添加這句就可以了馍悟,非常簡單畔濒。當(dāng)然還可以配置生成索引,這部分在進(jìn)階使用文章里介紹锣咒。

定義一個(gè)事件類:

public static class MessageEvent { /* Additional fields if needed */ }

在訂閱者類中定義一個(gè)用注解“@Subscribe”標(biāo)記并參數(shù)為事件類的方法:

@Subscribe
public void onMessageEvent(MessageEvent event) {/* Do something */};

然后在訂閱者類中注冊和注銷侵状,在Android上,通常在Activity或Fragment的生命周期里處理:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this); // 注銷毅整,當(dāng)訂閱者不再訂閱事件時(shí)趣兄,必須注銷,否則可能發(fā)生內(nèi)存泄漏
 }

最后悼嫉,發(fā)送事件艇潭,每個(gè)訂閱者注冊的事件都將收到消息(會(huì)調(diào)用“@Subscribe”標(biāo)記的方法):

EventBus.getDefault().post(new MessageEvent());
舉例

現(xiàn)在來舉個(gè)栗子來展示EventBus的魅力!想想一個(gè)閱讀App看書的場景:左邊是書籍選擇列表戏蔑,右邊是書籍的內(nèi)容蹋凝。這時(shí)我們大多數(shù)情況都是用Fragment來處理,也即左右兩邊都是Fragment总棵,這就涉及兩個(gè)Fragment的通信問題鳍寂。我們通常的處理方式是在Fragment添加接口回調(diào),然后Activitty作為兩個(gè)Fragment的通信橋梁彻舰。

或許有人說直接在Activity獲取Fragment里的控件伐割,交互更加簡單。這樣雖然簡單刃唤,但Activity的職責(zé)變得很重隔心,而Fragment的作用之一就是分擔(dān)Activity的職責(zé),所以這是不可取的尚胞。

好硬霍,我們先來看不用EventBus時(shí)用回調(diào)的方式來實(shí)現(xiàn)Fragment的通信的例子。

首先先定義兩個(gè)Fragment笼裳,左邊的Fragment1唯卖,布局只有一個(gè)ListView(布局代碼不貼粱玲,后面有源碼),在Fragment1里定義一個(gè)接口拜轨,當(dāng)點(diǎn)擊ListView的item時(shí)抽减,就把所點(diǎn)擊的書籍回調(diào)給外部:

public class Fragment1 extends Fragment {

    private final static String[] BOOKS = new String[] {
            "第一行代碼",
            "Java編程思想",
            "Android開發(fā)藝術(shù)探索",
            "Android源碼設(shè)計(jì)模式",
            "算法",
            "研磨設(shè)計(jì)模式"
    };

    ListView listView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);
        listView = (ListView) view.findViewById(R.id.lv_catalogue);
        ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1, BOOKS);
        listView.setAdapter(arrayAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // 回調(diào)
                if (listener != null) {
                    listener.onCLickBook(BOOKS[position]);
                }
            }
        });
        return view;
    }

    private OnClickBookListener listener;

    public void setOnClickBookListener(OnClickBookListener listener) {
        this.listener = listener;
    }

    /**
     * 點(diǎn)擊書籍監(jiān)聽接口
     */
    public interface OnClickBookListener {
        void onCLickBook(String book);
    }
}

右邊的Fragment2就更加簡單了,布局只有一個(gè)TextView來展示書籍的內(nèi)容:

public class Fragment2 extends Fragment {

    TextView textView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment2, container, false);
        textView = (TextView) view.findViewById(R.id.tv_content);
        return view;
    }

    /**
     * 需要暴露接口給外部來交互
     */
    public void readBook(String book) {
        textView.setText(book);
    }
}

最后橄碾,MainActivity作為兩個(gè)Fragment的通信橋梁卵沉,來實(shí)現(xiàn)兩個(gè)Fragment的通信:

public class MainActivity extends AppCompatActivity {

    Fragment1 fragment1;
    Fragment2 fragment2;

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

        fragment1 = (Fragment1) getSupportFragmentManager().findFragmentById(R.id.fragment1);
        // 設(shè)置監(jiān)聽
        fragment1.setOnClickBookListener(new Fragment1.OnClickBookListener() {
            @Override
            public void onCLickBook(String book) {
                // 更新fragment2的閱讀書籍
                fragment2.readBook(String.format("我正在閱讀%s", book));
            }
        });
        fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.fragment2);
    }
}

從上面代碼可以看到,兩個(gè)Fragment的通信也非常簡單法牲,而Activity的職責(zé)也明顯降低史汗,只需作為橋梁,那么我們還有必要用EventBus嗎拒垃?答案是有必要的停撞!這只是一個(gè)例子,而實(shí)際開發(fā)中兩個(gè)Fragment通信交互幾乎不可能這么簡單悼瓮,往往是縱橫交錯(cuò)的戈毒,這時(shí)難道在Fragment上定義多個(gè)監(jiān)聽接口,然后在Activity上處理這些邏輯横堡?這就又導(dǎo)致了Activity職責(zé)過大副硅,而耦合度也顯著增加了。所以翅萤,使用回調(diào)的通信方式似乎適合交互較少的兩個(gè)Fragment,但一旦交互過多腊满,就無能為力了套么。下面再來看看EventBus怎么實(shí)現(xiàn)Fragment的通信。

首先碳蛋,先定義一個(gè)閱讀書籍的事件類(注意胚泌,最好用public修飾,否則有可能在生成索引時(shí)失敗肃弟,這些內(nèi)容在后續(xù)文章會(huì)講解玷室,現(xiàn)在先記著):

public class ReadBookEvent {

    private String book;

    ReadBookEvent(String book) {
        this.book = book;
    }

    public String getBook() {
        return book;
    }
}

顯然,因?yàn)镕ragment2是監(jiān)聽Fragment1的點(diǎn)擊事件作出更新響應(yīng)笤受,所以穷缤,這里Fragment2是訂閱者(注意:與事件類一樣,訂閱者最好聲明為public箩兽,否則有可能在生成索引失敗):

public class Fragment2 extends Fragment {

    TextView textView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment2, container, false);
        textView = (TextView) view.findViewById(R.id.tv_content);
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this); // 注冊訂閱者
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this); 注銷訂閱者
    }

    /**
     * 監(jiān)聽閱讀事件
     */
    @Subscribe
    public void onReadBook(ReadBookEvent event) {
        textView.setText(String.format("我正在閱讀%s", event.getBook()));
    }
}

而Fragment1不用定義回調(diào)接口了津肛,只需在點(diǎn)擊書籍時(shí)發(fā)送事件:

public class Fragment1 extends Fragment {

    private final static String[] BOOKS = new String[] {
            "第一行代碼",
            "Java編程思想",
            "Android開發(fā)藝術(shù)探索",
            "Android源碼設(shè)計(jì)模式",
            "算法",
            "研磨設(shè)計(jì)模式"
    };

    ListView listView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);
        listView = (ListView) view.findViewById(R.id.lv_catalogue);
        ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1, BOOKS);
        listView.setAdapter(arrayAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                EventBus.getDefault().post(new ReadBookEvent(BOOKS[position])); // 發(fā)送事件
            }
        });
        return view;
    }
}

另外在MainActivity里,不用再處理Fragment的交互了汗贫,甚至不用創(chuàng)建Fragment的實(shí)例身坐,直接解耦:

public class MainActivity extends AppCompatActivity {

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

從上面代碼可以看到秸脱,EventBus很好地使兩個(gè)Fragment解耦,并優(yōu)雅地實(shí)現(xiàn)了兩者的通信部蛇,同時(shí)也減少了Fragment和Activity的耦合摊唇,真正的一舉多得!再想想兩個(gè)Activity之間的通信涯鲁,我們通常使用Intent作為載體巷查,來傳遞數(shù)據(jù),使用EventBus就可以拋開Intent了撮竿,直接實(shí)現(xiàn)Activity間的通信吮便!這里就不再舉例了,相信聰明的你能舉一反三幢踏。

至此髓需,相信大家對EventBus有了基礎(chǔ)的認(rèn)知了。


寫在最后

EventBus所帶來的便利和簡潔有沒有使你震驚房蝉?僚匆!別滿足的太早,EventBus如此受人待見搭幻,當(dāng)然有她的厲害之處咧擂。后續(xù)文章將會(huì)介紹使用編譯期處理生成索引來提升事件發(fā)送的效率,還有EventBus的高級配置檀蹋,敬請期待松申!

下一篇文章: EventBus源碼詳解(二):進(jìn)階使用


demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市俯逾,隨后出現(xiàn)的幾起案子贸桶,更是在濱河造成了極大的恐慌,老刑警劉巖桌肴,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皇筛,死亡現(xiàn)場離奇詭異,居然都是意外死亡坠七,警方通過查閱死者的電腦和手機(jī)水醋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彪置,“玉大人拄踪,你說我怎么就攤上這事∪” “怎么了宫蛆?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我耀盗,道長想虎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任叛拷,我火速辦了婚禮舌厨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忿薇。我一直安慰自己裙椭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布署浩。 她就那樣靜靜地躺著揉燃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筋栋。 梳的紋絲不亂的頭發(fā)上炊汤,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音弊攘,去河邊找鬼抢腐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛襟交,可吹牛的內(nèi)容都是我干的迈倍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捣域,長吁一口氣:“原來是場噩夢啊……” “哼啼染!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起焕梅,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤提完,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后丘侠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逐样,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年蜗字,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脂新。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挪捕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出争便,到底是詐尸還是另有隱情级零,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站奏纪,受9級特大地震影響鉴嗤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜序调,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一醉锅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧发绢,春花似錦硬耍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墩朦,卻和暖如春坯认,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背介杆。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工鹃操, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人春哨。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓荆隘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赴背。 傳聞我的和親對象是個(gè)殘疾皇子椰拒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

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