編輯:關於android開發
在上一篇文章中,我曾提到我所選擇的是Green Robot提供的EventBus(Android平台),而且這並非只是我一個人的選擇。在最近一次查看中,我發現選擇它的人數已經是Otto(由Jake Wharton和其他大神們在Square上所提供的版本)的兩倍之多了。GR的版本顯然比Otto有更多的性能提升,但最令我動心的地方在於它添加了很多新功能。今天我就打算談談其中的一項:通過sticky事件進行事件緩存。
sticky事件就是指在EventBus內部被緩存的那些事件。EventBus為每個類(class)類型保存了最近一次被發送的事件——sticky。後續被發送過來的相同類型的sticky事件會自動替換之前緩存的事件。當一個監聽者向EventBus進行注冊時,它可能會去請求這些緩存事件。這時,所有已緩存的事件就會立即自動發送給這個監聽者,就象這些事件又重新剛被發送了一次一樣。這就意味著,一個監聽者可以收到在它注冊之前就已經被發送到EventBus中的事件(甚至是在這個監聽者的實例被創建出來前,這一點是不是很奇妙)。這一強大功能將有助於我們解決某些固有的問題,如Android上跨Activity和Fragment生命周期傳遞數據這種復雜問題,異步調用等等。
使用sticky
使用sticky事件需要從兩個方面進行:
當調用了bus.registerSticky(this)後,監聽者會立即收到所有已在onEvent處理程序中定義過的那些已緩存的事件。另外,監聽者也可以根據需要通過bus.getStickyEvent(SomeEvent.class)來獲取這些緩存事件。
(注:調用postSticky,會像普通的post調用一樣將事件發送給所有當前活動的監聽者,而不是僅限於那些通過registerSticky注冊的。registerSticky僅僅是使緩存事件在注冊時被重發。)
sticky事件在緩存中存在的時間並不確定。所以如果你想在某一時刻消除緩存中的事件好讓它們不再被發送,可以通過bus.removeStickyEvent(event)或bus.removeStickyEvent(SomeEvent.class),以及bus.removeAllStickyEvents()來實現。
我在上一篇文章中曾說過,我並不喜歡Android中的Bundle,而且盡量避免使用它們。我不喜歡被象Serializable或者是Parcelable這類對象所約束,尤其是它們還缺少對類型安全的檢查。我的意思是必竟這是Java,而不是Python或者Javascript什麼的。我希望我的IDE能夠發現並告訴我這樣的錯誤,如一個組件向另外一個組件發送了一個不是它期望的對象類型。
不要誤會,Intent在進程間通信時還是很有用的,在這種情況下將攜帶的數據序列化成通用格式是合情合理的。但如果僅僅是為了在用戶旋轉了一下屏幕後,讓程序保持原來的狀態,以科學的名義說,有必要非得用這種方法麼?沒錯,我說的就是Android提供的處理配置改變的標准模式——在onSaveInstanceState(Bundle bundle)和onRestoreInstanceState(Bundle bundle)中保存和恢復狀態數據。且不提那些荒唐復雜的Fragment生命周期問題,單單是保持運行狀態的這種處理方式就是我最不喜歡的Android開發特點之一。
程序運行狀態除了保存到Bundle中,另一種方法是將它們保存到某些在配置改變時依然生存著的對象中去。GR的EventBus剛好內置了這種緩存機制可供我們使用。
考慮下面這個響應“Master/Detail流程”的標准場景:
在這個例子中具有挑戰性的是,當用戶在橫豎屏間來回切換時,程序要如何維護當前所選項的狀態。這個狀態的重要性不但在於詳細頁面需要知道該顯示哪條詳細信息,而且列表也需要顯示出當前哪一項被選中了。此外,對於主頁面來說也需要知道當前是否有項目被選中,以便決定在豎屏模式時需要加載哪個頁面,列表或詳細信息。
如你所見,這三個組件都需要同一個狀態信息(被選中項)。使用傳統方法,這三個組件每一個都需要在各自的onSaveInstanceState方法中將這一狀態保存進Bundle中,然後再從各自的onResumeInstanceState方法裡把數據取回來。不爽!
然而使用sticky事件,事情就變得簡單多了。為了更好地說明問題,我創建了一個Android示例工程:https://github.com/wongcain/EventBus-Config-Demo/ 下面所有的示例代碼都包含在這個工程裡。
首先,創建一個事件類(ItemSelectedEvent.java)用於傳遞被選中項的位置信息:
Java 1 2 3 4 5 6 public class ItemSelectedEvent { public final int position; public ItemSelectedEvent(int position) { this.position = position; } }然後在List組件(ItemListFragment.java)的listItemClick方法裡發送一個sticky事件:
Java 1 2 3 4 5 @Override public void onListItemClick(ListView listView, View itemView, int position, long id) { super.onListItemClick(listView, itemView, position, id); bus.postSticky(new ItemSelectedEvent(position)); }接下來,Detail組件(ItemDetailFragment.java)注冊接收sticky事件,並定義一個ItemSelectedEvent的處理方法。當收到事件時,查詢並顯示被選中項的詳細信息:
Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public void onResume() { super.onResume(); bus.registerSticky(this); } @Override public void onPause() { bus.unregister(this); super.onPause(); } ... public void onEvent(ItemSelectedEvent event) { Item item = MockDataSource.ITEMS.get(event.position); titleView.setText(item.title); dateView.setText(item.getDateStr()); bodyView.setText(item.body); }最後,在Main組件(MainActivity.java)中將所有內容集合到一起。Activity自身注冊監聽sticky事件,並創建與Detail組件一樣的ItemSelectedEvent處理方法。當收到事件時,根據當前頁面布局(layout)決定將Detail fragment加載哪個合適的容器中。
Java 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 @Override protected void onResume() { super.onResume(); bus.registerSticky(this); bus.postSticky(new LayoutEvent(isTwoPane())); } @Override protected void onPause() { bus.unregister(this); super.onPause(); } public void onEvent(ItemSelectedEvent event) { if(isTwoPane()){ getFragmentManager().beginTransaction() .replace(detailContainer.getId(), new ItemDetailFragment()) .commit(); } else { getFragmentManager().beginTransaction() .replace(listContainer.getId(), new ItemDetailFragment()) .addToBackStack(ItemDetailFragment.class.getName()) .commit(); } }注意,這個activity不僅監聽sticky事件,還發送了另外一個sticky事件用來傳遞當前屏幕模式。這一事件隨後會被List fragment(ItemListFragment.java)收到,並且根據條件對列表進行設置:
Java 1 2 3 4 5 6 7 8 public void onEvent(LayoutEvent event) { if(event.isTwoPane){ getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); updateSelectedItem(activePosition); } else { getListView().setChoiceMode(ListView.CHOICE_MODE_NONE); } }另外可以看到,沒有一個組件要去實現onSaveInstanceState(Bundle bundle)以及onRestoreInstanceState(Bundle bundle)方法。取而代之的是它們只需簡單地依賴於在registerSticky(this)時自動發送的緩存事件。所以,當用戶選擇一個項目並且在查看詳細信息時,以下情況便會在配置改變時自動發生:
希望這篇文章對你能有幫助。如之前提到的,所有的示例代碼都可以在這裡訪問到:https://github.com/wongcain/EventBus-Config-Demo/
下一篇將是有關EventBus系列教程的最後一篇, 我將談一談在EventBus中有關跨越多線程和進程的有關內容。
問啊一款面向程序員,共享知識技能的在線交互平台APP
問啊基於共享經濟理論,引入一鍵呼叫模式,一鍵解決程序員工作中遇到的技
術難題,提高工作效率。
產品形態: 移動客戶端
官方網站:http://www.wenaaa.com
appstore及各大應用市場均可下載
Android安全之旅系列博客導讀 總結下近一年的學習經歷,以中國的新年為節點。時間從2015年年後到今天,2015年年末。 首先我必須要感謝前輩的書籍和高質量的博客。
Android,androiddeveloper實現android雙擊後退鍵退出當前APP功能 實現該功能基本思路是, 1, 監聽後退鍵 , 比較兩次後退間隔 , 低於兩
本節教程主要講解Android傳感器編程的基礎知識,包括加速度傳感器(acce
Android ListView ArrayAdapter 的簡單使用,androidarrayadapter前面寫了3篇關於android的文章,其中的演示程序都寫在了