編輯:Android開發實例
在這一系列教程的最後一篇中,我想談談GR的EventBus,在處理多線程異步任務時是多麼簡單而有效。
AsyncTask, Loader和Executor…… 拜托!
Android中有很多種執行異步操作的方法(指平行於UI線程的)。AsyncTask對於用戶來說是最簡單的一種機制,並且只需要少量的設置代碼即可。然而,它的使用是有局限的,正如Android官方文檔中所描述的:
AsyncTask被設計成為一個工具類,在它內部包含了Thread和Handler,但它本身並不是通用線程框架的一部分。AsyncTask應該盡可能地被用在執行一些較短的操作中(最多幾秒)。如果你需要在線程中執行較長時間的任務,那麼建議你直接使用java.util.concurrent包中提供的各種API,如Executor、 ThreadPoolExecutor以及FutureTask。
不過即便是執行短時間的操作也會帶來一些問題,特別是在與Activity/Fragment生命周期有關的地方。由於AsyncTask會持續地運行下去(即使啟動它們的Activity/Fragment已經被銷毀了)。這樣,一旦你在onPostExecute方法中試圖對UI進行更新,那麼最終將導致拋出一個IllegalStateException異常。
Android 3.0中引入了Loader API用來解決Activity/Fragment生命周期的問題(它們的確很有效)。Loader API被設計成向Activity/Fragment中以異步方式加載數據。盡管加載數據是一種非常常見的異步操作,但並非唯一一種需要從UI線程中分開的操作。Loader還需要在Activity/Fragment中實現另外一個監聽接口。盡管這麼做沒有錯,但我個人並不喜歡這種模式(我的意思是最終你的代碼中會包含許多的回調函數,導致代碼的可讀性變得很差)。最後,Activity和Fragment也並非唯一需要對異步操作分線程的地方。例如如果在Service裡,你就不能訪問LoaderManager,所以最終你還是得使用AsyncTask或者java.util.concurrent。
java.util.concurrent包很不錯,我在Android和非Android項目中都可以使用。不過使用時需要對其進行多一點兒配置和管理,不象AsyncTask那麼簡單。你需要對ExecutorService進行初始化,管理和監視它的生命周期,並且可能需要跟一些Future對象打交道。
只要使用恰當,AsyncTask、 Loader和Executor都是非常有效的。但在復雜應用中,需要為每個任務選擇合適的工具,最終你可能三種都會用到。這樣你就得維護三種不同的處理並發的框架代碼。
Green Robot來幫忙了!
GR的EventBus中內置了一個非常棒的並發處理機制。在監聽類中,你可以實現4種不同類型的處理方法。當一個匹配事件被發送過來時,EventBus會根據不同的並發模型將事件發送到相應的處理方法中:
onEvent(T event):運行在和被發送事件相同的線程中。
onEventMainThread(T event):運行在主(UI)線程中,不管事件從哪個線程中被發送過來。
onEventAsync(T event):運行在單獨的線程中,即非UI線程,也非發送事件的線程。
onEventBackgroundThread(T event):如果發送事件的線程不是UI線程,則運行在該線程中。如果發送事件的是UI線程,則它運行在由EventBus維護的一個單獨的線程中。多個事件會同步地被這個單獨的後台線程所處理。
這些方法功能強大而且使用簡單。例如有一個比較耗時的操作(可能是網絡調用,大量數據處理等),這一操作需要由UI上的行為來觸發,並且當操作執行完畢後還需對UI進行更新。在這個例子中,UI行為即按鈕點擊,按鈕在activity中,耗時操作在service中。我們可以按下面的方式來實現:
Java
盡管這個例子比較簡單,但它卻非常簡明扼要地說明了問題。這裡即不需要實現監聽接口,也不會出現類似生命周期之類的問題(由於activity只能在它存在的時候才能接收到OperationCompleteEvent事件)。除此之外,如果發生了配置改變(旋轉屏幕)或其他什麼原因導致activity在兩次事件發生之間被銷毀並重建,最終仍可以接收到OperationCompleteEvent事件。
此外,我們也可以容易地想到一些其它用法。比如,如果需要將更新進度發出去,你只需另外實現一個封裝了進度值的事件類,然後將其發送出去即可。或者,如果你想讓其它一些事件(不管是相同還是不同類型)不被並行處理(同步執行),你可以選擇使用onEventBackgroundThread。
依賴Bus
實例化EventBus最簡單的方法就是通過EventBus.getDefault()。然而,在EventBusBuilder類(通過EventBus.builder()獲得)中還包含了另外一些有用的配置方法。特別是在本文中提到過的使用你自己的ExecutorService。缺省情況下EventBus通過Executors.newCachedThreadPool()創建自己的ExecutorService,在大多數情況下都已滿足你的需要。然而,有時你可能仍然想要顯示地控制EventBus所使用的線程數量,這種情況下你就可以象下面這樣初始化EventBus:
Java
EventBus.builder().executorService(Executors.newFixedTheadPool(NUM_THREADS)).installDefaultEventBus();
在EventBusBuilder中另外一些可供配置的是一些和異常處理的有關的控制,以及一個是否允許事件類被繼承的控制開關。這些內容超出了本文所討論的范圍,但我還是建議你仔細去研究一番。GR可能並沒有把這些內容都寫在文檔裡,但如果你讀一讀EventBusBuilder和EventBus的源代碼,相信你會很容易理解它們的。
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
前面文章已經詳細介紹了Android界面的入門技術,相信大家在看完和跟著練習之後,會對於常用的Layout和View都會有一定的了解了,接下來就不再強調介紹界面了