Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 溫故知新-Service學習筆記

溫故知新-Service學習筆記

編輯:關於Android編程

1.Services

Service 是一個可以在後台執行長時間運行操作而不使用用戶界面的應用組件。服務可由其他應用組件啟動,而且即使用戶切換到其他應用,服務仍將在後台繼續運行。 此外,組件可以綁定到服務,以與之進行交互,甚至是執行進程間通信 (IPC)。 例如,服務可以處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序交互,而所有這一切均可在後台進行。

服務基本上分為兩種形式:

啟動

當應用組件(如 Activity)通過調用 startService() 啟動服務時,服務即處於“啟動”狀態。
一旦啟動,服務即可在後台無限期運行,即使啟動服務的組件已被銷毀也不受影響。 已啟動的服務通常是執行單一操作,而且不會將結果返回給調用方。
例如,它可能通過網絡下載或上傳文件。 操作完成後,服務會自行停止運行。

綁定

當應用組件通過調用 bindService() 綁定到服務時,服務即處於“綁定”狀態。
綁定服務提供了一個客戶端-服務器接口,允許組件與服務進行交互、發送請求、獲取結果,甚至是利用進程間通信 (IPC) 跨進程執行這些操作。 
僅當與另一個應用組件綁定時,綁定服務才會運行。 多個組件可以同時綁定到該服務,但全部取消綁定後,該服務即會被銷毀。

注意:服務在其托管進程的主線程中運行,它既不創建自己的線程,也不在單獨的進程中運行(除非另行指定)。 這意味著,如果服務將執行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或聯網),則應在服務內創建新線程來完成這項工作。通過使用單獨的線程,可以降低發生“應用無響應”(ANR) 錯誤的風險,而應用的主線程仍可繼續專注於運行用戶與 Activity 之間的交互。

1.回調方法

int onStartCommand (Intent intent, int flags, int startId)

當另一個組件(如 Activity)通過調用 startService() 請求啟動服務時,系統將調用此方法。一旦執行此方法,服務即會啟動並可在後台無限期運行。 如果您實現此方法,則在服務工作完成後,需要由您通過調用 stopSelf() 或 stopService() 來停止服務。(如果您只想提供綁定,則無需實現此方法。)

IBinder onBind (Intent intent)

當另一個組件想通過調用 bindService() 與服務綁定(例如執行 RPC)時,系統將調用此方法。在此方法的實現中,您必須通過返回 IBinder 提供一個接口,供客戶端用來與服務進行通信。請務必實現此方法,但如果您並不希望允許綁定,則應返回 null。

void onCreate ()

首次創建服務時,系統將調用此方法來執行一次性設置程序(在調用 onStartCommand() 或 onBind() 之前)。如果服務已在運行,則不會調用此方法。

void onDestroy ()

當服務不再使用且將被銷毀時,系統將調用此方法。服務應該實現此方法來清理所有資源,如線程、注冊的偵聽器、接收器等。 這是服務接收的最後一個調用。

如果組件通過調用 startService() 啟動服務(這會導致對 onStartCommand() 的調用),則服務將一直運行,直到服務使用 stopSelf() 自行停止運行,或由其他組件通過調用 stopService() 停止它為止。

如果組件是通過調用 bindService() 來創建服務(且未調用 onStartCommand()),則服務只會在該組件與其綁定時運行。一旦該服務與所有客戶端之間的綁定全部取消,系統便會銷毀它。

僅當內存過低且必須回收系統資源以供具有用戶焦點的 Activity 使用時,Android 系統才會強制停止服務。如果將服務綁定到具有用戶焦點的 Activity,則它不太可能會終止;如果將服務聲明為在前台運行(稍後討論),則它幾乎永遠不會終止。或者,如果服務已啟動並要長時間運行,則系統會隨著時間的推移降低服務在後台任務列表中的位置,而服務也將隨之變得非常容易被終止;如果服務是啟動服務,則您必須將其設計為能夠妥善處理系統對它的重啟。 如果系統終止服務,那麼一旦資源變得再次可用,系統便會重啟服務(不過這還取決於從 onStartCommand() 返回的值)。

2.使用清單文件聲明服務

為了確保應用的安全性,請始終使用顯式 Intent 啟動或綁定 Service,且不要為服務聲明 Intent 過濾器。 啟動哪個服務存在一定的不確定性,而如果對這種不確定性的考量非常有必要,則可為服務提供 Intent 過濾器並從 Intent 中排除相應的組件名稱,但隨後必須使用 setPackage() 方法設置 Intent 的軟件包,這樣可以充分消除目標服務的不確定性。

此外,還可以通過添加 android:exported 屬性並將其設置為 “false”,確保服務僅適用於您的應用。這可以有效阻止其他應用啟動您的服務,即便在使用顯式 Intent 時也如此。

3.創建啟動服務

啟動服務由另一個組件通過調用 startService() 啟動,這會導致調用服務的 onStartCommand() 方法。

服務啟動之後,其生命周期即獨立於啟動它的組件,並且可以在後台無限期地運行,即使啟動服務的組件已被銷毀也不受影響。 因此,服務應通過調用 stopSelf() 結束工作來自行停止運行,或者由另一個組件通過調用 stopService() 來停止它。

應用組件(如 Activity)可以通過調用 startService() 方法並傳遞 Intent 對象 (指定服務並包含待使用服務的所有數據)來啟動服務。服務通過 onStartCommand() 方法接收此 Intent。

例如,假設某 Activity 需要將一些數據保存到在線數據庫中。該 Activity 可以啟動一個協同服務,並通過向 startService() 傳遞一個 Intent,為該服務提供要保存的數據。服務通過 onStartCommand() 接收 Intent,連接到 Internet 並執行數據庫事務。事務完成之後,服務會自行停止運行並隨即被銷毀。

注意:默認情況下,服務與服務聲明所在的應用運行於同一進程,而且運行於該應用的主線程中。 因此,如果服務在用戶與來自同一應用的 Activity 進行交互時執行密集型或阻止性操作,則會降低 Activity 性能。 為了避免影響應用性能,您應在服務內啟動新線程。

Service與IntentService:

Service

這是適用於所有服務的基類。擴展此類時,必須創建一個用於執行所有服務工作的新線程,因為默認情況下,服務將使用應用的主線程,這會降低應用正在運行的所有 Activity 的性能。

IntentService

這是 Service 的子類,它使用工作線程逐一處理所有啟動請求。如果您不要求服務同時處理多個請求,這是最好的選擇。 您只需實現 onHandleIntent() 方法即可,該方法會接收每個啟動請求的 Intent,使您能夠執行後台工作。

4.擴展 IntentService 類

由於大多數啟動服務都不必同時處理多個請求(實際上,這種多線程情況可能很危險),因此使用 IntentService 類實現服務也許是最好的選擇。

IntentService 執行以下操作:

創建默認的工作線程,用於在應用的主線程外執行傳遞給 onStartCommand() 的所有 Intent。 創建工作隊列,用於將一個 Intent 逐一傳遞給 onHandleIntent() 實現,這樣您就永遠不必擔心多線程問題。 在處理完所有啟動請求後停止服務,因此您永遠不必調用 stopSelf()。 提供 onBind() 的默認實現(返回 null)。 提供 onStartCommand() 的默認實現,可將 Intent 依次發送到工作隊列和 onHandleIntent() 實現。

綜上所述,您只需實現 onHandleIntent() 來完成客戶端提供的工作即可。

5.擴展Service類

使用 IntentService 顯著簡化了啟動服務的實現。但是,若要求服務執行多線程(而不是通過工作隊列處理啟動請求),則可擴展 Service 類來處理每個 Intent。

onStartCommand() 返回的值必須是以下常量之一:

START_NOT_STICKY

如果系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務。這是最安全的選項,可以避免在不必要時以及應用能夠輕松重啟所有未完成的作業時運行服務。

START_STICKY

如果系統在 onStartCommand() 返回後終止服務,則會重建服務並調用 onStartCommand(),但絕對不會重新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啟動服務(在這種情況下,將傳遞這些 Intent ),否則系統會通過空 Intent 調用 onStartCommand()。這適用於不執行命令、但無限期運行並等待作業的媒體播放器(或類似服務)。

START_REDELIVER_INTENT

如果系統在 onStartCommand() 返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 調用 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載文件)的服務。

6.啟動服務

您可以通過將 Intent(指定要啟動的服務)傳遞給 startService(),從 Activity 或其他應用組件啟動服務。Android 系統調用服務的 onStartCommand() 方法,並向其傳遞 Intent。

startService() 方法將立即返回,且 Android 系統調用服務的 onStartCommand() 方法。如果服務尚未運行,則系統會先調用 onCreate(),然後再調用 onStartCommand()。

如果服務亦未提供綁定,則使用 startService() 傳遞的 Intent 是應用組件與服務之間唯一的通信模式。但是,如果您希望服務返回結果,則啟動服務的客戶端可以為廣播創建一個 PendingIntent (使用 getBroadcast()),並通過啟動服務的 Intent 傳遞給服務。然後,服務就可以使用廣播傳遞結果。

多個服務啟動請求會導致多次對服務的 onStartCommand() 進行相應的調用。但是,要停止服務,只需一個服務停止請求(使用 stopSelf() 或 stopService())即可。

7.停止服務

啟動服務必須管理自己的生命周期。也就是說,除非系統必須回收內存資源,否則系統不會停止或銷毀服務,而且服務在 onStartCommand() 返回後會繼續運行。因此,服務必須通過調用 stopSelf() 自行停止運行,或者由另一個組件通過調用 stopService() 來停止它。

一旦請求使用 stopSelf() 或 stopService() 停止服務,系統就會盡快銷毀服務。

但是,如果服務同時處理多個 onStartCommand() 請求,則您不應在處理完一個啟動請求之後停止服務,因為您可能已經收到了新的啟動請求(在第一個請求結束時停止服務會終止第二個請求)。為了避免這一問題,您可以使用 stopSelf(int) 確保服務停止請求始終基於最近的啟動請求。也就說,在調用 stopSelf(int) 時,傳遞與停止請求的 ID 對應的啟動請求的 ID(傳遞給 onStartCommand() 的 startId) 。然後,如果在您能夠調用 stopSelf(int) 之前服務收到了新的啟動請求, ID 就不匹配,服務也就不會停止。

注意:為了避免浪費系統資源和消耗電池電量,應用必須在工作完成之後停止其服務。 如有必要,其他組件可以通過調用 stopService() 來停止服務。即使為服務啟用了綁定,一旦服務收到對 onStartCommand() 的調用,您始終仍須親自停止服務。

8.創建綁定服務

綁定服務允許應用組件通過調用 bindService() 與其綁定,以便創建長期連接(通常不允許組件通過調用 startService() 來啟動它)。

如需與 Activity 和其他應用組件中的服務進行交互,或者需要通過進程間通信 (IPC) 向其他應用公開某些應用功能,則應創建綁定服務。

要創建綁定服務,必須實現 onBind() 回調方法以返回 IBinder,用於定義與服務通信的接口。然後,其他應用組件可以調用 bindService() 來檢索該接口,並開始對服務調用方法。服務只用於與其綁定的應用組件,因此如果沒有組件綁定到服務,則系統會銷毀服務(您不必按通過 onStartCommand() 啟動的服務那樣來停止綁定服務)。

要創建綁定服務,首先必須定義指定客戶端如何與服務通信的接口。 服務與客戶端之間的這個接口必須是 IBinder 的實現,並且服務必須從 onBind() 回調方法返回它。一旦客戶端收到 IBinder,即可開始通過該接口與服務進行交互。

多個客戶端可以同時綁定到服務。客戶端完成與服務的交互後,會調用 unbindService() 取消綁定。一旦沒有客戶端綁定到該服務,系統就會銷毀它。

9.向用戶發送通知

一旦運行起來,服務即可使用 Toast 通知或狀態欄通知來通知用戶所發生的事件。

Toast 通知是指出現在當前窗口的表面、片刻隨即消失不見的消息,而狀態欄通知則在狀態欄提供內含消息的圖標,用戶可以選擇該圖標來采取操作(例如啟動 Activity)。

通常,當某些後台工作已經完成(例如文件下載完成)且用戶現在可以對其進行操作時,狀態欄通知是最佳方法。 當用戶從展開視圖中選定通知時,通知即可啟動 Activity(例如查看已下載的文件)。

10.在前台運行服務

前台服務被認為是用戶主動意識到的一種服務,因此在內存不足時,系統也不會考慮將其終止。 前台服務必須為狀態欄提供通知,狀態欄位於“正在進行”標題下方,這意味著除非服務停止或從前台刪除,否則不能清除通知。

例如,應該將從服務播放音樂的音樂播放器設置為在前台運行,這是因為用戶明確意識到其操作。 狀態欄中的通知可能表示正在播放的歌曲,並允許用戶啟動 Activity 來與音樂播放器進行交互。

要請求讓服務運行於前台,請調用 startForeground()。此方法取兩個參數:唯一標識通知的整型數和狀態欄的 Notification。例如:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

注意:提供給 startForeground() 的整型 ID 不得為 0。

要從前台刪除服務,請調用 stopForeground()。此方法取一個布爾值,指示是否也刪除狀態欄通知。 此方法絕對不會停止服務。 但是,如果您在服務正在前台運行時將其停止,則通知也會被刪除。

11.管理服務生命周期

服務生命周期(從創建到銷毀)可以遵循兩條不同的路徑:

啟動服務

該服務在其他組件調用 startService() 時創建,然後無限期運行,且必須通過調用 stopSelf() 來自行停止運行。此外,其他組件也可以通過調用 stopService() 來停止服務。服務停止後,系統會將其銷毀。

綁定服務

該服務在另一個組件(客戶端)調用 bindService() 時創建。然後,客戶端通過 IBinder 接口與服務進行通信。客戶端可以通過調用 unbindService() 關閉連接。多個客戶端可以綁定到相同服務,而且當所有綁定全部取消後,系統即會銷毀該服務。 (服務不必自行停止運行。)

這兩條路徑並非完全獨立。也就是說,您可以綁定到已經使用 startService() 啟動的服務。例如,可以通過使用 Intent(標識要播放的音樂)調用 startService() 來啟動後台音樂服務。隨後,可能在用戶需要稍加控制播放器或獲取有關當前播放歌曲的信息時,Activity 可以通過調用 bindService() 綁定到服務。在這種情況下,除非所有客戶端均取消綁定,否則 stopService() 或 stopSelf() 不會真正停止服務。

12.實現生命周期回調

\<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPs2ouf3Ktc/W1eLQqbe9t6ijrMT6v8nS1Lzgv9i3/s7xyfrD/Nbcxtq1xMG9uPbHtszX0a27t6O6PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L3A+DQo8cD48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT4qKrf+zvG1xNX7uPbJ+sP81tzG2rTTtffTwyBvbkNyZWF0ZSgpIL+qyrzG8KOstb0gb25EZXN0cm95KCkgt7W72MqxveHK+KGj0+sgQWN0aXZpdHkgwODLxqOst/7O8dKy1Nogb25DcmVhdGUoKSDW0M3qs8mz9cq8yejWw6OssqLU2iBvbkRlc3Ryb3koKSDW0MrNt8XL+dPQyqPT4NfK1LSho8D9yOejrNL0wNaypbfFt/7O8b/J0tTU2iBvbkNyZWF0ZSgpINbQtLS9qNPD09qypbfF0vTA1rXEz9+zzKOsyLu689TaIG9uRGVzdHJveSgpINbQzaPWubjDz9+zzKGjPGJyIC8+DQrO3sLbt/7O8crHzai5/SBzdGFydFNlcnZpY2UoKSC7ucrHIGJpbmRTZXJ2aWNlKCkgtLS9qKOstry74c6qy/nT0Lf+zvG199PDIG9uQ3JlYXRlKCkgus0gb25EZXN0cm95KCkgt723qKGjKio8L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxzdHJvbmc+t/7O8bXE09DQp8n6w/zW3MbatNO199PDIG9uU3RhcnRDb21tYW5kKCkgu/Igb25CaW5kKCkgt723qL+qyryho8O/1ta3vbeovvnT0CBJbnRlbnQgttTP86OsuMO21M/zt9ax8LSrtd21vSBzdGFydFNlcnZpY2UoKSC78iBiaW5kU2VydmljZSgpoaM8L3N0cm9uZz48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPrbU09rG9Lavt/7O8aOs09DQp8n6w/zW3Mba0+vV+7j2yfrD/NbcxtrNrMqxveHK+KOovLSx48rH1Nogb25TdGFydENvbW1hbmQoKSC3tbvY1q6686Ost/7O8cjUyLu0ptPau+62r9e0zKyjqaGjttTT2rDztqi3/s7xo6zT0NCnyfrD/NbcxtrU2iBvblVuYmluZCgpILe1u9jKsb3hyvihozwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9wPg0KPHA+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+16Kjur6hudzG9Lavt/7O8crHzai5/bX308Mgc3RvcFNlbGYoKSC78iBzdG9wU2VydmljZSgpIMC0zaPWuaOstavKx7jDt/7O8bKizt7P4NOmtcS72LX3o6jDu9PQIG9uU3RvcCgpILvYtfejqaGj0vK0y6Oss/23x7f+zvGw87aotb2/zbuntsujrLfx1PLU2rf+zvHNo9a5yrGjrM+1zbO74b2rxuTP+rvZJm1kYXNoO29uRGVzdHJveSgpIMrHvdPK1bW9tcTOqNK7u9i196GjPC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L3A+DQo8aDEgaWQ9"2bind-services">2.Bind Services

綁定服務是客戶端-服務器接口中的服務器。綁定服務可讓組件(例如 Activity)綁定到服務、發送請求、接收響應,甚至執行進程間通信 (IPC)。 綁定服務通常只在為其他應用組件服務時處於活動狀態,不會無限期在後台運行。

1.基礎知識

綁定服務是 Service 類的實現,可讓其他應用與其綁定和交互。要提供服務綁定,您必須實現 onBind() 回調方法。該方法返回的 IBinder 對象定義了客戶端用來與服務進行交互的編程接口。

客戶端可通過調用 bindService() 綁定到服務。調用時,它必須提供 ServiceConnection 的實現,後者會監控與服務的連接。bindService() 方法會立即無值返回,但當 Android 系統創建客戶端與服務之間的連接時,會調用 ServiceConnection 上的 onServiceConnected(),向客戶端傳遞用來與服務通信的 IBinder。

多個客戶端可同時連接到一個服務。不過,只有在第一個客戶端綁定時,系統才會調用服務的 onBind() 方法來檢索 IBinder。系統隨後無需再次調用 onBind(),便可將同一 IBinder 傳遞至任何其他綁定的客戶端。

當最後一個客戶端取消與服務的綁定時,系統會將服務銷毀(除非 startService() 也啟動了該服務)。

2.創建綁定服務

創建提供綁定的服務時,您必須提供 IBinder,用以提供客戶端用來與服務進行交互的編程接口。 您可以通過三種方法定義接口:

擴展 Binder 類

如果服務是供您的自有應用專用,並且在與客戶端相同的進程中運行(常見情況),則應通過擴展 Binder 類並從 onBind() 返回它的一個實例來創建接口。客戶端收到 Binder 後,可利用它直接訪問 Binder 實現中乃至 Service 中可用的公共方法。

如果服務只是您的自有應用的後台工作線程,則優先采用這種方法。 不以這種方式創建接口的唯一原因是,您的服務被其他應用或不同的進程占用。

使用 Messenger

如需讓接口跨不同的進程工作,則可使用 Messenger 為服務創建接口。服務可以這種方式定義對應於不同類型 Message 對象的 Handler。此 Handler 是 Messenger 的基礎,後者隨後可與客戶端分享一個 IBinder,從而讓客戶端能利用 Message 對象向服務發送命令。此外,客戶端還可定義自有 Messenger,以便服務回傳消息。

這是執行進程間通信 (IPC) 的最簡單方法,因為 Messenger 會在單一線程中創建包含所有請求的隊列,這樣您就不必對服務進行線程安全設計。

使用 AIDL

AIDL(Android 接口定義語言)執行所有將對象分解成原語的工作,操作系統可以識別這些原語並將它們編組到各進程中,以執行 IPC。之前采用 Messenger 的方法實際上是以 AIDL 作為其底層結構。如上所述,Messenger 會在單一線程中創建包含所有客戶端請求的隊列,以便服務一次接收一個請求。不過,如果您想讓服務同時處理多個請求,則可直接使用 AIDL。 在此情況下,您的服務必須具備多線程處理能力,並采用線程安全式設計。

如需直接使用 AIDL,您必須創建一個定義編程接口的 .aidl 文件。Android SDK 工具利用該文件生成一個實現接口並處理 IPC 的抽象類,您隨後可在服務內對其進行擴展。

注:大多數應用“都不會”使用 AIDL 來創建綁定服務,因為它可能要求具備多線程處理能力,並可能導致實現的復雜性增加。

3.擴展 Binder 類

如果您的服務僅供本地應用使用,不需要跨進程工作,則可以實現自有 Binder 類,讓您的客戶端通過該類直接訪問服務中的公共方法。

注:此方法只有在客戶端和服務位於同一應用和進程內這一最常見的情況下方才有效。 例如,對於需要將 Activity 綁定到在後台播放音樂的自有服務的音樂應用,此方法非常有效。

以下是具體的設置方法:

1).在您的服務中,創建一個可滿足下列任一要求的 Binder 實例:

包含客戶端可調用的公共方法 返回當前 Service 實例,其中包含客戶端可調用的公共方法 或返回由服務承載的其他類的實例,其中包含客戶端可調用的公共方法

2).從 onBind() 回調方法返回此 Binder 實例。

3).在客戶端中,從 onServiceConnected() 回調方法接收 Binder,並使用提供的方法調用綁定服務。

注:之所以要求服務和客戶端必須在同一應用內,是為了便於客戶端轉換返回的對象和正確調用其 API。服務和客戶端還必須在同一進程內,因為此方法不執行任何跨進程編組。

代碼示例:

public class LocalService extends Service {
    private final IBinder mBinder = new LocalBinder();
    private final Random mGenerator = new Random();

    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    public void onButtonClick(View v) {
        if (mBound) {
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

4.使用 Messenger

如需讓服務與遠程進程通信,則可使用 Messenger 為您的服務提供接口。利用此方法,您無需使用 AIDL 便可執行進程間通信 (IPC)。

以下是 Messenger 的使用方法摘要:

服務實現一個 Handler,由其接收來自客戶端的每個調用的回調 Handler 用於創建 Messenger 對象(對 Handler 的引用) Messenger 創建一個 IBinder,服務通過 onBind() 使其返回客戶端 客戶端使用 IBinder 將 Messenger(引用服務的 Handler)實例化,然後使用後者將 Message 對象發送給服務 服務在其 Handler 中(具體地講,是在 handleMessage() 方法中)接收每個 Message

這樣,客戶端並沒有調用服務的“方法”。而客戶端傳遞的“消息”(Message 對象)是服務在其 Handler 中接收的。

代碼示例:

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    final Messenger mMessenger = new Messenger(new IncomingHandler());

    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

public class ActivityMessenger extends Activity {
    Messenger mService = null;
    boolean mBound;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

5.綁定到服務

應用組件(客戶端)可通過調用 bindService() 綁定到服務。Android 系統隨後調用服務的 onBind() 方法,該方法返回用於與服務交互的 IBinder。

綁定是異步的。bindService() 會立即返回,“絕對不會”使 IBinder 返回客戶端。要接收 IBinder,客戶端必須創建一個 ServiceConnection 實例,並將其傳遞給 bindService()。ServiceConnection 包括一個回調方法,系統通過調用它來傳遞 IBinder。

注:只有 Activity、服務和內容提供程序可以綁定到服務—您無法從廣播接收器綁定到服務。

因此,要想從您的客戶端綁定到服務,您必須:

1).實現 ServiceConnection。

您的實現必須重寫兩個回調方法:

void onServiceConnected (ComponentName name, IBinder service)

系統會調用該方法以傳遞服務的 onBind() 方法返回的 IBinder。

void onServiceDisconnected (ComponentName name)

Android 系統會在與服務的連接意外中斷時(例如當服務崩潰或被終止時)調用該方法。當客戶端取消綁定時,系統“絕對不會”調用該方法。

2).調用 bindService() 以傳遞 ServiceConnection 實現。

3).當系統調用您的 onServiceConnected() 回調方法時,您可以使用接口定義的方法開始調用服務。

4).要斷開與服務的連接,請調用 unbindService()。

當您的客戶端被銷毀時,它將取消與服務的綁定,但您應該始終在完成與服務的交互時或您的 Activity 暫停時取消綁定,以便服務能夠在未被占用時關閉。

代碼示例:

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    public void onServiceDisconnected(ComponentName className) {
        mBound = false;
    }
};

客戶端可通過將此 ServiceConnection 傳遞至 bindService() 綁定到服務。例如:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService() 的第一個參數是一個 Intent,用於顯式命名要綁定的服務(但 Intent 可能是隱式的) 第二個參數是 ServiceConnection 對象 第三個參數是一個指示綁定選項的標志。它通常應該是 BIND_AUTO_CREATE,以便創建尚未激活的服務。 其他可能的值為 BIND_DEBUG_UNBIND 和 BIND_NOT_FOREGROUND,或 0(表示無)。

6.附加說明

您應該始終捕獲 DeadObjectException 異常,它們是在連接中斷時引發的。這是遠程方法引發的唯一異常 對象是跨進程計數的引用 您通常應該在客戶端生命周期的匹配引入 (bring-up) 和退出 (tear-down) 時刻期間配對綁定和取消綁定。 如果您只需要在 Activity 可見時與服務交互,則應在 onStart() 期間綁定,在 onStop() 期間取消綁定。 如果您希望 Activity 在後台停止運行狀態下仍可接收響應,則可在 onCreate() 期間綁定,在 onDestroy() 期間取消綁定。請注意,這意味著您的 Activity 在其整個運行過程中(甚至包括後台運行期間)都需要使用服務,因此如果服務位於其他進程內,那麼當您提高該進程的權重時,系統終止該進程的可能性會增加

7.管理綁定服務的生命周期

當服務與所有客戶端之間的綁定全部取消時,Android 系統便會銷毀服務(除非還使用 onStartCommand() 啟動了該服務)。因此,如果您的服務是純粹的綁定服務,則無需對其生命周期進行管理—Android 系統會根據它是否綁定到任何客戶端代您管理。

不過,如果您選擇實現 onStartCommand() 回調方法,則您必須顯式停止服務,因為系統現在已將服務視為已啟動。在此情況下,服務將一直運行到其通過 stopSelf() 自行停止,或其他組件調用 stopService() 為止,無論其是否綁定到任何客戶端。

此外,如果您的服務已啟動並接受綁定,則當系統調用您的 onUnbind() 方法時,如果您想在客戶端下一次綁定到服務時接收 onRebind() 調用(而不是接收 onBind() 調用),則可選擇返回 true。onRebind() 返回空值,但客戶端仍在其 onServiceConnected() 回調中接收 IBinder。下文圖 1 說明了這種生命周期的邏輯。

\

3.Activity與Service使用Message通信

代碼示例:

public class MainActivity extends Activity {
    Messenger mService = null;
    boolean mIsBound;
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyService.MSG_SET_INT_VALUE:
                break;
            case MyService.MSG_SET_STRING_VALUE:
                String str1 = msg.getData().getString("str1");
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            try {
                Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
            }
            catch (RemoteException e) {
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mService = null;
        }
    };

    private void sendMessageToService(int intvaluetosend) {
        if (mIsBound) {
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                }
                catch (RemoteException e) {
                }
            }
        }
    }

    void doBindService() {
        bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    void doUnbindService() {
        if (mIsBound) {
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                }
                catch (RemoteException e) {
                }
            }

            unbindService(mConnection);
            mIsBound = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            doUnbindService();
        }
        catch (Throwable t) {
        }
    }
}

public class MyService extends Service {
    private NotificationManager nm;
    private Timer timer = new Timer();
    private int counter = 0, incrementby = 1;
    private static boolean isRunning = false;

    ArrayList mClients = new ArrayList(); 
    int mValue = 0; 
    static final int MSG_REGISTER_CLIENT = 1;
    static final int MSG_UNREGISTER_CLIENT = 2;
    static final int MSG_SET_INT_VALUE = 3;
    static final int MSG_SET_STRING_VALUE = 4;
    final Messenger mMessenger = new Messenger(new IncomingHandler()); 

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
    class IncomingHandler extends Handler { 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_REGISTER_CLIENT:
                mClients.add(msg.replyTo);
                break;
            case MSG_UNREGISTER_CLIENT:
                mClients.remove(msg.replyTo);
                break;
            case MSG_SET_INT_VALUE:
                incrementby = msg.arg1;
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private void sendMessageToUI(int intvaluetosend) {
        for (int i = mClients.size() - 1; i >= 0; i--) {
            try {
                mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));

                Bundle b = new Bundle();
                b.putString("str1", "ab" + intvaluetosend + "cd");
                Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
                msg.setData(b);
                mClients.get(i).send(msg);
            }
            catch (RemoteException e) {
                mClients.remove(i);
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        showNotification();
        timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
        isRunning = true;
    }
    private void showNotification() {
        nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        CharSequence text = getText(R.string.service_started);
        Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
        notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
        nm.notify(R.string.service_started, notification);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY; 
    }

    public static boolean isRunning() {
        return isRunning;
    }

    private void onTimerTick() {
        try {
            counter += incrementby;
            sendMessageToUI(counter);
        }
        catch (Throwable t) { 
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (timer != null) {timer.cancel();}
        counter=0;
        nm.cancel(R.string.service_started);
        isRunning = false;
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved