Android事件匯流排 AndroidEventBus 開源庫釋出
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
AndroidEventBus
如果你不知道事件匯流排是什麼,那麼沒有關係,下面我們先來看這麼一個場景:
你是否在開發的過程中遇到過想在Activity-B中回撥Activity-A中的某個函式,但Activity又不能手動建立物件來設定一個Listener什麼的? 你是否想在某個Service中想更新Activity或者Fragment中的介面? 等等之類的元件之間的互動問題……
一經思考,你會發現Android中的Activity, Fragment, Service之間的互動是比較麻煩的,可能我們第一想到的是使用廣播接收器來在它們之間進行互動。例如上述所說在Activity-B中發一個廣播,在Activity-A中註冊一個廣播接收器來接受該廣播。但使用廣播接收器稍顯麻煩,如果你要將一個實體類當做資料在元件之間傳遞,那麼該實體類還得實現序列化介面,這個成本實在有點高啊!如下所示 :
// ActivityA中註冊廣播接收器 class ActivityA extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { User person = intent.getParcelableExtra("user") ; } }, new IntentFilter("my_action")) ; } // ...... } // ActivityB中釋出廣播 class ActivityB extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent("my_action") ; intent.putExtra("user", new User("mr.simple")) ; sendBroadcast(intent); } // ...... } // 實體類實現序列化 class User implements Parcelable { String name ; public User(String aName) { name = aName ; } // 程式碼省略 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
是不是有很麻煩的感覺!
我們再來看一個示例,在開發過程中,我們經常要在子執行緒中做一些耗時操作,然後將結果更新到UI執行緒,除了AsyncTask之外,Thread加Handler是我們經常用的手段。我們看看如下示例:
class MyActivity extends Activity { Handler mHandler = new Handler () { public void handleMessage(android.os.Message msg) { if ( msg.what == 1 ) { User user = (User)msg.obj ; // do sth } }; } ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // code ...... new Thread( new Runnable() { public void run() { // do sth User newUser = new User("simple") ; Message msg = mHandler.obtainMessage() ; msg.what = 1 ; msg.obj = newUser ; mHandler.sendMessage(msg) ; } }).start(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
是不是又有相當麻煩的感覺!!!此時,你應該冷靜下來思考一下,還有哪些情況適合使用事件匯流排。例如穿透多個類只為一個回撥的Listener、……
事件匯流排框架就是為了簡化這些操作而出現的,並且降低元件之間的耦合而出現的,到底如何解決呢?咱們繼續看下去吧。
AndroidEventBus是一個Android平臺輕量級的事件匯流排框架, 它簡化了Activity、Fragment、Service等元件之間的互動,很大程度上降低了它們之間的耦合,使得我們的程式碼更加簡潔,耦合性更低,提升我們的程式碼質量。
在往下看之前,你可以考慮這麼一個場景,兩個Fragment之間的通訊你會怎麼實現?
按照Android官方給的建議的解決方法如下: Communicating with the Activity,思路就是Activity實現某個介面,然後在Fragment-A關聯上Activity之後將Activity強轉為介面型別,然後在某個時刻Fragment中回撥這個介面,然後再從Activity中呼叫Fragment-B中方法。這個過程是不是有點複雜呢? 如果你也這麼覺得,那也就是你繼續看下去的理由了。
基本結構
AndroidEventBus類似於觀察者模式,通過register函式將需要訂閱事件的物件註冊到事件匯流排中,然後根據@Subscriber註解來查詢物件中的訂閱方法,並且將這些訂閱方法和訂閱物件儲存在map中。當用戶在某個地方釋出一個事件時,事件匯流排根據事件的引數型別和tag找到對應的訂閱者物件,最後執行訂閱者物件中的方法。這些訂閱方法會執行在使用者指定的執行緒模型中,比如mode=ThreadMode.ASYNC則表示該訂閱方法執行在子執行緒中,更多細節請看下面的說明。
與greenrobot的EventBus的不同
- greenrobot的EventBus是一個非常流行的開源庫,但是它在使用體驗上並不友好,例如它的訂閱函式必須以onEvent開頭,並且如果需要指定該函式執行的執行緒則又要根據規則將函式名加上執行執行緒的模式名,這麼說很難理解,比如我要將某個事件的接收函式執行在主執行緒,那麼函式名必須為onEventMainThread。那如果我一個訂閱者中有兩個引數名相同,且都執行在主執行緒的接收函式呢? 這個時候似乎它就沒法處理了。而且規定死了函式命名,那就不能很好的體現該函式的功能,也就是函式的自文件性。AndroidEventBus使用註解來標識接收函式,這樣函式名不受限制,比如我可以把接收函式名寫成updateUserInfo(Person info),這樣就靈活得多了。
- 另一個不同就是AndroidEventBus增加了一個額外的tag來標識每個接收函式可接收的事件的tag,這類似於Broadcast中的action,比如每個Broadcast對應一個或者多個action,當你發廣播時你得指定這個廣播的action,然後對應的廣播接收器才能收到.greenrobot的EventBus只是根據函式引數型別來標識這個函式是否可以接收某個事件,這樣導致只要是引數型別相同,任何的事件它都可以接收到,這樣的投遞原則就很侷限了。比如我有兩個事件,一個新增使用者的事件, 一個刪除使用者的事件,他們的引數型別都為User,那麼greenrobot的EventBus大概是這樣的:
private void onEventMainThread(User aUser) { // code }
- 1
- 2
- 3
如果你有兩個同參數型別的接收函式,並且都要執行在主執行緒,那如何命名呢 ? 即使你有兩個符合要求的函式吧,那麼我實際上是新增使用者的事件,但是由於EventBus只根據事件引數型別來判斷接收函式,因此會導致兩個函式都會被執行。AndroidEventBus的策略是為每個事件新增一個tag,引數型別和tag共同標識一個事件的唯一性,這樣就確保了事件的精確投遞。
這就是AndroidEventBus和greenrobot的EventBus的不同,當然greenrobot出於效能的考慮這麼處理也可以理解,但是我們在應用中釋出的事件數量是很有限的,效能差異可以忽略,但使用體驗上卻是很直接的。另外由於本人對greenrobot的EventBus並不是很瞭解,很可能上述我所說的有誤,如果是那樣,歡迎您指出。
AndroidEventBus起初只是為了學習,但是在學習了EventBus的實現之後,發現它在使用上有些不便之處,我想既然我有這些感覺,應該也是有同感之人,在開發群裡交流之後,發現確實有這樣的情況。因此才將正式地AndroidEventBus以開源庫的形式推出來,希望能夠幫助到一些需要的人。當然,這個庫的成長需要大家的支援與測試,歡迎大家發 pull request。
使用AndroidEventBus
你可以按照下面幾個步驟來使用AndroidEventBus.
- 1 註冊事件接收物件
public class YourActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); // 註冊物件 EventBus.getDefault().register(this); } @Override protected void onDestroy() { // 不要忘記登出!!!! EventBus.getDefault().unregister(this); super.onDestroy(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 2 通過Subscriber註解來標識事件接收物件中的接收方法
public class YourActivity extends Activity { // code ...... // 接收方法,預設的tag,執行在UI執行緒 @Subscriber private void updateUser(User user) { Log.e("", "### update user name = " + user.name); } // 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件才會觸發該函式,執行在UI執行緒 @Subscriber(tag = "my_tag") private void updateUserWithTag(User user) { Log.e("", "### update user with my_tag, name = " + user.name); } // 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件才會觸發該函式, // post函式在哪個執行緒執行,該函式就執行在哪個執行緒 @Subscriber(tag = "my_tag", mode=ThreadMode.POST) private void updateUserWithMode(User user) { Log.e("", "### update user with my_tag, name = " + user.name); } // 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件才會觸發該函式,執行在一個獨立的執行緒 @Subscriber(tag = "my_tag", mode = ThreadMode.ASYNC) private void updateUserAsync(User user) { Log.e("", "### update user async , name = " + user.name + ", thread name = " + Thread.currentThread().getName()); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
接收函式使用tag來標識可接收的事件型別,與BroadcastReceiver中指定action是一樣的,這樣可以精準的投遞訊息。mode可以指定目標函式執行在哪個執行緒,預設會執行在UI執行緒,方便使用者更新UI。目標方法執行耗時操作時,可以設定mode為ASYNC,使之執行在子執行緒中。
- 3 在其他元件,例如Activity, Fragment,Service中釋出事件
EventBus.getDefault().post(new User("android")); // post a event with tag, the tag is like broadcast's action EventBus.getDefault().post(new User("mr.simple"), "my_tag");
- 1
- 2
- 3
- 4
釋出事件之後,註冊了該事件型別的物件就會接收到響應的事件.
Github連結
歡迎star和fork, AndroidEventBus框架 。