天天看點

關于Service服務常用的知識

一、Service的簡單概述

在背景長時間運作操作而沒有使用者界面的應用元件,服務可由其他元件啟動,即使使用者切換到了其他應用,服務仍将繼續在背景運作。例如,服務可以處理網絡事物,播放音樂,執行檔案I/O,與内容提供程式互動等等。

  • 服務有兩種啟動方式:

    (1)一種是服務可由其他應用元件啟動(Activity),服務一旦被啟動将在背景一直運作,即使啟動服務的元件(Activity)已銷毀也不受影響,一般情況下這種服務執行單一任務,如網絡下載下傳或者檔案上傳,一旦任務執行完畢,服務會自動停止。

    (2)另一種是元件可以綁定到服務,相當于提供了一個用戶端-伺服器的接口,允許元件和服務進行互動,發送請求,擷取結果,甚至是執行程序間通信(IPC),服務可以同時綁定多個元件,當所有的元件都解綁時,服務才會自行銷毀。

    (3)當然,服務可同時是這兩種啟動模式。

  • 服務在其托管的主線程中運作,它既不建立線程,也不在單獨的程序中運作;是以如果在服務中執行CPU密集型工作或者阻止性工作,需要建立單獨的線程來運作這部分代碼,否則極容易會出現ANR情況。

二、Service服務在清單檔案中的聲明

無論是啟動狀态還是綁定狀态,都需要通過繼承Service基類自定義而來,也都需要在AndroidMainfest.xml中聲明。

<Service 
android:enable=["true" | "false"] // 
android:exported=["true" | "false"] // 代表是否能被其他應用隐式調用
android:isolatedProcess=["true" | "false"] // 設定true意味着服務會在一個特殊的程序下運作,這個程序和系統其他程序分開且沒有自己的權限。與其通信的唯一途徑是通過服務API(bind & start)
android:label="string resource" // 
android:name="string" // 對應Service類名
android:permission="string" // 權限聲明
android:process="string" // 是否需要在單獨的程序中運作
</Service>
           
  • 注:android:process=":remote"時,代表Service在單獨的程序中運作;“:”這個冒号很重要,它表示要在目前程序名稱前面附加上目前的包名,是以“remote”和“:remote”不是同一個意思,前者的程序名稱為:remote,後者的程序名稱為:App-packageName:remote

三、Service中應重寫的重要方法簡述

  • onCreate():首次建立服務時,系統将調用此方法來執行一次性設定程式(在調用onStartCommand()或onBind()方法之前),如果服務已經在運作,則不會調用。
  • onStartCommand():當一個元件調用startService()請求啟動服務時,系統将調用此方法,即會啟動服務并在背景無限期運作。如果使用此方法開啟任務,則在服務完成後需要手動調用stopSelf()【Service自己調用該方法】或stopService()【由其他元件調用該方法】來停止服務。
  • onBind():當一個元件通過調用bindService()與服務綁定時,系統将會調用此方法;此方法實作中必須通過傳回IBinder提供一個接口,供用戶端用來與服務進行通信。
  • onDestroy():當服務不在使用且将被銷毀時,系統将會調用這個方法;服務應該在此方法中來清理所有資源,如線程,注冊的監聽器,接收器等等。

四、從以下幾點講解服務的建立

1、因為在某些情況下系統會終止服務,是以從以下幾點來學習服務的具體知識:
  • 如果将服務綁定到擁有使用者焦點的Activity上時,服務優先級很高,不太可能被系統銷毀。
  • 如果将服務聲明為在前台運作,則它永遠不會被終止。
  • 如果服務已開啟并且需要長時間在背景運作,系統會随着時間的推移降低服務在背景任務清單中的位置,而服務也将随之變得非常容易被終止。
  • 如果是啟動服務,就需要考慮被系統銷毀後一旦有資源可以重新開機服務的情況
2、API的選擇
  • Service:适用于所有服務的基類。擴充此類時,必須建立一個用于執行所有服務工作的新線程,因為預設情況下,服務将使用主線程。
  • IntentService:官方提供的Service的子類,它使用工作線程逐一處理所有啟動請求。如果不需要服務同時處理多個請求,這是最好的選擇。隻需要實作onHandleIntent()方法即可,該方法會接收每個啟動請求的Intent,依次執行這些請求。
  • startForeground():在前台運作服務

五、 啟動方式:

1、onStartCommand(Intent intent, int flags, int startId)參數講解

(1)intent:啟動時,啟動元件傳遞過來的Intent,如Activity可利用Intent封裝所需要的參數并傳遞給Service;

(2)flags:表示啟動請求時是否有額外資料,可選值有0,START_FLAG_REDELIVERY,START_FLAG_RETRY;

  • 0:代表沒有
  • START_FLAG_REDELIVERY:這個值代表了onStartCommand方法的傳回值為START_REDELIVER_INTENT,而且在上一次服務被殺死前會去調用stopSelf方法停止服務。其中START_REDELIVER_INTENT意味着目前Service因記憶體不足而被系統kill後,則會重建服務,并通過傳遞給服務的最後一個Intent調用onStartCommand(),此時Intent是有值的。
  • START_FLAG_RETRY:當onStartCommand調用後一直沒有傳回值時,會嘗試重新去調用onStartCommand()。

    (3)startId:指明目前服務的唯一ID,與stopSelfResult(int startId)配合使用,stopSelfResult可以更安全的根據ID停止服務。

2、onStartCommand的傳回值講解(很重要)
  • START_STICKY:

    服務因為記憶體不足而被系統kill後,當記憶體再次空閑的時候會重新建立服務。一旦建立成功将會調用onStartCommand方法,不會重新傳遞最後一個Intent,此時傳遞的Intent為null。除非有挂起的Intent要啟動服務;這個狀态下比較适合不執行指令但無限期運作下去并等待作業的媒體播放器或類似服務。

  • START_NOT_STICKY:

    服務因記憶體不足而被系統kill後,除非有挂起的Intent要傳遞,否則不會重建服務。這是最安全的選項,可以避免在不必要時以及應用能夠輕松重新開機所有未完成的作業時運作服務。

  • START_REDELIVER_INTENT:

    服務因為記憶體不足被系統kill後,會重建服務,并通過傳遞給服務的最後一個intent調用onStartCommand,任何挂起intent均依次傳遞,與START_STICKY不同的是,其中傳遞的intent将是非空,是最後一次調用startService中的intent,這個情況适用于主動執行應該立即恢複的作業(如下載下傳檔案)的服務。

  • 總結:每次啟動服務(調用startService)時,onStartCommand方法都會被調用,是以我們可以通過該方法使用intent給Service傳遞所需要的參數,然後在onStartCommand方法中處理事件,最後根據需求選擇不同的Flag傳回值。
  • 注:這種模式下,啟動服務時傳遞的Intent參數是元件與服務之間唯一的通信模式,如果希望元件得到服務所執行任務的結果,可通過廣播的形式建立連接配接。
  • 必須自己管理生命周期,建議服務自身調用stopSelf(int)方法,確定在多次啟動服務的情況下正确停止服務。

六、Service綁定服務

1、關聯:

元件可以向服務發送請求,或者調用服務的方法,此時被綁定的服務會接收資訊并響應,甚至可以通過綁定服務執行程序間通信。

生命周期:調用bindService()時建立,調用unbindService()關閉連接配接;隻在為其他元件服務時處于活動狀态,不會無限期在背景運作;所有宿主解綁後,服務就會被系統銷毀。

2、綁定到已啟動服務:

可以建立同時具有已啟動和綁定兩種狀态的服務;即通過startService()啟動該服務,讓服務無限期運作下去,還可以通過bindService()使用戶端綁定到服務;同時具備兩種狀态的服務,系統不會在所有用戶端都解除綁定之後銷毀服務,而是通過調用stopSelf()/stopService()顯示停止服務。

3、建立綁定服務,通過三種方法定義接口
  • 擴充Binder類:服務供自有應用專用,并且運作在與用戶端相同的程序中,此時使用該方法;不以這種方式建立接口的唯一原因是服務被其他應用或不同的程序占用。
  • 使用Messager:需要讓接口跨不同的程序工作,使用此方法;服務可用這種方式定義對應不同類型Message對象的Handler。此Handler是Messager的基礎,後者随後可與用戶端分享一個IBindler,進而讓用戶端能利用Message對象向服務發送指令;此外,用戶端還可以定義自由Messenger,以便服務回傳消息。這是執行程序間通信(IPC)的最簡單方法,因為Messenger會在單一線程中建立包含所有請求的隊列。
  • 使用AIDL(Android接口定義語言):執行所有将對象分解成原語的工作,作業系統可以識别這些原語并将它們編組到各程序中,以執行IPC;Messenger方法是建立在AIDL的基礎之上的,Messenger會在單一線程中建立包含所有用戶端請求的隊列,此時服務一次接受一個請求;而AIDL可同時發送多個請求,此時服務就必須具備多線程處理的能力。
  • 綁定過程:因為服務綁定過程是異步的,是以要建立一個ServiceConnection執行個體。
  • 附加說明:如果隻需要在Activity可見時與服務互動,則應該在onStart()期間綁定,在onStop()期間取消綁定。如果希望Activity在背景停止運作狀态下仍可接收響應,則可在onCreate()期間綁定,在onDestroy()期間取消綁定。

七、在前台運作服務

  • 定義:

    前台服務被認為是使用者主動意識到的一種服務,是以記憶體不足的時候系統也不會考慮銷毀它。

  • 前台服務必須為狀态欄提供通知,放在“正在進行”标題下方,這意味着除非服務停止或從前台移除,否則不能清除通知。
  • 請求讓服務運作于前台:調用startForeground()
  • 從前台移除服務:調用stopForeground()

參考文章

  • https://blog.csdn.net/javazejian/article/details/52709857
  • https://developer.android.com/guide/components/services?hl=zh-cn
  • https://developer.android.com/guide/components/bound-services?hl=zh-CN