2010年10月5日 星期二

深入研究 IntentService 原始碼

IntentService Source Code Internals

小心,AsyncTask 不是萬能的 中,我提到 IntentService 是你在實現下載功能時,最好用的武器之一。

這次,我們來深入研究 IntentService 的原始碼,看他是如何用短短幾行代碼,來解決這個問題。

從 class 的宣告中,你可以知道 IntentService 就是一般的 Service。

接著他定義一個 ServiceHandler。一般我們在創建 Handler 時,都是用預設的建構式。預設的 Handler 建構式,是依附在當前的 thread 身上。所以一般開發者都以為 Handler 一定是依附在 main-thread 上,這是不對的觀念。

這個 ServiceHandler,是依附在一個 non-UI thread 的 Looper 上。Looper 就是訊息迴圈 (message loop),這是 Android UI 在處理各式訊息時,最重要的元件之一。而 ServiceHandler 建構式中所傳入的 Looper 物件,是在 onCreate() 中所建立的。

看到了吧,mServiceLooper 就是 thread 物件中的 Looper 物件。這個 thread 物件就是一個 HandlerThread,他是一個 non-UI thread。

所以現在這個 Service,一個帶有 message loop(mServiceLooper) 的 non-UI thread (mServiceHandler) 正一直在背後執行著,監看是否有任何傳入的 message。

接著,每當你呼叫 startService(new Intent(this, MyIntentService.class)) 啟動 IntentService 時,其實他只是很簡單地送出一個 message 到 mServiceHandler 上。

我們知道這個 message 最終會由 Handler.handleMessage() 來處理。由於整個 mServiceHandler 是在 non-UI thread 中執行。當他在 handleMessage() 中呼叫你的 onHandleIntent() 時,你的 onHandleIntent() 自然也是在 non-UI thread 中執行。

onHandleIntent() 執行結束後,接著呼叫 stopSelf(int)。由於 IntentService 是設計用來可以在一個 Service 中服務多項工作,因此在這裡,不可以呼叫 stopSelf()。

如果你呼叫 startService() 多次,每一次的呼叫都會被轉成一個 message,並放在 mServiceLooper 的 message queue 中,等待被服務。一個 message 所對應的工作被完成後,才會繼續服務下一個工作。所以,這些等待被服務的工作,並不是一起並行 (Concurrent) 的,而是循序執行。

當所有的工作都處裡完時,也是 Service 該結束的時候。

在 onDestroy() 中,唯一要做的事,就是要將 mServiceLooper 停下來。

從研究這個 IntentService 的原始碼,我們可以學到如何運用簡單的 pattern (Service + Handler + HandlerThread),幫我們更簡易與有系統地,完成我們所想要做的事。

最後我們先前說過,在 IntentServcie 中等待被服務的工作,並不會被一起並行,而是循序執行。如果你今天想要這些等待被服務的工作,能夠一起被並行,在研讀完這個 IntentService 的原始碼後,你自己知不知道如何寫個可支援並行工作的 IntentService?

提示:可以用 Service + AsyncTask 的組合。

5 則留言:

400 提到...

好深奧阿...留個名晚點研究

使徒 提到...

老師您好
我做了一個簡單的程式(主要都是參考範本程式),模擬器上可以執行,但是當我將檔案放到SD卡上,想做實機裝機測試時,它卻不讓我裝,應該說安裝失敗,這是什麼原因呢?點選該檔時,有進入安裝畫面,但是卻顯示無法安裝

我的程式是使用2.0的版本,手機是2.1的版本,硬體是MUCHTEL A1,在建立APK檔時,是使用不簽署金鑰的方式

【Palatis】 提到...

嘖嘖,我試著讓 Looper 不用 MessageQueue 改用 "MessageStack",可是失敗了 XD~

本來想用 IntentService 來實做 LazyAdapter...

匿名 提到...

並行也可以用 handlerthread + handler 達成, 不過需要小心控制回收問題.

匿名 提到...

可是 asyncTask 有 pre post action 比較容易處理
IntentService 好像只成靠全域變數來處理

張貼留言