Android 中 EventBus 的使用之多線程事件處理
在這一系列教程的最后一篇中,我想談?wù)凣R的EventBus,在處理多線程異步任務(wù)時是多么簡單而有效。
AsyncTask, Loader和Executor…… 拜托!
Android中有很多種執(zhí)行異步操作的方法(指平行于UI線程的)。AsyncTask對于用戶來說是最簡單的一種機制,并且只需要少量的設(shè)置代碼即可。然而,它的使用是有局限的,正如Android官方文檔中所描述的:
AsyncTask被設(shè)計成為一個工具類,在它內(nèi)部包含了Thread和Handler,但它本身并不是通用線程框架的一部分。AsyncTask應(yīng)該盡可能地被用在執(zhí)行一些較短的操作中(最多幾秒)。如果你需要在線程中執(zhí)行較長時間的任務(wù),那么建議你直接使用java.util.concurrent包中提供的各種API,如Executor、 ThreadPoolExecutor以及FutureTask。
不過即便是執(zhí)行短時間的操作也會帶來一些問題,特別是在與Activity/Fragment生命周期有關(guān)的地方。由于AsyncTask會持續(xù)地運行下去(即使啟動它們的Activity/Fragment已經(jīng)被銷毀了)。這樣,一旦你在onPostExecute方法中試圖對UI進行更新,那么最終將導(dǎo)致拋出一個IllegalStateException異常。
Android 3.0中引入了Loader API用來解決Activity/Fragment生命周期的問題(它們的確很有效)。Loader API被設(shè)計成向Activity/Fragment中以異步方式加載數(shù)據(jù)。盡管加載數(shù)據(jù)是一種非常常見的異步操作,但并非唯一一種需要從UI線程中分開的操作。Loader還需要在Activity/Fragment中實現(xiàn)另外一個監(jiān)聽接口。盡管這么做沒有錯,但我個人并不喜歡這種模式(我的意思是最終你的代碼中會包含許多的回調(diào)函數(shù),導(dǎo)致代碼的可讀性變得很差)。最后,Activity和Fragment也并非唯一需要對異步操作分線程的地方。例如如果在Service里,你就不能訪問LoaderManager,所以最終你還是得使用AsyncTask或者java.util.concurrent。
java.util.concurrent包很不錯,我在Android和非Android項目中都可以使用。不過使用時需要對其進行多一點兒配置和管理,不象AsyncTask那么簡單。你需要對ExecutorService進行初始化,管理和監(jiān)視它的生命周期,并且可能需要跟一些Future對象打交道。
只要使用恰當(dāng),AsyncTask、 Loader和Executor都是非常有效的。但在復(fù)雜應(yīng)用中,需要為每個任務(wù)選擇合適的工具,最終你可能三種都會用到。這樣你就得維護三種不同的處理并發(fā)的框架代碼。
Green Robot來幫忙了!
GR的EventBus中內(nèi)置了一個非常棒的并發(fā)處理機制。在監(jiān)聽類中,你可以實現(xiàn)4種不同類型的處理方法。當(dāng)一個匹配事件被發(fā)送過來時,EventBus會根據(jù)不同的并發(fā)模型將事件發(fā)送到相應(yīng)的處理方法中:
onEvent(T event):運行在和被發(fā)送事件相同的線程中。
onEventMainThread(T event):運行在主(UI)線程中,不管事件從哪個線程中被發(fā)送過來。
onEventAsync(T event):運行在單獨的線程中,即非UI線程,也非發(fā)送事件的線程。
onEventBackgroundThread(T event):如果發(fā)送事件的線程不是UI線程,則運行在該線程中。如果發(fā)送事件的是UI線程,則它運行在由EventBus維護的一個單獨的線程中。多個事件會同步地被這個單獨的后臺線程所處理。
這些方法功能強大而且使用簡單。例如有一個比較耗時的操作(可能是網(wǎng)絡(luò)調(diào)用,大量數(shù)據(jù)處理等),這一操作需要由UI上的行為來觸發(fā),并且當(dāng)操作執(zhí)行完畢后還需對UI進行更新。在這個例子中,UI行為即按鈕點擊,按鈕在activity中,耗時操作在service中。我們可以按下面的方式來實現(xiàn):
Java
盡管這個例子比較簡單,但它卻非常簡明扼要地說明了問題。這里即不需要實現(xiàn)監(jiān)聽接口,也不會出現(xiàn)類似生命周期之類的問題(由于activity只能在它存在的時候才能接收到OperationCompleteEvent事件)。除此之外,如果發(fā)生了配置改變(旋轉(zhuǎn)屏幕)或其他什么原因?qū)е耡ctivity在兩次事件發(fā)生之間被銷毀并重建,最終仍可以接收到OperationCompleteEvent事件。
此外,我們也可以容易地想到一些其它用法。比如,如果需要將更新進度發(fā)出去,你只需另外實現(xiàn)一個封裝了進度值的事件類,然后將其發(fā)送出去即可?;蛘?,如果你想讓其它一些事件(不管是相同還是不同類型)不被并行處理(同步執(zhí)行),你可以選擇使用onEventBackgroundThread。
依賴Bus
實例化EventBus最簡單的方法就是通過EventBus.getDefault()。然而,在EventBusBuilder類(通過EventBus.builder()獲得)中還包含了另外一些有用的配置方法。特別是在本文中提到過的使用你自己的ExecutorService。缺省情況下EventBus通過Executors.newCachedThreadPool()創(chuàng)建自己的ExecutorService,在大多數(shù)情況下都已滿足你的需要。然而,有時你可能仍然想要顯示地控制EventBus所使用的線程數(shù)量,這種情況下你就可以象下面這樣初始化EventBus:
Java
EventBus.builder().executorService(Executors.newFixedTheadPool(NUM_THREADS)).installDefaultEventBus();
在EventBusBuilder中另外一些可供配置的是一些和異常處理的有關(guān)的控制,以及一個是否允許事件類被繼承的控制開關(guān)。這些內(nèi)容超出了本文所討論的范圍,但我還是建議你仔細去研究一番。GR可能并沒有把這些內(nèi)容都寫在文檔里,但如果你讀一讀EventBusBuilder和EventBus的源代碼,相信你會很容易理解它們的。
相關(guān)文章
Android ListView實現(xiàn)下拉頂部圖片變大效果
這篇文章主要為大家詳細介紹了Android ListView實現(xiàn)下拉頂部圖片變大,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Android實現(xiàn)QQ登錄界面遇到問題及解決方法
本文給大家介紹android仿qq登錄界面的實現(xiàn)代碼,在實現(xiàn)此功能過程中遇到各種問題,但是最終都順利解決,如果大家對android qq登錄界面實現(xiàn)方法感興趣的朋友一起學(xué)習(xí)吧2016-09-09Android開發(fā)中比較耗時的一些操作小結(jié)
這篇文章主要介紹了Android開發(fā)中比較耗時的一些操作小結(jié),本文根據(jù)實際開發(fā)經(jīng)驗總結(jié)了6條比較耗時的編程操作,請大家注意下,需要的朋友可以參考下2015-06-06android?ViewPager實現(xiàn)一個無限輪播圖
大家好,本篇文章主要講的是android?ViewPager實現(xiàn)一個無限輪播圖,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下2022-02-02Android使用NestedScrollView?內(nèi)嵌RecycleView滑動沖突問題解決
這篇文章主要介紹了Android使用NestedScrollView?內(nèi)嵌RecycleView滑動沖突問題解決,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-06-06TabLayout+ViewPager實現(xiàn)切頁的示例代碼
這篇文章主要介紹了TabLayout+ViewPager實現(xiàn)切頁的示例代碼,可實現(xiàn)左右滑動切換視圖界面和點擊切換,非常具有實用價值,需要的朋友可以參考下2019-01-01