前言:本文所寫(xiě)的是博主的個(gè)人見(jiàn)解歪今,如有錯(cuò)誤或者不恰當(dāng)之處株婴,歡迎私信博主,加以改正喊熟!原文鏈接,demo鏈接
Serviec(服務(wù))簡(jiǎn)述
什么是Service
Service 是一個(gè)可以在后臺(tái)執(zhí)行長(zhǎng)時(shí)間運(yùn)行操作而不提供用戶界面的應(yīng)用組件姐刁。Service 可以由其他應(yīng)用組件啟動(dòng)芥牌,即便用戶切換到其他應(yīng)用,Service 仍將在后臺(tái)繼續(xù)運(yùn)行聂使。此外壁拉,組件可以綁定到 Service ,進(jìn)行交互柏靶,甚至執(zhí)行進(jìn)程間通信(IPC)弃理。例如,Service 可以處理網(wǎng)絡(luò)事務(wù)屎蜓,播放音樂(lè)痘昌,執(zhí)行文件讀寫(xiě)或與內(nèi)容提供程序交互,這一切都可以在后臺(tái)進(jìn)行。-
服務(wù)的兩種基本形式
啟動(dòng)
當(dāng)應(yīng)用組件(如 Activity )通過(guò)調(diào)用 startService() 啟動(dòng)服務(wù)時(shí)辆苔,服務(wù)處于 “啟動(dòng)” 狀態(tài)笔诵,一旦啟動(dòng),服務(wù)可以在后臺(tái)無(wú)限期運(yùn)行姑子,即使啟動(dòng)服務(wù)的組件被銷毀了也不受影響乎婿。已經(jīng)啟動(dòng)的服務(wù)通常執(zhí)行單一操作,而且不會(huì)講結(jié)果返回給調(diào)用方街佑。例如谢翎,它可能通過(guò)網(wǎng)絡(luò)下載或者上傳文件。操作完成后沐旨,服務(wù)會(huì)自動(dòng)停止運(yùn)行森逮。
綁定
當(dāng)應(yīng)用組件通過(guò)調(diào)用 bindService() 綁定到服務(wù)時(shí),服務(wù)處于綁定狀態(tài)磁携。綁定服務(wù)提供了一個(gè)客戶端-服務(wù)器( client-serve )接口,允許組件與服務(wù)進(jìn)行交互褒侧,發(fā)送請(qǐng)求,獲取結(jié)果谊迄,甚至是利用進(jìn)程間通信( IPC )跨進(jìn)程執(zhí)行這些操作闷供。只有與另一個(gè)組件綁定時(shí),綁定服務(wù)才會(huì)運(yùn)行统诺。多個(gè)組件可以綁定同個(gè)服務(wù)歪脏,但全部取消綁定后,該服務(wù)將會(huì)被銷毀粮呢。
雖然服務(wù)的形式有兩種婿失,但服務(wù)可以同時(shí)以兩種方式運(yùn)行,也就是說(shuō)啄寡,它既可以是啟動(dòng)服務(wù)(以無(wú)限期運(yùn)行)豪硅,也允許綁定。問(wèn)題在于你是否實(shí)現(xiàn)一組回調(diào)方法: onStartCommand() (允許組件啟動(dòng)服務(wù)) 和 onBind() (允許綁定服務(wù))挺物。
無(wú)論應(yīng)用是否處于啟動(dòng)狀態(tài)懒浮,綁定狀態(tài),或是處于啟動(dòng)并且綁定狀態(tài)姻乓,任何應(yīng)用組件均可以像使用 Activity 那樣通過(guò)調(diào)用 Intent 來(lái)使用服務(wù)(即使服務(wù)來(lái)自另一個(gè)應(yīng)用)嵌溢。不過(guò)眯牧,你可以通過(guò)清單文件聲明服務(wù)為私有服務(wù)蹋岩,阻止其他應(yīng)用訪問(wèn)。
注意:服務(wù)在其托管進(jìn)程的主線程中運(yùn)行学少,它不創(chuàng)建自己的線程剪个,也不在單獨(dú)的進(jìn)程中運(yùn)行(除非另行指定)。這意味著版确,如果服務(wù)將執(zhí)行任何CPU密集型工作或者阻止性操作(例如 MP3 播放或聯(lián)網(wǎng))扣囊,則應(yīng)在服務(wù)內(nèi)創(chuàng)建新的線程來(lái)完成這項(xiàng)工作乎折,通過(guò)使用單獨(dú)的線程,可以降低發(fā)生ANR錯(cuò)誤的風(fēng)險(xiǎn)侵歇,而應(yīng)用的主線程仍可以繼續(xù)專注于運(yùn)行用戶與 Activity 之間的交互骂澄。
認(rèn)識(shí) Service
要?jiǎng)?chuàng)建服務(wù),必須創(chuàng)建 Service 的子類(或者使用它的一個(gè)現(xiàn)有子類)惕虑。需要重寫(xiě)一些回調(diào)方法坟冲,以處理服務(wù)生命周期的有些關(guān)鍵方面,并提供一種機(jī)制將組件綁定到服務(wù)應(yīng)重寫(xiě)的最重要的回調(diào)方法包括:
onStartCommand()
當(dāng)另一個(gè)組件(如 Activity )通過(guò)調(diào)用 startService() 請(qǐng)求啟動(dòng)服務(wù)時(shí)溃蔫,系統(tǒng)將調(diào)用此方法健提。一旦執(zhí)行此方法,服務(wù)會(huì)啟動(dòng)并可在后臺(tái)無(wú)限期運(yùn)行伟叛。如果你實(shí)現(xiàn)了此方法私痹,在服務(wù)工作完成后,需要調(diào)用 stopSelf() 或 stopService() 來(lái)停止服務(wù)(如果只是提供綁定則無(wú)需實(shí)現(xiàn)此方法)onBind()
當(dāng)另一個(gè)組件調(diào)用 bindService() 與服務(wù)綁定時(shí)统刮,系統(tǒng)將調(diào)用此方法紊遵。在此方法中必須返回 IBinder 提供一個(gè)接口,供客戶端與服務(wù)器進(jìn)行通信侥蒙。如果不希望允許綁定癞蚕,則可以返回 null 。onCreate()
首次創(chuàng)建服務(wù)時(shí)辉哥,系統(tǒng)調(diào)用次方法來(lái)執(zhí)行一次性程序(在調(diào)用 onStartCommand() 或 onBind() 之前)桦山。如果服務(wù)已經(jīng)運(yùn)行則不會(huì)調(diào)用此方法。onDestory()
當(dāng)服務(wù)不再使用且將被銷毀是醋旦,系統(tǒng)調(diào)用此方法恒水。服務(wù)應(yīng)該實(shí)現(xiàn)方法來(lái)清理所有資源,如線程饲齐、注冊(cè)的監(jiān)聽(tīng)器钉凌,接收器等。
如果組件調(diào)用 startService()啟動(dòng)服務(wù)(會(huì)導(dǎo)致對(duì) onStartCommand() 的調(diào)用)捂人,則服務(wù)將一直運(yùn)行御雕,知道服務(wù)使用 stopSelf() 自行停止運(yùn)行或者其他組件調(diào)用 stopService() 停止它為止。
如果組件調(diào)用 bindService() 來(lái)創(chuàng)建服務(wù)(且未調(diào)用 onStartCommand() )滥搭,則服務(wù)只會(huì)在該組件與其綁定時(shí)運(yùn)行酸纲,一旦服務(wù)與所有客戶端全部取消綁定時(shí),系統(tǒng)會(huì)銷毀它瑟匆。
僅當(dāng)內(nèi)存過(guò)低且系統(tǒng)必須回收資源供具有用戶焦點(diǎn)的 Activity 使用時(shí)闽坡,Android 系統(tǒng)才會(huì)強(qiáng)制停止服務(wù)。如果將服務(wù)綁定到具有用戶焦點(diǎn)的 Activity ,它被系統(tǒng)終止的可能性不大疾嗅;如果將服務(wù)聲明為在前臺(tái)運(yùn)行,則它幾乎永遠(yuǎn)不會(huì)終止外厂。如果服務(wù)已經(jīng)啟動(dòng)且要長(zhǎng)時(shí)間運(yùn)行,則系統(tǒng)會(huì)隨著時(shí)間推移降低服務(wù)在后臺(tái)列表中的位置代承,服務(wù)也將變得容易被終止汁蝶;如果服務(wù)是啟動(dòng)服務(wù),則必須將其設(shè)計(jì)為能夠妥善處理系統(tǒng)對(duì)它的重啟论悴。如果系統(tǒng)終止服務(wù)穿仪,那么一旦資源變得再次可用,系統(tǒng)便會(huì)重啟服務(wù)(取決于從 onStartCommand() )返回的值意荤。
使用清單聲明服務(wù)
如同 Activity (或是其他組件)一樣啊片,必須在應(yīng)用的清單文件中聲明所有服務(wù)。
要聲明服務(wù)玖像,清添加 <service>
元素作為 <application>
元素的子元素紫谷。例如
<manifest ... >
...
<application ... >
<service android:name=".DemoService" />
...
</application>
</manifest>
<service>
元素還可以包含其他屬性,定義一些特性捐寥,如啟動(dòng)服務(wù)及運(yùn)行所需要的權(quán)限笤昨。其中 android:name 屬性是唯一必須的屬性,用于指定服務(wù)的類名握恳。應(yīng)用一經(jīng)發(fā)布瞒窒,不可更改類名,不然會(huì)因依賴顯示 Intent 啟動(dòng)或綁定服務(wù)而導(dǎo)致破壞代碼的風(fēng)險(xiǎn)乡洼。
為了確保應(yīng)用的安全性崇裁,請(qǐng)使用顯示 Intent 或 綁定 Service , 而且不要為服務(wù)聲明 Intent 過(guò)濾器束昵。啟動(dòng)哪個(gè)服務(wù)存在不確定性拔稳,對(duì)這種不確定性的考量非常有必要,可以為服務(wù)提供 Intent 過(guò)濾器并從 Intent 中排除想應(yīng)的組件名稱锹雏,但必須使用 setPackage() 方法設(shè)置 Intent 的軟件包, 這樣做可以消除目標(biāo)服務(wù)的不確定性巴比。
此外還可以通過(guò)添加 android:exporeted = "false"
,確保服務(wù)為應(yīng)用私有礁遵,可以阻止其他應(yīng)用啟動(dòng)你的服務(wù)轻绞,同理,使用顯示 Intent 時(shí)也是如此佣耐。
啟動(dòng)服務(wù)的創(chuàng)建
啟動(dòng)服務(wù)由另一個(gè)組件通過(guò)調(diào)用 startService() 啟動(dòng)政勃,服務(wù)的 onStartCommand() 方法也將被調(diào)用。
服務(wù)啟動(dòng)之后晰赞,其生命周期完全獨(dú)立稼病,且可以在后臺(tái)無(wú)限期地運(yùn)行,即使啟動(dòng)服務(wù)的組件被銷毀了掖鱼,該服務(wù)也不受影響然走。如果要結(jié)束該服務(wù),可以調(diào)用 stopSelf() 自行停止運(yùn)行戏挡,或者由另一個(gè)組件調(diào)用 stopService() 來(lái)停止芍瑞。
應(yīng)用組件(如 Activity )可以通過(guò)調(diào)用 startService() 方法且傳遞一個(gè) Intent 對(duì)象(指定服務(wù)和其所有數(shù)據(jù))來(lái)啟動(dòng)服務(wù)。服務(wù)通過(guò) onStartCommand() 方法來(lái)接收 Intent 對(duì)象褐墅。
例如拆檬,某個(gè) Activity 需要保存一些數(shù)據(jù)到線上的數(shù)據(jù)庫(kù)中,這時(shí)它可以啟用一個(gè)協(xié)同服務(wù)妥凳,調(diào)用 startService() 并傳遞一個(gè) Intent 竟贯,提供需要保存的數(shù)據(jù)。服務(wù)通過(guò) onStartCommand() 接收 Intent 逝钥,連接到互聯(lián)網(wǎng)并執(zhí)行數(shù)據(jù)庫(kù)事務(wù)屑那,事務(wù)完成后,服務(wù)將自行停止運(yùn)行且隨即被銷毀艘款。
注意: 默認(rèn)情況下持际,服務(wù)與服務(wù)聲明所在的應(yīng)用處于同一進(jìn)程,而且運(yùn)行在主線程中哗咆。因此蜘欲,如果是執(zhí)行一些耗時(shí)操作,需要在服務(wù)內(nèi)啟動(dòng)新的線程晌柬,避免影響應(yīng)用的性能姥份。
你可以通過(guò)擴(kuò)展兩個(gè)類來(lái)創(chuàng)建啟動(dòng)服務(wù):
Service
這是適用于所有服務(wù)的基類。默認(rèn)情況下該服務(wù)將在應(yīng)用的主線程中運(yùn)行年碘,你需要?jiǎng)?chuàng)建一個(gè)新的線程供服務(wù)工作殿衰,避免影響正在運(yùn)行的所有 Activity 的性能。
IntentService
這個(gè)是 Service 的子類盛泡,它適用工作線程逐一處理所有啟動(dòng)請(qǐng)求闷祥。如果不要求服務(wù)同時(shí)處理 請(qǐng)求,這毫無(wú)疑問(wèn)是最好的選擇傲诵。只需要實(shí)現(xiàn) onHandleIntent() 方法即可凯砍。該方法會(huì)接收每個(gè)啟動(dòng)請(qǐng)求的 Intent ,使你能夠執(zhí)行后臺(tái)工作拴竹。
下面演示如何使用其中任意一個(gè)類來(lái)實(shí)現(xiàn)服務(wù)悟衩。
-
擴(kuò)展 IntentService 類
由于大多數(shù)啟動(dòng)服務(wù)不用同時(shí)處理多個(gè)請(qǐng)求(這種多線程情況可能很危險(xiǎn)),因此選擇 IntentService 類實(shí)現(xiàn)服務(wù)無(wú)疑是最好的。
IntentServic 執(zhí)行以下的操作:
(1) 創(chuàng)建默認(rèn)的工作線程栓拜,在主線程外執(zhí)行傳遞給 onStartConmmand() 的所有 Intent座泳。
(2) 創(chuàng)建工作隊(duì)列惠昔,將 Intent 逐一傳遞給 onHandleIntent() 實(shí)現(xiàn),不用擔(dān)心多線程問(wèn)題。
(3) 處理所有啟動(dòng)請(qǐng)求后停止服務(wù)(不用手動(dòng)調(diào)用 stopSelf() 來(lái)結(jié)束服務(wù))
(4) 提供 onBind() 的默認(rèn)實(shí)現(xiàn)(返回 null )挑势。
(5) 提供 onStartCommand() 的默認(rèn)實(shí)現(xiàn)镇防,可以將 Intent 依次發(fā)送到工作隊(duì)列 和 onHandleIntent() 實(shí)現(xiàn)。綜上所述潮饱,只需要實(shí)現(xiàn) onHandleIntent() 來(lái)完成客戶端提供的工作即可来氧。(需要為服務(wù)提供構(gòu)造函數(shù))
下面是 IntentService 的實(shí)現(xiàn)示例:
public class DemoIntentService extends IntentService { /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public DemoIntentService(String name) { super(name); } @Override protected void onHandleIntent(@Nullable Intent intent) { //模擬耗時(shí)操作,線程沉睡3秒 try { Thread.sleep(3000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
只需要一個(gè)構(gòu)造函數(shù)和一個(gè) onHandleIntent() 實(shí)現(xiàn)即可香拉。
如果重寫(xiě)其他回調(diào)方法(如 onCreate() 啦扬、onStartCommand() 或 onDestroy),要確保調(diào)用超類實(shí)現(xiàn)凫碌,讓 IntentService 能夠妥善處理工作線程的生命周期扑毡。
例如, onStartCommand() 必須返回默認(rèn)實(shí)現(xiàn)(將 Intent 傳遞給 onHandleIntent() ):
@Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); Log.i(TAG, "service starting"); return super.onStartCommand(intent, flags, startId); }
除了 onHandleIntent() 之外 盛险,無(wú)需調(diào)用的方法就是 onBind() (僅當(dāng)服務(wù)允許綁定時(shí)僚楞,才需要實(shí)現(xiàn)該方法)
-
擴(kuò)展服務(wù)類
如上部分所述,使用 IntentService 簡(jiǎn)化了啟動(dòng)服務(wù)的實(shí)現(xiàn)枉层,如果要服務(wù)執(zhí)行多線程(不是通過(guò)工作隊(duì)列處理啟動(dòng)請(qǐng)求)泉褐,則可以擴(kuò)展 Service 類來(lái)處理每個(gè) Intent 。
以下是 Service 類實(shí)現(xiàn)代碼示例鸟蜡,該類執(zhí)行的工作與上面的 IntentService 示例相同膜赃。對(duì)每個(gè)啟動(dòng)請(qǐng)求,它都使用工作線程執(zhí)行作業(yè)揉忘,且每次僅處理一個(gè)請(qǐng)求跳座。
public class DemoService extends Service {
private Looper mServiceLooper;
private ServiceHandle mServiceHandle;
@Override
public void onCreate() {
//啟動(dòng)運(yùn)行該服務(wù)的線程
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process
.THREAD_PRIORITY_BACKGROUND);
thread.start();
//獲取HandlerThread的Looper并將其用于自己的Handler
mServiceLooper = thread.getLooper();
mServiceHandle = new ServiceHandle(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
//每一個(gè)啟動(dòng)請(qǐng)求,發(fā)送一個(gè)消息來(lái)啟動(dòng)一個(gè)工作并提交開(kāi)始Id
Message msg = mServiceHandle.obtainMessage();
msg.arg1 = startId;
mServiceHandle.sendMessage(msg);
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
//從線程接收消息的處理程序
private final class ServiceHandle extends Handler {
public ServiceHandle(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//模擬耗時(shí)操作,線程沉睡3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
stopSelf(msg.arg1);
}
}
}
如上面的示例泣矛,與使用 IntentService 相比疲眷,這需顯得復(fù)雜一些。
但是您朽,自己處理 onStartCommand() 的每個(gè)調(diào)用狂丝,因此可以同時(shí)執(zhí)行多個(gè)請(qǐng)求。上面的示例沒(méi)有實(shí)現(xiàn)哗总。如果你有需要几颜,可以為每個(gè)請(qǐng)求創(chuàng)建一個(gè)新線程,然后立即運(yùn)行這些線程(不是等待上一個(gè)請(qǐng)求完成)讯屈。
注意: onStartCommand() 方法必須返回整型數(shù)蛋哭。 該返回值用于描述系統(tǒng)改如何在服務(wù)終止的情況下繼續(xù)運(yùn)行服務(wù),從 onStartCommand() 返回的值必須是以下常量注意之一:
- START_NOT_STICKY
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù)涮母,除非有掛起的 Intent 要傳遞谆趾,否則系統(tǒng)不會(huì)重建服務(wù)躁愿。這是最安全的選項(xiàng),可以避免在不必要時(shí)一級(jí)應(yīng)用能夠輕松啟動(dòng)所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)沪蓬。
- START_STICKY
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù)彤钟,則會(huì)重建服務(wù)并調(diào)用 onStartCommand(),但不會(huì)重新傳遞最后一個(gè) Intent 怜跑。相反样勃,除非有掛起 Intent 要啟動(dòng)服務(wù)(在這種情況下吠勘,傳遞這些 Intent )性芬,否則系統(tǒng)會(huì)通過(guò)空 Intent 調(diào)用 onStartCommand() ,這個(gè)返回值在適用于不執(zhí)行命令剧防,但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器(或類似服務(wù))植锉。
- START_REDELIVER_INTENT
如果系統(tǒng)在 onStartCommand() 返回后終止服務(wù),則會(huì)重建服務(wù)峭拘,并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand() 俊庇。任何掛起 Intent 均依次傳遞。適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(如下載文件)的服務(wù)鸡挠。
3. 啟動(dòng)服務(wù)
可以通過(guò)將 Intent (指定要啟動(dòng)的服務(wù))傳遞給 startService辉饱,從 Activity 或其他應(yīng)用組件啟動(dòng)服務(wù)。 Android 系統(tǒng)調(diào)用服務(wù)的 onStartCommand() 方法拣展,并向其傳遞 Intent 彭沼。(請(qǐng)勿直接調(diào)用 onStartCommand() )
例如,Activity 可以結(jié)合使用顯式 Intent 與 startService()备埃,啟動(dòng)上文中的示例服務(wù)( DemoService ):
```java
Intent intent = new Intent(this,DemoService.class);
startService(intent);
startService() 方法將立即返回姓惑,且 Android 系統(tǒng)調(diào)用服務(wù)的 onStartCommand() 方法。如果服務(wù)尚未運(yùn)行按脚,則系統(tǒng)會(huì)先調(diào)用 onCreate() 于毙,然后調(diào)用 onStartCommand() 。
如果服務(wù)未提供綁定辅搬,則使用 startService() 傳遞的 Intent 是應(yīng)用組件與服務(wù)之間唯一的通信模式唯沮。但是,如果你希望服務(wù)返回結(jié)果堪遂,則啟動(dòng)服務(wù)的客戶端可以為廣播創(chuàng)建一個(gè) PendingIntent(使用 getBroadcast()),并通過(guò)啟動(dòng)服務(wù)的 Intent 傳遞給服務(wù)烂翰,然后服務(wù)可以通過(guò)廣播來(lái)傳遞結(jié)果。
多個(gè)服務(wù)啟動(dòng)請(qǐng)求會(huì)導(dǎo)致多次對(duì)服務(wù)的 onStartCommand() 進(jìn)行相應(yīng)的調(diào)用蚤氏。但是要停止服務(wù)甘耿,只需要一個(gè)服務(wù)停止請(qǐng)求(使用 stopSelf() 或 stopService() )即可。
-
停止服務(wù)
啟動(dòng)服務(wù)必須管理自己的生命周期竿滨。也就是說(shuō)佳恬,除非系統(tǒng)必須回收內(nèi)存資源捏境,否則系統(tǒng)不會(huì)停止或銷毀服務(wù),而且服務(wù)會(huì)在 onStartCommand() 返回后繼續(xù)運(yùn)行毁葱。因此垫言,服務(wù)必須通過(guò)調(diào)用 stopSelf() 自行停止運(yùn)行,或者其他組件調(diào)用 stopService() 來(lái)停止倾剿。
一旦請(qǐng)求使用 stopSelf() 或者 stopService() 停止服務(wù)筷频,系統(tǒng)就會(huì)盡快銷毀服務(wù)。
但是前痘,如果服務(wù)同時(shí)處理多個(gè) onStartCommand() 請(qǐng)求凛捏,則不應(yīng)該在處理第一個(gè)啟動(dòng)請(qǐng)求后停止服務(wù),有可能你已經(jīng)接收新的啟動(dòng)請(qǐng)求(第一個(gè)請(qǐng)求結(jié)束時(shí)停止服務(wù)會(huì)終止第二個(gè)請(qǐng)求)芹缔。為了避免這個(gè)問(wèn)題坯癣,可以使用 stopSelf( int ) 確保服務(wù)停止于最近的啟動(dòng)請(qǐng)求。也就是最欠,在調(diào)用 stopSelf( int ) 時(shí)示罗,傳遞與停止請(qǐng)求的 ID 對(duì)應(yīng)的啟動(dòng)請(qǐng)求 ID (傳遞給 onStartCommand() 的 startId )。然后芝硬,在調(diào)用 stopSelf( int ) 之前服務(wù)收到了新的請(qǐng)求蚜点,ID 不匹配,服務(wù)也就不會(huì)停止拌阴。
注意: 為了避免浪費(fèi)系統(tǒng)資源和小號(hào)電池電量,應(yīng)用必須在工作完成后停止其服務(wù)绍绘。如有必要,其他組件可以通過(guò)調(diào)用 stopService 來(lái)停止服務(wù)皮官,即使為服務(wù)啟用了綁定脯倒,一旦服務(wù)受到對(duì) onStartCommand 的調(diào)用, 始終需要親自停止服務(wù)。
創(chuàng)建綁定服務(wù)
綁定服務(wù)允許應(yīng)用通過(guò)調(diào)用 bindService() 與其綁定捺氢,以便創(chuàng)建長(zhǎng)期連接(通常不允許組件通過(guò)調(diào)用 startService() 來(lái)啟動(dòng)它)藻丢。
如需與 Activity 和其他應(yīng)用組件中的服務(wù)進(jìn)行交互,或者需要跨進(jìn)程通信摄乒,則應(yīng)創(chuàng)建綁定服務(wù)悠反。
創(chuàng)建綁定服務(wù),必須實(shí)現(xiàn) onBind() 回調(diào)方法以返回 IBinder 馍佑,用于定義與服務(wù)通信的接口斋否。然后其他應(yīng)用組件可以調(diào)用 bindService() 來(lái)檢索該接口,并開(kāi)始對(duì)服務(wù)調(diào)用方法拭荤。服務(wù)只用于與其綁定的應(yīng)用組件,因此如果沒(méi)有組件綁定到服務(wù)茵臭,則系統(tǒng)會(huì)銷毀服務(wù)(不必通過(guò) onStartCommand() 啟動(dòng)的服務(wù)來(lái)停止綁定服務(wù))。
要?jiǎng)?chuàng)建綁定服務(wù)舅世,必須定義與指定客戶端與服務(wù)通信的接口旦委。服務(wù)與客戶端之間的這個(gè)接口必須是 IBinder 的實(shí)現(xiàn)奇徒,且服務(wù)必須從 onBind() 回調(diào)方法返回它。一旦客戶端收到 IBinder 缨硝,即可開(kāi)始通過(guò)該接口與服務(wù)器進(jìn)行交互摩钙。
多個(gè)客戶端可以同時(shí)綁定到服務(wù),客戶端完成與服務(wù)的交互后查辩,會(huì)調(diào)用 unbindService() 取消綁定胖笛。一旦沒(méi)有客戶端綁定到該服務(wù),系統(tǒng)就會(huì)銷毀它宜岛。
有多種方法實(shí)現(xiàn)綁定服務(wù)长踊,實(shí)現(xiàn)方式比啟動(dòng)服務(wù)更復(fù)雜,這里就不詳說(shuō)了谬返,后續(xù)會(huì)單獨(dú)的說(shuō)明之斯。
向用戶發(fā)送通知
服務(wù)一旦運(yùn)行起來(lái)日杈,即可使用 Toast 通知或狀態(tài)欄通知來(lái)通知用戶所發(fā)生的事情遣铝。
Toast 通知是指出現(xiàn)在當(dāng)前窗口的表面、片刻隨即消失不見(jiàn)的消息莉擒,而狀態(tài)通知欄則在狀態(tài)欄中隨消息一起提供圖標(biāo)酿炸,用戶可以選擇圖標(biāo)來(lái)采取操作(例如啟動(dòng) Activity )。
通常涨冀,當(dāng)某些后臺(tái)工作完成(錄入文件下載完成)且用戶現(xiàn)在可以對(duì)其進(jìn)行操作時(shí)填硕,狀態(tài)欄通知是最佳方法。當(dāng)用戶從展開(kāi)視圖中選定通知時(shí)鹿鳖,通知即可啟動(dòng) Activity (例如查看下載的文件)扁眯。
在前臺(tái)運(yùn)行服務(wù)
前臺(tái)服務(wù)被認(rèn)為是用戶主動(dòng)意識(shí)到的一種服務(wù),在內(nèi)存不足時(shí)翅帜,系統(tǒng)也不會(huì)考慮將其終止姻檀。前臺(tái)服務(wù)必須為狀態(tài)欄提供通知,放在 “正在進(jìn)行” 標(biāo)題下方涝滴,除非服務(wù)停止或者從前臺(tái)溢出绣版,否則不會(huì)清除通知。
例如歼疮,通過(guò)服務(wù)播放音樂(lè)的音樂(lè)播放器設(shè)置為在前臺(tái)運(yùn)行杂抽,用戶能明確意識(shí)到其操作。狀態(tài)欄中的通知可能表示正在播放的歌曲韩脏,并允許用戶啟動(dòng) Activity 來(lái)與音樂(lè)播放器進(jìn)行交互缩麸。
要請(qǐng)求讓服務(wù)運(yùn)行于前臺(tái),可以調(diào)用 startForeground() 赡矢。此方法采用兩個(gè)參數(shù):唯一標(biāo)識(shí)通知的整型數(shù)和通知欄的 Notification 杭朱。例如:
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("Notification title")
.setContentText("Notification describe")
.setSmallIcon(R.mipmap.ic_launcher);
Intent notificationIntent = new Intent(this,DemoActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID,notification);
注意:提供給 startForegrond() 的整型 ID 不可以為0愚屁。
要從前臺(tái)移除服務(wù),需要調(diào)用 stopForeground() 痕檬。次方法采用一個(gè)布爾值霎槐,指示是否移除狀態(tài)欄通知,此方法不會(huì)停止服務(wù)梦谜。但是丘跌,如果你的服務(wù)正在前臺(tái)運(yùn)行時(shí)將其停止,則通知也會(huì)被移除唁桩。
管理服務(wù)生命周期
服務(wù)的生命周期比 Activity 的生周期要簡(jiǎn)單多闭树。但是密切關(guān)注如何創(chuàng)建和銷毀服務(wù)反而更重要,因?yàn)榉?wù)可以在用戶沒(méi)有意識(shí)到的情況下運(yùn)行于后臺(tái)荒澡。
服務(wù)生命周期可以(從創(chuàng)建到銷毀)可以遵循兩條不同的路徑:
啟動(dòng)服務(wù)
該服務(wù)在其他組件調(diào)用 startService() 時(shí)創(chuàng)建报辱,然后無(wú)限期運(yùn)行,且必須通過(guò)調(diào)用 stopSelf() 來(lái)自行停止運(yùn)行单山。此外碍现,其他組件也可以通過(guò)調(diào)用 stopService 來(lái)停止服務(wù)。服務(wù)停止后米奸,系統(tǒng)將其銷毀昼接。綁定服務(wù)
該服務(wù)在另一個(gè)組件(客戶端)調(diào)用 bindService() 時(shí)創(chuàng)建,然后客戶端通過(guò) IBinder 接口與服務(wù)進(jìn)行通信悴晰÷客戶端可以調(diào)用 unbindService() 關(guān)閉連接。多個(gè)客戶端可以綁定到相同的服務(wù)铡溪,而且當(dāng)所有綁定取消后漂辐,系統(tǒng)會(huì)銷毀該服務(wù)(服務(wù)不必自行停止)。
這兩條路徑并非完全獨(dú)立棕硫,也就是說(shuō)髓涯,你可以綁定到已經(jīng)使用 startService() 啟動(dòng)的服務(wù)。例如饲帅,通過(guò)使用 Intent (標(biāo)識(shí)要播放的音樂(lè))調(diào)用 startService() 來(lái)啟動(dòng)后臺(tái)音樂(lè)服務(wù)复凳。隨后可能在用戶需要稍加控制播放器或獲取有關(guān)當(dāng)前播放歌曲的信息時(shí), Activity 可以通過(guò)調(diào)用 bindService() 綁定到服務(wù)灶泵,在這種情況下育八,除非所有客戶端取消綁定,否則 stopService() 或 stopSelf() 不會(huì)實(shí)際停止服務(wù)赦邻。
實(shí)現(xiàn)生命周期回調(diào)
與 Activity 類似髓棋,服務(wù)也擁有生命周期回調(diào)方法,你可以實(shí)現(xiàn)這些方法來(lái)監(jiān)控服務(wù)狀態(tài)的變化,并適時(shí)執(zhí)行工作按声。下面的例子展示了每種生命周期方法:
public class TestService extends Service {
int mStartMode; // 指示如果服務(wù)被殺死膳犹,該如何操作
IBinder mBinder; // 客戶端綁定接口
boolean mAllowRebind; // 指示是否應(yīng)使用onRebind
@Override
public void onCreate() {
//服務(wù)正在創(chuàng)建中
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//調(diào)用了startService(),服務(wù)正在啟動(dòng)
return mStartMode;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//客戶端綁定到具有bindService()的服務(wù)
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
//所有客戶端都使用unbindService()取消綁定
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
//客戶端綁定到具有bindService()的服務(wù)签则,
//onUnbind()已經(jīng)被調(diào)用
}
@Override
public void onDestroy() {
//該服務(wù)已不再使用并被銷毀
}
}
注:與 Activity 生命周期回調(diào)方法不同须床,你不需要調(diào)用這些方法的超類實(shí)現(xiàn)。
![Service生命周期圖][1]
[1]: http://www.passershowe.com/img/service_lifecycle.png
左圖顯示了使用 startService() 所創(chuàng)建的服務(wù)的生命周期渐裂,右圖顯示了使用 bindService() 所創(chuàng)建的服務(wù)的生命周期豺旬。
通過(guò)實(shí)現(xiàn)這些方法,你可以監(jiān)控這兩種服務(wù)生命周期:
服務(wù)的整個(gè)生命周期從調(diào)用 onCreate() 開(kāi)始柒凉,到 onDestroy() 返回時(shí)結(jié)束族阅。與 Activity 類似,服務(wù)也在 onCreate() 中完成初始設(shè)置膝捞,在 onDestroy() 中釋放所有剩余資源坦刀。例如音樂(lè)播放服務(wù)可以在 onCreate() 中創(chuàng)建用于播放音樂(lè)的線程,然后可以在 onDestroy() 中停止該線程蔬咬。無(wú)論服務(wù)是通過(guò) startService() 還是 bindService() 創(chuàng)建鲤遥,都會(huì)為所有服務(wù)調(diào)用 onCreate() 和 onDestroy() 方法。
服務(wù)的有效生命周期從調(diào)用 onStartCommand() 或 onBind() 方法開(kāi)始计盒。每種方法均有 Intent 對(duì)象渴频,該對(duì)象分別傳遞到 startService() 或 bindService() 芽丹。
對(duì)于啟動(dòng)服務(wù)北启,有效生命周期與整個(gè)生命周期同時(shí)結(jié)束(即便是在 onStartCommand() 返回之后,服務(wù)仍然處于活動(dòng)狀態(tài))拔第。對(duì)于綁定服務(wù)咕村,有效生命周期在 onUnbind() 返回時(shí)結(jié)束。
注:盡管啟動(dòng)服務(wù)是通過(guò) stopSelf() 或 stopService() 來(lái)停止蚊俺,但是該服務(wù)并無(wú)相應(yīng)的回調(diào)(沒(méi)有 onStop 回調(diào))懈涛。因此,除非服務(wù)綁定到客戶端泳猬,否則在服務(wù)停止時(shí)批钠,系統(tǒng)會(huì)將其銷毀而 onDestroy() 是接收到的唯一回調(diào)。
盡管該圖分開(kāi)介紹通過(guò) startService() 創(chuàng)建的服務(wù)和通過(guò) bindService() 創(chuàng)建的服務(wù)得封,但是記住一點(diǎn)埋心,不管啟動(dòng)方式如何,任何服務(wù)均有可能允許客戶端與其綁定忙上。因此拷呆,最初使用 onStartCommand()(通過(guò)客戶端調(diào)用 startService())啟動(dòng)的服務(wù)仍可接收對(duì) onBind() 的調(diào)用(當(dāng)客戶端調(diào)用 bindService() 時(shí))。