最近大家面試說經常被問到eventbus,github上果斷down了一份,位址:https://github.com/greenrobot/eventbus,的确是個不錯的架構,主要用于事件的釋出和訂閱。
eventbus定義:是一個釋出 / 訂閱的事件總線。
這麼說應該包含4個成分:釋出者,訂閱者,事件,總線。
那麼這四者的關系是什麼呢?
很明顯:訂閱者訂閱事件到總線,發送者釋出事件。
大體應該是這樣的關系:
訂閱者可以訂閱多個事件,發送者可以釋出任何事件,釋出者同時也可以是訂閱者。
好了,大體了解基本的關系以後,我們通過案例驅動來教大家如何使用;
相信大家對fragment都有所了解,現在我們的需求是這樣的,兩個fragment組成主界面,左邊的fragment是個目錄、即清單,右邊的fragment是詳細資訊面闆;
a、目錄的清單是從網絡擷取的。
b、當點選目錄上的條目時,動态更新詳細資訊面闆;
效果圖:
看了這個需求,我們傳統的做法是:
a、目錄fragment在oncreate中去開啟線程去通路網絡擷取資料,擷取完成以後,通過handler去更新界面。
b、在目錄的fragment中提供一個接口,然後詳細資訊面闆去注冊這個接口,當發生點選時,去回調這個接口,讓詳細資訊面闆發生改變。
其實這種做法也還是不錯的,但是有了eventbus之後,我們互動會發生什麼樣的變化呢?拭目以待吧。
首先提一下:
eventbus.getdefault().register(this);//訂閱事件
eventbus.getdefault().post(object);//釋出事件
eventbus.getdefault().unregister(this);//取消訂閱
[java] view
plaincopy
package com.angeldevil.eventbusdemo;
import android.os.bundle;
import android.support.v4.app.fragmentactivity;
public class mainactivity extends fragmentactivity
{
@override
protected void oncreate(bundle savedinstancestate)
{
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
}
}
[html] view
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselinealigned="false"
android:divider="?android:attr/dividerhorizontal"
android:orientation="horizontal"
android:showdividers="middle" >
<fragment
android:id="@+id/item_list"
android:name="com.angeldevil.eventbusdemo.itemlistfragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
android:id="@+id/item_detail_container"
android:name="com.angeldevil.eventbusdemo.itemdetailfragment"
android:layout_weight="2" />
</linearlayout>
可以看到,我們mainactvity可以說沒有一行代碼,布局檔案即兩個fragment組成;
首先看個實體類:
import java.util.arraylist;
import java.util.list;
public class item
public string id;
public string content;
public static list<item> items = new arraylist<item>();
static
// add 6 sample items.
additem(new item("1", "item 1"));
additem(new item("2", "item 2"));
additem(new item("3", "item 3"));
additem(new item("4", "item 4"));
additem(new item("5", "item 5"));
additem(new item("6", "item 6"));
private static void additem(item item)
items.add(item);
public item(string id, string content)
this.id = id;
this.content = content;
public string tostring()
return content;
import android.support.v4.app.listfragment;
import android.view.view;
import android.widget.arrayadapter;
import android.widget.listview;
import com.angeldevil.eventbusdemo.event.itemlistevent;
import de.greenrobot.event.eventbus;
public class itemlistfragment extends listfragment
public void oncreate(bundle savedinstancestate)
// register
eventbus.getdefault().register(this);
public void ondestroy()
super.ondestroy();
// unregister
eventbus.getdefault().unregister(this);
public void onviewcreated(view view, bundle savedinstancestate)
super.onviewcreated(view, savedinstancestate);
// 開啟線程加載清單
new thread()
{
public void run()
{
try
{
thread.sleep(2000); // 模拟延時
// 釋出事件,在背景線程發的事件
eventbus.getdefault().post(new itemlistevent(item.items));
} catch (interruptedexception e)
e.printstacktrace();
}
};
}.start();
public void oneventmainthread(itemlistevent event)
setlistadapter(new arrayadapter<item>(getactivity(),
android.r.layout.simple_list_item_activated_1,
android.r.id.text1, event.getitems()));
public void onlistitemclick(listview listview, view view, int position,
long id)
super.onlistitemclick(listview, view, position, id);
eventbus.getdefault().post(getlistview().getitematposition(position));
itemlistfragment裡面在oncreate裡面進行了事件的訂閱,ondestroy裡面進行了事件的取消;onviewcreated中我們模拟了一個子線程去網絡加載資料,擷取成功後我們調用
了eventbus.getdefault().post(new itemlistevent(item.items));釋出了一個事件;
onlistitemclick則是listview的點選事件,我們調用了eventbus.getdefault().post(getlistview().getitematposition(position));去釋出一個事件,
getlistview().getitematposition(position)的類型為item類型;
細心的你一定發現了一些詭異的事,直接new thread()擷取到資料以後,竟然沒有使用handler;我們界面竟然發生了變化,那麼list是何時綁定的資料?
仔細看下代碼,發現這個方法:
public void oneventmainthread(itemlistevent event)
{
setlistadapter(new arrayadapter<item>(getactivity(),
android.r.layout.simple_list_item_activated_1,
android.r.id.text1, event.getitems()));
}
應該是這個方法為list綁定的資料。那麼這個方法是怎麼被調用的呢?
現在就可以細談訂閱事件與釋出事件了:
如果方法名以onevent開頭,則代表要訂閱一個事件,mainthread意思,這個方法最終要在ui線程執行;當事件釋出的時候,這個方法就會被執行。
那麼這個事件什麼時候釋出呢?
我們的oneventmainthread觸發時機應該在new thread()執行完成之後,可以看到子線程執行完成之後,執行了eventbus.getdefault().post(new itemlistevent(item.items));
意味着釋出了一個事件,當這個事件釋出,我們的oneventmainthread就執行了,那麼二者的關聯關系是什麼呢?
其實和參數的類型,我們oneventmainthread需要接收一個itemlistevent ,我們也釋出了一個itemlistevent的執行個體。
現在我們完整的理一下:
在oncreate裡面執行 eventbus.getdefault().register(this);意思是讓eventbus掃描目前類,把所有onevent開頭的方法記錄下來,如何記錄呢?使用map,key為方法的參數類型,value中包含我們的方法。
這樣在oncreate執行完成以後,我們的oneventmainthread就已經以鍵值對的方式被存儲到eventbus中了。
然後當子線程執行完畢,調用eventbus.getdefault().post(new itemlistevent(item.items))時,eventbus會根據post中實參的類型,去map中查找對于的方法,于是找到了我們的oneventmainthread,最終調用反射去執行我們的方法。
現在應該明白了,整個運作的流程了;那麼沒有接口卻能發生回調應該也能解釋了。
現在我們在看看代碼,當item點選的時候eventbus.getdefault().post(getlistview().getitematposition(position));我們同樣釋出了一個事件,參數為item;這個事件是為了讓詳細資訊的fragment去更新資料,不用說,按照上面的推測,詳細資訊的fragment裡面一個有個這樣的方法:public void oneventmainthread(item item) ; 是不是呢?我們去看看。
import android.support.v4.app.fragment;
import android.view.layoutinflater;
import android.view.viewgroup;
import android.widget.textview;
public class itemdetailfragment extends fragment
private textview tvdetail;
/** list點選時會發送些事件,接收到事件後更新詳情 */
public void oneventmainthread(item item)
if (item != null)
tvdetail.settext(item.content);
public view oncreateview(layoutinflater inflater, viewgroup container,
bundle savedinstancestate)
view rootview = inflater.inflate(r.layout.fragment_item_detail,
container, false);
tvdetail = (textview) rootview.findviewbyid(r.id.item_detail);
return rootview;
果然不出我們的所料,真的存在oneventmainthread(item item)的方法。當然了,必須在oncreate裡面首先書寫eventbus.getdefault().register(this);讓eventbus掃描再說。
那麼這個fragment的流程就是:oncreate時,eventbus掃描目前類,将oneventmainthread以鍵值對的形式進行存儲,鍵為item.class ,值為包含該方法的對象。
然後當itemlistfragment中item被點選時,釋出了一個事件:eventbus.getdefault().post(getlistview().getitematposition(position));實參的類型恰好是item,于是觸發我們的
oneventmainthread方法,并把item實參傳遞進來,我們更新控件。
這裡還有個事件類:
public class event
/** 清單加載事件 */
public static class itemlistevent
private list<item> items;
public itemlistevent(list<item> items)
this.items = items;
}
public list<item> getitems()
return items;
itemlistevent我們在itemlistfragment中使用的,作為的是oneventmainthread中的參數。為什麼封裝這麼個類呢?會在之後的eventbus源碼解析中說明。
到此我們的eventbus的初步用法就介紹完畢了。縱觀整個代碼,木有handler、木有asyntask,木有接口回調;but,我們像魔術般的實作了我們的需求;來告訴我,什麼是耦合,沒見到~~~
eventbus包含4個threadmode:postthread,mainthread,backgroundthread,async
mainthread我們已經不陌生了;我們已經使用過。
具體的用法,極其簡單,方法名為:oneventpostthread, oneventmainthread,oneventbackgroundthread,oneventasync即可
具體什麼差別呢?
oneventmainthread代表這個方法會在ui線程執行
oneventpostthread代表這個方法會在目前釋出事件的線程執行
backgroundthread這個方法,如果在非ui線程釋出的事件,則直接執行,和釋出在同一個線程中。如果在ui線程釋出的事件,則加入到一個背景的單線程隊列中去。
async 代表這個方法直接在獨立的線程中執行。
大家可以利用eventbus嘗試做以下操作:
當接收到某個廣播,例如短信,在界面上顯示。
開啟一個service,在伺服器裡面啟動一個定時線程,不斷更新activityui。
等等...之後,你會發現eventbus的魅力!
補充:
eventbus使用詳解(一)——初步使用eventbus
前言:eventbus是上周項目中用到的,網上的文章大都一樣,或者過時,有用的沒幾篇,經過琢磨,請教他人,也終于弄清楚點眉目,記錄下來分享給大家。
eventbus是一款針對android優化的釋出/訂閱事件總線。主要功能是替代intent,handler,broadcast在fragment,activity,service,線程之間傳遞消息.優點是開銷小,代碼更優雅。以及将發送者和接收者解耦。
1、下載下傳eventbus的類庫
源碼:https://github.com/greenrobot/eventbus
2、基本使用
(1)自定義一個類,可以是空類,比如:
public class anyeventtype {
public anyeventtype(){}
}
(2)在要接收消息的頁面注冊:
eventbus.register(this);
(3)發送消息
eventbus.post(new anyeventtype event);
(4)接受消息的頁面實作(共有四個函數,各功能不同,這是其中之一,可以選擇性的實作,這裡先實作一個):
public void onevent(anyeventtype event) {}
(5)解除注冊
eventbus.unregister(this);
順序就是這麼個順序,可真正讓自己寫,估計還是雲裡霧裡的,下面舉個例子來說明下。
首先,在eventbus中,擷取執行個體的方法一般是采用eventbus.getinstance()來擷取預設的eventbus執行個體,當然你也可以new一個又一個,個人感覺還是用預設的比較好,以防出錯。
先給大家看個例子:
當擊btn_try按鈕的時候,跳到第二個activity,當點選第二個activity上面的first event按鈕的時候向第一個activity發送消息,當第一個activity收到消息後,一方面将消息toast顯示,一方面放入textview中顯示。
按照下面的步驟,下面來建這個工程:
想必大家從一個activity跳轉到第二個activity的程式應該都會寫,這裡先稍稍把兩個activity跳轉的代碼建起來。後面再添加eventbus相關的玩意。
mainactivity布局(activity_main.xml)
android:orientation="vertical">
<button
android:id="@+id/btn_try"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="btn_bty"/>
<textview
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
建立一個activity,secondactivity布局(activity_second.xml)
android:orientation="vertical"
tools:context="com.harvic.try_eventbus_1.secondactivity" >
android:id="@+id/btn_first_event"
android:text="first event"/>
mainactivity.java (點選btn跳轉到第二個activity)
public class mainactivity extends activity {
button btn;
protected void oncreate(bundle savedinstancestate) {
btn = (button) findviewbyid(r.id.btn_try);
btn.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
// todo auto-generated method stub
intent intent = new intent(getapplicationcontext(),
secondactivity.class);
startactivity(intent);
}
});
到這,基本架構就搭完了,下面開始按步驟使用eventbus了。
package com.harvic.other;
public class firstevent {
private string mmsg;
public firstevent(string msg) {
// todo auto-generated constructor stub
mmsg = msg;
public string getmsg(){
return mmsg;
這個類很簡單,構造時傳進去一個字元串,然後可以通過getmsg()擷取出來。
在上面的gif圖檔的示範中,大家也可以看到,我們是要在mainactivity中接收發過來的消息的,是以我們在mainactivity中注冊消息。
通過我們會在oncreate()函數中注冊eventbus,在ondestroy()函數中反注冊。是以整體的注冊與反注冊的代碼如下:
package com.example.tryeventbus_simple;
import com.harvic.other.firstevent;
import android.app.activity;
import android.content.intent;
import android.util.log;
import android.widget.button;
import android.widget.toast;
textview tv;
//注冊eventbus
tv = (textview)findviewbyid(r.id.tv);
protected void ondestroy(){
eventbus.getdefault().unregister(this);//反注冊eventbus
發送消息是使用eventbus中的post方法來實作發送的,發送過去的是我們建立的類的執行個體!
eventbus.getdefault().post(new firstevent("firstevent btn clicked"));
完整的secondactivity.java的代碼如下:
public class secondactivity extends activity {
private button btn_firstevent;
setcontentview(r.layout.activity_second);
btn_firstevent = (button) findviewbyid(r.id.btn_first_event);
btn_firstevent.setonclicklistener(new view.onclicklistener() {
eventbus.getdefault().post(
new firstevent("firstevent btn clicked"));
接收消息時,我們使用eventbus中最常用的oneventmainthread()函數來接收消息,具體為什麼用這個,我們下篇再講,這裡先給大家一個初步認識,要先能把eventbus用起來先。
在mainactivity中重寫oneventmainthread(firstevent event),參數就是我們自己定義的類:
在收到event執行個體後,我們将其中攜帶的消息取出,一方面toast出去,一方面傳到textview中;
public void oneventmainthread(firstevent event) {
string msg = "oneventmainthread收到了消息:" + event.getmsg();
log.d("harvic", msg);
tv.settext(msg);
toast.maketext(this, msg, toast.length_long).show();
完整的mainactiviy代碼如下:
public void oneventmainthread(firstevent event) {
string msg = "oneventmainthread收到了消息:" + event.getmsg();
log.d("harvic", msg);
tv.settext(msg);
toast.maketext(this, msg, toast.length_long).show();
好了,到這,基本上算初步把eventbus用起來了,下篇再講講eventbus的幾個函數,及各個函數間是如何識别目前如何調用哪個函數的。
源碼位址:http://download.csdn.net/detail/harvic880925/8111357
請大家尊重原創者版權,轉載請标明出處:http://blog.csdn.net/harvic880925/article/details/40660137
謝謝!
eventbus使用詳解(二)——eventbus使用進階
前言:這段時間感覺自己也有點懶了,真是内心有點自責呢,除了工作,也沒做點什麼,eventbus也是一周前總結出來的,隻能以寫部落格為名來彌補内心的罪惡感了,集合同僚們做的項目,雖然上周開動了,但總感覺大家積極性不高,如何才能做一個合格的管理者,還真是一個考驗。follow your heart!! just do it!
前一篇給大家裝簡單示範了eventbus的oneventmainthread()函數的接收,其實eventbus還有另外有個不同的函數,他們分别是:
1、onevent
2、oneventmainthread
3、oneventbackgroundthread
4、oneventasync
這四種訂閱函數都是使用onevent開頭的,它們的功能稍有不同,在介紹不同之前先介紹兩個概念:
告知觀察者事件發生時通過eventbus.post函數實作,這個過程叫做事件的釋出,觀察者被告知事件發生叫做事件的接收,是通過下面的訂閱函數實作的。
onevent:如果使用onevent作為訂閱函數,那麼該事件在哪個線程釋出出來的,onevent就會在這個線程中運作,也就是說釋出事件和接收事件線程在同一個線程。使用這個方法時,在onevent方法中不能執行耗時操作,如果執行耗時操作容易導緻事件分發延遲。
oneventmainthread:如果使用oneventmainthread作為訂閱函數,那麼不論事件是在哪個線程中釋出出來的,oneventmainthread都會在ui線程中執行,接收事件就會在ui線程中運作,這個在android中是非常有用的,因為在android中隻能在ui線程中跟新ui,是以在onevnetmainthread方法中是不能執行耗時操作的。
oneventbackground:如果使用oneventbackgrond作為訂閱函數,那麼如果事件是在ui線程中釋出出來的,那麼oneventbackground就會在子線程中運作,如果事件本來就是子線程中釋出出來的,那麼oneventbackground函數直接在該子線程中執行。
oneventasync:使用這個函數作為訂閱函數,那麼無論事件在哪個線程釋出,都會建立新的子線程在執行oneventasync.
上面列出的這四個函數,關鍵問題在于,我們怎麼指定調用哪個函數呢?
我們先研究一下,上一篇中是怎麼調用的oneventmainthread函數,除了在接收端注冊與反注冊以後,關鍵問題在于建立的一個類:
建立一個類:
發送時:
eventbus.getdefault().post(new firstevent("firstevent btn clicked"));
接收時:
public void oneventmainthread(firstevent event) {
……
}
發現什麼問題了沒?
沒錯,發送時發送的是這個類的執行個體,接收時參數就是這個類執行個體。
是以!!!!!!當發過來一個消息的時候,eventbus怎麼知道要調哪個函數呢,就看哪個函數傳進去的參數是這個類的執行個體,哪個是就調哪個。那如果有兩個是呢,那兩個都會被調用!!!!
為了證明這個問題,下面寫個例子,先看下效果
先看看我們要實作的效果:
這次我們在上一篇的基礎上,建立三個類:firstevent、secondevent、thirdevent,在第二個activity中發送請求,在mainactivity中接收這三個類的執行個體,接收時的代碼為:
log.d("harvic", "oneventmainthread收到了消息:" + event.getmsg());
public void oneventmainthread(secondevent event) {
public void onevent(thirdevent event) {
log.d("harvic", "onevent收到了消息:" + event.getmsg());
使用兩個oneventmainthread分别接收firstevent執行個體的消息和secondevent執行個體的消息,使用onevent接收thirdevent執行個體的消息。界面操作及結果如下:
log輸出結果:
可以看到,在發送firstevent時,在mainactiviy中雖然有三個函數,但隻有第一個oneventmainthread函數的接收參數是firstevent,是以會傳到它這來接收。是以這裡識别調用eventbus中四個函數中哪個函數,是通過參數中的執行個體來決定的。
因為我們是在上一篇例子的基礎上完成的,是以這裡的代碼就不詳細寫了,隻寫改動的部分。
public class secondevent{
public secondevent(string msg) {
mmsg = "mainevent:"+msg;
public class thirdevent {
public thirdevent(string msg) {
然後在secondactivity中建立三個按鈕,分别發送不同的類的執行個體,代碼如下:
package com.harvic.tryeventbus2;
import com.harvic.other.secondevent;
import com.harvic.other.thirdevent;
private button btn_firstevent, btn_secondevent, btn_thirdevent;
btn_secondevent = (button) findviewbyid(r.id.btn_second_event);
btn_thirdevent = (button) findviewbyid(r.id.btn_third_event);
btn_secondevent.setonclicklistener(new view.onclicklistener() {
new secondevent("secondevent btn clicked"));
btn_thirdevent.setonclicklistener(new view.onclicklistener() {
new thirdevent("thirdevent btn clicked"));
在mainactivity中,除了注冊與注冊,我們利用oneventmainthread(firstevent event)來接收來自firstevent的消息,使用oneventmainthread(secondevent event)接收來自secondevent 執行個體的消息,使用onevent(thirdevent event) 來接收thirdevent
執行個體的消息。
import android.view.menu;
import android.view.menuitem;
eventbus eventbus;
log.d("harvic", "oneventmainthread收到了消息:" + event.getmsg());
public void oneventmainthread(secondevent event) {
public void onevent(thirdevent event) {
log.d("harvic", "onevent收到了消息:" + event.getmsg());
protected void ondestroy() {
// todo auto-generated method stub
到這裡,代碼就結束 了,從上面的代碼,我們可以看到,eventbus是怎麼接收消息的,是根據參數中類的執行個體的類型的判定的,是以當如果我們在接收時,同一個類的執行個體參數有兩個函數來接收會怎樣?答案是,這兩個函數都會執行,下面實驗一下:
在mainactivity中接收時,我們在接收secondevent時,在上面oneventmainthread基礎上另加一個oneventbackgroundthread和oneventasync,即下面的代碼:
//secondevent接收函數一
//secondevent接收函數二
public void oneventbackgroundthread(secondevent event){
log.d("harvic", "oneventbackground收到了消息:" + event.getmsg());
//secondevent接收函數三
public void oneventasync(secondevent event){
log.d("harvic", "oneventasync收到了消息:" + event.getmsg());
完整的代碼在這裡:
//secondevent接收函數一
//secondevent接收函數二
public void oneventbackgroundthread(secondevent event){
log.d("harvic", "oneventbackground收到了消息:" + event.getmsg());
//secondevent接收函數三
public void oneventasync(secondevent event){
log.d("harvic", "oneventasync收到了消息:" + event.getmsg());
經過上面的分析,當發送secondevent執行個體的消息過來的時候,這三個函數會同時接收到并各自執行,是以當點選second event這個button的時候,會出現下面的結果:
好啦,這篇就到了,講來講去就是說一個問題:消息的接收是根據參數中的類名來決定執行哪一個的;
參考文章:
《android解耦庫eventbus的使用和源碼分析》:http://blog.csdn.net/yuanzeyao/article/details/38174537
《eventbus的使用初試》:http://blog.csdn.net/pp_hdsny/article/details/14523561
《eventbusexplained 》:https://code.google.com/p/guava-libraries/wiki/eventbusexplained
guava eventbus執行個體與分析》
如果我的文章有幫到你,記得關注哦!
源碼下載下傳位址:http://download.csdn.net/detail/harvic880925/8128633
請大家尊重原創者版權,轉載請标明出處:http://blog.csdn.net/harvic880925/article/details/40787203