顯示具有 ThreadPoolExecutor 標籤的文章。 顯示所有文章
顯示具有 ThreadPoolExecutor 標籤的文章。 顯示所有文章

2010年11月9日 星期二

並行下載的完美解決方案

How to implement the function that supports concurrent downloading?

在看完 小心,AsyncTask 不是萬能的 以及 深入研究 IntentService 原始碼 這兩篇文章後,我想你應該已經對如何寫個正確處理費時工作的 Android 程式,建立起正確的觀念。

從這兩篇文章中,我們也了解到,在 IntentServcie 中等待被服務的工作,並不會被一起並行 (Concurrent),而是循序執行。在多數的案例中,循序執行已能滿足你的需求,而且這樣的程式也比較能夠受控制而不會出錯。

不過在處理網路下載這件事情上,循序執行可不是最好的方式,每件網路下載的工作,免不了要浪費時間在等待網路的傳輸上。為了要善用 CPU 的資源,加速資料的下載工作(例如下載多張圖片),你今天可能會想要這些等待被服務的工作,能夠一起被並行,而不是循序執行。在研讀完這個 IntentService 的原始碼後,你自己知不知道如何寫個可支援並行工作的 IntentService?

如果你曾研讀過 AsyncTask 的原始碼,你應該知道所有的 AsyncTask實例 (instances),都是由同一個 ThreadPoolExecutor 在管理。每一個 AsyncTask 的實例,都會由 ThreadPoolExecutor 中分配到一個 thread 來執行。因此所有的 AsyncTask 實例都是並行的。要實現一個可支援並行工作的 IntentService,我們剛好可以利用 Service + AsyncTask 的組合。

底下是我所實現的 ConcurrentIntentService 的完整程式代碼。

要使用這個 ConcurrentIntentService,和使用 IntentService 是相同的,你只要繼承這個 ConcurrentIntentService,並將費時的工作,移到 onHandleIntent() 中處裡即可。onHandleIntent() 是被 non-UI thread 所喚起的。因此在這個函式中,你可以放心地去執行你的下載工作,而不用擔心會產生任何的 ANR 錯誤。

我們先看 #29~#40 行onStart() 這個函式。

每當你想執行某件費時的工作,透過 Context.startService() 呼叫到 Service.onStart() 時,我們立即產生一個 MyAsyncTask (AsyncTask 的子類,見 #56~#83) 的實例(#37),並將他放到 m_mapIntent2AsyncTask 這個 HashMap 中(#38)。

設計這類的樣式,其中一項重要的工作是決定何時要殺掉 Service。在 IntentService 中,每項工作都是循序執行的,因此他利用 stopSelf(int),來讓系統判斷是否最後一項工作已執行完畢。系統會在最後一項工作已執行完畢時,自動殺掉 Service。不過在 ConcurrentIntentService 中的所有工作,都是並行的,而且每件工作的執行時間也長短不一。因此,我們不能參考 IntentService 的作法,我們必須借助一個 HashMap 來管理,還有哪些待執行的工作,自己決定要殺掉 Service 的時機。

在 MyAsyncTask 這個類別 (#56~#83) 的 doInBackground() 函式中,我們先將欲執行的工作從 m_mapIntent2AsyncTask 中移除。接下來,再呼叫你設計的 onHandleIntent(Intent),每個 AsyncTask 執行完後,最終都會執行 onPostExecute()。因此,我們在這個函式中,判斷 m_mapIntent2AsyncTask 中是否還有待執行的工作。如果沒有,那就是這個 Service 的生命該終結的時候 (#73~#74)。

其他部份的設計邏輯,和 IntentService 類似,請自行研讀。不過,我們為這個 ConcurrentIntentService,還特別加了 cancel() (#19~27) 介面,讓你可以取消所有正在進行或等待執行的工作。

如何在 Android 應用中,順利處理雲端資源的存取工作,一直是應用開發者的痛苦。不過,透過系統提供的 IntentService 與我自行設計的 ConcurrentIntentService,應當可以幫助開發者減少在處理網路下載時所遇到的問題。

要寫 Android 應用,一點都不難,不就是 Java。這是我最常聽到,剛跨過入門檻開發者告訴我的一句話。不過當你曾嘗試寫過 Android 的雲端應用後,你應該有所體會,要寫好的一個 Android 的網路應用,這當中還是有許多門檻需要跨過的。


繼續閱讀全文...