前言
在整理Android藍牙開發(fā)的過程中,會用到后臺播放音樂的功能,需要與service進行交互,因此對service作一個筆記,對以前未注意的細節(jié)作一次梳理。
一、service的基礎知識
1.1 service的兩種類型
-
started
當應用組件,如Activity通過startService()啟動service,此時service處于started狀態(tài)。一旦啟動,此時service能夠在后臺一直運行,即使啟動它的組件已經(jīng)被銷毀。一般,啟動服務執(zhí)行單個操作,并且不會向調(diào)用者返回結果。當然這種情況視需求而定。 -
Bound
當應用程序通過bindService()方法綁定到service時,service處于綁定狀態(tài)。綁定服務可以為客戶端-服務器提供接口,允許組件與service進行交互,發(fā)送請求,獲得結果等操作,甚至還可以進行IPC(進程間通信)。僅當其他應用程序的組件與service綁定后,綁定服務才會允許。多個組件可以綁定到一個service上,并且當他們?nèi)拷獬壎〞r,service才可以被銷毀。
1.2 service的線程
service運行于管理它的進程的主線程,即在APP內(nèi),service的線程id和主線程的id一致。可以使用log進行驗證分別在MainActivity中的onCreate()中,以及自定義的MyService中的onCreate()中,執(zhí)行Log
Log.e("TAG","主線程id = " + Thread.currentThread().getId());
Log.e(TAG,"Service的線程id = " + Thread.currentThread().getId());
得到的結果如下
06-21 03:49:28.491 5057-5057/com.example.slide_table E/MyService:主線程id=1
06-21 03:49:28.495 5057-5057/com.example.slide_table E/MyService:Service的線程id=1
如果需要在service中運行比較耗時的操作,可以在service中創(chuàng)建新的線程來完成這些操作,以減少應用可能出現(xiàn)的ANR。
二、service中的重要方法
在實際研發(fā)的過程中,幾乎都需要對service中的回調(diào)方法進行重寫以期達到需求。經(jīng)常需要重寫的幾種回調(diào)方法如:
onCreate()
服務第一次創(chuàng)建時,會執(zhí)行這個函數(shù),創(chuàng)一次性創(chuàng)建service,如果service已經(jīng)存在,則不需要再次執(zhí)行。onStartCommand()
當其他組件,例如activity調(diào)用startService()的方法請求啟動service時,系統(tǒng)調(diào)用該方法,一旦執(zhí)行此方法,service會一直運行于后臺。同理,在服務完成后,如果需要停止service,可以調(diào)用stopSelf()或者stopService()方法停止service,如果用戶只是想綁定service,則不需要重寫該方法。onBind()
當其他的組件通過bindService()方法與service進行綁定,系統(tǒng)會調(diào)用該方法,但是在開發(fā)中必須通過返回IBinder提供客戶端與service通信的接口,如果用戶不想進行綁定,則返回null。onDestroyed()
當不需要service,需要對service進行銷毀的時候調(diào)用該方法,服務應該利用該方法來實現(xiàn)清理線程,注冊監(jiān)聽器等資源,這也是service收到的最后一個方法調(diào)用。需要注意的是用戶是通過startService()啟動該service時(此時service會調(diào)用onStartCommand()),服務調(diào)用stopSelf()方法停止自身?;蛘咂渌慕M件使用stopService()來停止該service。
當用戶的系統(tǒng)內(nèi)存不足時,必須回收系統(tǒng)資源來顯示用戶關注的activity時,才會停止service的運行。但是若此service為用戶關注的activity所綁定的,則會減少回收的幾率。如果服務被聲明運行于前臺,則基本不會停止。
如果service處于started狀態(tài),雖然會一直運行于后臺,但是為了降低這種系統(tǒng)資源被回收的風險,需要對重啟該service進行設計。當系統(tǒng)資源變得充足可用時,可重新啟動該service,但是依賴于onStartCommand()的返回值。
三、service配置的幾種屬性
當在開發(fā)中聲明某service時,必須在AndroidManifest.xml中對該服務進行配置。常用的幾種屬性為:
android:name
實現(xiàn)該service的名稱,一個完整的類名,通常簡寫為".MyService"。android:enabled
服務能否被系統(tǒng)實例化,可以則返回true,or false,默認為true。android:exported
其他組件能否調(diào)用服務或者與其交互,可以則返回true,or false。當表示false時,只有該APP內(nèi)的應用組件或者與APP具有相同用戶ID的APP能啟動或者與之綁定。android:label
顯示給用戶的service名稱,如果沒有設置,以應用程序APP的標簽取代。android:permission
實體必須包含的權限名稱,如果沒有設置該屬性,可以使用<application>標簽的permission屬性設置服務權限。
四、service的創(chuàng)建
通過繼承創(chuàng)建service,Android提供以下兩個類供開發(fā)繼承:
4.1 Service
所有service的基類,當繼承該類時,創(chuàng)建新的線程來處理需要執(zhí)行的動作,避免出現(xiàn)ANR的情況。特點:
- 可以實現(xiàn)服務處理多線程的任務
- 每次請求創(chuàng)建一個新線程并且立即執(zhí)行,不用等待前一個請求的結束
4.2 IntentService
該類為service的子類,每次都會使用一個工作線程來處理全部的啟動請求。在APP中如果不是處理多個請求,最優(yōu)的創(chuàng)建服務繼承該類。開發(fā)人員只需要重寫onHandleIntent()方法,來執(zhí)行接收到的intent,從而完成后臺服務。
特點:
- 創(chuàng)建工作線程來執(zhí)行發(fā)送到onStartCommand()的全部intent
- 創(chuàng)建工作隊列,一次傳遞一個Intent給onHandleIntent()
- 避免出現(xiàn)多線程
- 所有啟動service請求完畢后,自動停止service,不需要調(diào)用stopSelf()停止該service
- 提供的onBind()方法默認實現(xiàn),返回null
- 提供onStartCommand()方法默認實現(xiàn),先將intent發(fā)送給工作隊列,然后在發(fā)送給onHandleIntent()執(zhí)行
- 只需要在onHandleIntent()執(zhí)行客戶端提供的任務
注意:IntentService并沒有提供構造函數(shù),因此在繼承IntentService后,需提供一個無參的構造方法。
public class MyIntentService extend IntentService{
public MyIntentService(){
super("MyIntentService ");
}
}
五、service中的重要函數(shù)
onStartCommand()
在組件請求啟動service后,會發(fā)送Intent,服務開啟后,調(diào)用onStartCommand()方法執(zhí)行該任務。
但是該方法必須返回一個整數(shù),用于描述系統(tǒng)停止service后如何繼續(xù)服務,此點僅在繼承自service的服務中使用,因為繼承IntentService已經(jīng)默認實現(xiàn)。返回值:
-
START_NOT_STICKY
在執(zhí)行onStartCommand()完成任務后,停止該service,不重新創(chuàng)建服務,則返回 -
START_STICKY
在執(zhí)行onStartCommand()完成任務后,停止該service,重新創(chuàng)建服務,并且再次調(diào)用onStartCommand(),但是不重新發(fā)送最后的Intent。相反,系統(tǒng)使用空Intent調(diào)用onStartCommand(),除非有PendingIntent來啟動該service,此時這些Intent會被發(fā)送。
此返回值經(jīng)常用于多媒體播放器,它們不執(zhí)行命令,但無限期的運行并等待命令。 -
START_REDELIVER_INTENT
在執(zhí)行onStartCommand()返回后停止該service,重新創(chuàng)建服務,并使用發(fā)送給服務最后的Intent重新調(diào)用onStartCommand(),全部的PendingIntent依次發(fā)送。此返回值適用于積極執(zhí)行并且立即恢復工作的服務,如wifi模式下后臺下載文件,斷開wifi后,文件下載中斷,重新打開wifi,繼續(xù)下載文件。
六、service的啟動與停止
6.1 service的啟動
service的啟動分為傳遞intent啟動以及綁定啟動,本章主要講述通過傳遞intent來啟動service。
通過activity或者其他應用組件來傳遞Intent來啟動service,系統(tǒng)調(diào)用service的onStartCommand()并且將該Intent傳遞給它。
Intent service_intent = new Intent(MAcitivty.class, MyService.class);
startService(service_intent);
/**
如果activity與fragment交互,需要使用
getActivity().startService(service_intent);
*/
如果service沒有提供綁定,則startService()發(fā)送的intent是APP的組件與該service唯一的通信模式。如果需要服務返回結果,則啟動該服務的客戶端能為廣播創(chuàng)建一個PendingIntent,并且通過啟動service的Intent進行傳送。服務便可以通過廣播來發(fā)送結果。
6.2 service的停止
通常情況下,APP在服務完成任務后,應該停止服務,避免造成系統(tǒng)資源的浪費和電池的消耗。系統(tǒng)不會自動的停止或者銷毀服務,除非系統(tǒng)資源緊缺需要系統(tǒng)資源的回收,而且還可以在onStartCommand()返回后,服務繼續(xù)運行。service本身可以調(diào)用stopSelf()停止自身,或者其他的應用組件調(diào)用stopService()來停止該service。
注意,如果該service同時處理多個onStartCommand()的調(diào)用請求,在處理一個請求后,首先需要判斷另一個新的請求的ID是否與上一個請求的ID是否相同,即在使用stopSelf(int)方法停止該服務時,同時將新的啟動service的請求ID傳遞給停止請求進行匹配,如果下一個新的請求的ID與上一個請求的ID不相同,則不會停止本次的服務,這樣就會避免多次啟動服務的請求,而多次調(diào)用onStartCommand()。