淺談 Android Service

前言:本文所寫(xiě)的是博主的個(gè)人見(jiàn)解歪今,如有錯(cuò)誤或者不恰當(dāng)之處株婴,歡迎私信博主,加以改正喊熟!原文鏈接demo鏈接

Serviec(服務(wù))簡(jiǎn)述

  1. 什么是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)行。

  2. 服務(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ù)悟衩。

  1. 擴(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)該方法)

  2. 擴(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() )即可。

  1. 停止服務(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í))。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茬斧,隨后出現(xiàn)的幾起案子腰懂,更是在濱河造成了極大的恐慌,老刑警劉巖项秉,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绣溜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡娄蔼,警方通過(guò)查閱死者的電腦和手機(jī)涮毫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贷屎,“玉大人罢防,你說(shuō)我怎么就攤上這事“χ叮” “怎么了咒吐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)属划。 經(jīng)常有香客問(wèn)我恬叹,道長(zhǎng),這世上最難降的妖魔是什么同眯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任绽昼,我火速辦了婚禮,結(jié)果婚禮上须蜗,老公的妹妹穿的比我還像新娘硅确。我一直安慰自己,他們只是感情好明肮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布菱农。 她就那樣靜靜地躺著,像睡著了一般柿估。 火紅的嫁衣襯著肌膚如雪循未。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天秫舌,我揣著相機(jī)與錄音的妖,去河邊找鬼。 笑死足陨,一個(gè)胖子當(dāng)著我的面吹牛嫂粟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钠右,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赋元,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起搁凸,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤媚值,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后护糖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體褥芒,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嫡良,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锰扶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寝受,死狀恐怖坷牛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情很澄,我是刑警寧澤暗甥,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布昆雀,位于F島的核電站吐咳,受9級(jí)特大地震影響赏参,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讯蒲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一痊土、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧墨林,春花似錦赁酝、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辆雾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間月劈,已是汗流浹背度迂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猜揪,地道東北人惭墓。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像而姐,于是被迫代替她去往敵國(guó)和親腊凶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • Service 服務(wù) 什么是服務(wù): A service is not a separate process and...
    Showdy閱讀 398評(píng)論 0 1
  • 服務(wù)基本上分為兩種形式 啟動(dòng) 當(dāng)應(yīng)用組件(如 Activity)通過(guò)調(diào)用 startService() 啟動(dòng)服務(wù)時(shí)...
    pifoo閱讀 1,256評(píng)論 0 8
  • [文章內(nèi)容來(lái)自Developers] Service是一個(gè)可以在后臺(tái)執(zhí)行長(zhǎng)時(shí)間運(yùn)行操作而不提供用戶界面的應(yīng)用組件。...
    岳小川閱讀 857評(píng)論 0 7
  • 一起走的路钧萍,縱使是未來(lái)會(huì)忘記褐缠,但是分別時(shí)還是那么的不舍,那份單純的愛(ài)是只想看著你離開(kāi)风瘦,看著你幸福便足矣队魏,哪怕是犧牲...
    阿睿任逍遙閱讀 512評(píng)論 0 0
  • 今天是女性朋友的節(jié)日。而我們今天的幸福生活万搔,都要感激那位全世界第一個(gè)在電影里全裸出鏡的女星Hedwig Eva M...
    C老師碎碎念閱讀 268評(píng)論 0 1