Android四大組件之Service

Android學(xué)習(xí)的第一步從四大組件開(kāi)始


在四大組件中ActivityService應(yīng)該算的上是一對(duì)親兄弟了韧掩,兩者的使用都比較頻繁而且在使用上亦有很多相似之處(畢竟都是有Context的人)济似。但是“親兄弟也要明算賬”這對(duì)兄弟一個(gè)負(fù)責(zé)在外面“拋頭露面”巡社,一個(gè)負(fù)責(zé)在后面“默默無(wú)聞”删掀。
“拋頭露面”的那位已經(jīng)介紹完了,現(xiàn)在來(lái)看看久居后臺(tái)的這位。

Service 是一個(gè)可以在后臺(tái)執(zhí)行長(zhǎng)時(shí)間運(yùn)行操作而不提供用戶(hù)界面的應(yīng)用組件饰豺。
服務(wù)可由其他應(yīng)用組件啟動(dòng)齐莲,而且即使用戶(hù)切換到其他應(yīng)用痢站,服務(wù)仍將在后臺(tái)繼續(xù)運(yùn)行。 此外选酗,組件可以綁定到服務(wù)阵难,以與之進(jìn)行交互,甚至是執(zhí)行進(jìn)程間通信 (IPC)星掰。

1.注冊(cè)

既然進(jìn)行注冊(cè)那肯定少不了AndroidManifest.xml文件多望,畢竟package中所有暴露的組件都需要在這里聲明。與Activity相同氢烘,Service的注冊(cè)就是在 <application/> 節(jié)點(diǎn)下添加一個(gè)<service/>節(jié)點(diǎn):

<manifest ... > 
    ... 
    <application ... >     
        <service android:name=".TestService" />    
        ...  
    </application>
</manifest>

在注冊(cè)時(shí)只有android:name屬性是必須的屬性,其他屬性可以根據(jù)自己的情況進(jìn)行添加家厌,這里不額外進(jìn)行介紹了播玖,推薦在開(kāi)發(fā)者文檔進(jìn)行查看。

注意一點(diǎn)點(diǎn):
為了確保應(yīng)用的安全性饭于,請(qǐng)始終使用顯式 Intent 啟動(dòng)或綁定 Service蜀踏,且不要為服務(wù)聲明 Intent 過(guò)濾器。

2.生命周期的前一章(啟動(dòng)/綁定狀態(tài))

因?yàn)?code>service的生命周期和使用service的處理方式密切相關(guān)掰吕,所以這里插一段說(shuō)明一下潮瓶。
對(duì)于service的處理方式良瞧,有兩種態(tài)度,一種是 "放養(yǎng)" :任務(wù)給你了,你去干吧基跑,其他的我就不管了,但是讓你停下時(shí)必須停下引瀑;另一種是 "放風(fēng)箏" :任務(wù)給你了驼鹅,但是你要跟著繩子走,如果繩子斷了纽门,你就會(huì)掉下來(lái)薛耻。

  • 啟動(dòng)狀態(tài)
    ??當(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ù)的組件已被銷(xiāo)毀也不受影響蝙搔,除非手動(dòng)調(diào)用才能停止服務(wù)缕溉, 已啟動(dòng)的服務(wù)通常是執(zhí)行單一操作,而且不會(huì)將結(jié)果返回給調(diào)用方杂瘸。

  • 綁定狀態(tài)
    ??當(dāng)應(yīng)用組件通過(guò)調(diào)用 bindService() 綁定到服務(wù)時(shí)倒淫,服務(wù)即處于“綁定”狀態(tài)。綁定服務(wù)提供了一個(gè)客戶(hù)端-服務(wù)器接口,允許組件與服務(wù)進(jìn)行交互敌土、發(fā)送請(qǐng)求镜硕、獲取結(jié)果,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作返干。 僅當(dāng)與另一個(gè)應(yīng)用組件綁定時(shí)兴枯,綁定服務(wù)才會(huì)運(yùn)行。 多個(gè)組件可以同時(shí)綁定到該服務(wù)矩欠,但全部取消綁定后财剖,該服務(wù)即會(huì)被銷(xiāo)毀。

3.生命周期

不一樣的選擇會(huì)有不一樣的人生癌淮,不同的狀態(tài)自然有不同的生命周期躺坟。下面我們看一下Service這位默默無(wú)聞的人的“人生路線(xiàn)”。

Service的生命周期

使用且將被銷(xiāo)毀時(shí)乳蓄,系統(tǒng)將調(diào)用此方法咪橙。服務(wù)應(yīng)該實(shí)現(xiàn)此方法來(lái)清理所有資源,如線(xiàn)程虚倒、注冊(cè)的偵聽(tīng)器美侦、接收器等,這是服務(wù)接收的最后一個(gè)調(diào)用魂奥。(就是在這里寫(xiě)遺書(shū)菠剩,分配一下“死”后的“財(cái)產(chǎn)”)

然后就是Service正式“參與工作”(有效生命周期):

  1. 第一種是通過(guò) 調(diào)用startService() 開(kāi)啟服務(wù):
  • onStartCommand()
    當(dāng)開(kāi)啟者(如 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)行(一直運(yùn)行到onDestroy,與開(kāi)啟者無(wú)關(guān))违霞。 如果自己實(shí)現(xiàn)此方法嘴办,則需要在服務(wù)工作完成后,通過(guò)調(diào)用 stopSelf()stopService() 來(lái)停止服務(wù)买鸽。
  1. 第二種是通過(guò)調(diào)用bindService() 開(kāi)啟服務(wù):
  • onBind()
    當(dāng)開(kāi)啟者(如 Activity)想通過(guò)調(diào)用 bindService() 與服務(wù)綁定時(shí)涧郊,系統(tǒng)將調(diào)用此方法。在此方法的實(shí)現(xiàn)中眼五,必須返回 一個(gè)IBinder 接口的實(shí)現(xiàn)類(lèi)妆艘,供客戶(hù)端用來(lái)與服務(wù)進(jìn)行通信。
  • onUnbind()
    當(dāng)開(kāi)啟者(如 Activity)想通過(guò)調(diào)用 unbindService() 與服務(wù)解除綁定時(shí)看幼,系統(tǒng)將調(diào)用此方法批旺。Service解除綁定后將被銷(xiāo)毀

注意一點(diǎn)點(diǎn):
無(wú)論是啟動(dòng)狀態(tài)還是綁定狀態(tài)诵姜,onBind() 方法必須重寫(xiě)汽煮,但在啟動(dòng)狀態(tài)的情況下直接返回 null
在綁定狀態(tài)下,無(wú)需實(shí)現(xiàn)onStartCommand() 方法

4.Service啟動(dòng)狀態(tài)

從上面生命周期的圖中暇赤,我們可以看到啟動(dòng)服務(wù)是通過(guò)startService()啟動(dòng)心例,這會(huì)調(diào)用服務(wù)的onCreate()(僅第一次)和onStartCommand()方法,直到stopService()停止服務(wù)鞋囊。直到了基本流程了止后,我們來(lái)寫(xiě)一個(gè)啟動(dòng)狀態(tài)的Service:

package com.test.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Service 啟動(dòng)狀態(tài)
 *
 * @author ydd
 */
public class StartServiceTest extends Service{

    private static final String TAG = "StartServiceTest";

    /**
     * 服務(wù)綁定時(shí)調(diào)用,且必須要實(shí)現(xiàn)該方法(abstract函數(shù))
     * @param intent 接收 bindService()的傳入
     * @return 返回IBinder接口的實(shí)現(xiàn)溜腐,在綁定狀態(tài)使用译株,本處為空
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()");
        return null;
    }

    /**
     * 首次創(chuàng)建服務(wù)時(shí)調(diào)用,且super中無(wú)任何實(shí)現(xiàn)
     */
    @Override
    public void onCreate() {
        Log.d(TAG,"onCreate()");
        super.onCreate();
    }

    /**
     * 每次通過(guò)startService()方法啟動(dòng)Service時(shí)都會(huì)被回調(diào)挺益。
     * @param intent 啟動(dòng)時(shí)startService()傳輸過(guò)來(lái)的數(shù)據(jù)包
     * @param flags 表示啟動(dòng)請(qǐng)求時(shí)是否有額外數(shù)據(jù)
     * @param startId 指定當(dāng)前服務(wù)的唯一Id
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 銷(xiāo)毀服務(wù)時(shí)調(diào)用
     */
    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy()");
        super.onDestroy();
    }
}

服務(wù)寫(xiě)起來(lái)很簡(jiǎn)單Service中的abstract方法只有onBind(),所以這里必須實(shí)現(xiàn)onBind歉糜,為了看一下完整的生命周期運(yùn)行,這里在幾個(gè)關(guān)鍵的回調(diào)函數(shù)中打印Log矩肩,看一下運(yùn)行情況现恼。下面再實(shí)現(xiàn)一下操作界面:

package com.test.servicetest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener{


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this , StartServiceTest.class);
        switch (v.getId()){
            case R.id.btnStartService:
                startService(intent);
                break;
            case R.id.btnStopService:
                stopService(intent);
                break;
            default:
                break;
        }
    }
}

界面只添加了兩個(gè)按鈕一個(gè)用于啟動(dòng)服務(wù)(調(diào)用startService())一個(gè)用于關(guān)閉服務(wù)(調(diào)用stopService()).代碼都準(zhǔn)備好了,下面我們開(kāi)始進(jìn)行測(cè)試黍檩。
說(shuō)明一下:本次操作為連續(xù)2次啟動(dòng)服務(wù),然后停止服務(wù)始锚。再次開(kāi)啟服務(wù)并停止服務(wù)刽酱。

    ——————————第一次開(kāi)啟————————————
    com.test.servicetest D/StartServiceTest: onCreate()
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————第二次開(kāi)啟————————————
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————關(guān)閉服務(wù)—————————————
    com.test.servicetest D/StartServiceTest: onDestroy()
    ——————————重新開(kāi)啟服務(wù)———————————
    com.test.servicetest D/StartServiceTest: onCreate()
    com.test.servicetest D/StartServiceTest: onStartCommand()
    ——————————關(guān)閉服務(wù)—————————————
    com.test.servicetest D/StartServiceTest: onDestroy()

第一次啟動(dòng)時(shí)從Log中我們可以清晰的看出第一次啟動(dòng)時(shí)調(diào)用了onCreate()onStartCommand()方法,之后在開(kāi)啟時(shí)瞧捌,因?yàn)?Service 已經(jīng)存在了棵里,就只調(diào)用onStartCommand()方法。關(guān)閉服務(wù)調(diào)用onDestroy()方法姐呐,當(dāng)服務(wù)關(guān)閉后殿怜,如果再次啟動(dòng)就會(huì)開(kāi)始新的生命周期。
下面來(lái)點(diǎn)好玩的東西:
從上面可以看出onStartCommand()是整個(gè)開(kāi)啟服務(wù)的一個(gè)“有趣”點(diǎn)曙砂,這里簡(jiǎn)單介紹一下這個(gè)函數(shù)头谜。
我們先看傳入的參數(shù):

  • intent:是startService()時(shí)傳過(guò)來(lái)的;

  • flags:是系統(tǒng)傳入 有如下三種值:

    1. 直接通過(guò) startService() 啟動(dòng)時(shí),flags為0鸠澈;
    2. onStartCommand()返回為START_STICKY_COMPATIBILITY或者START_STICKY并且服務(wù)異常殺死后由系統(tǒng)啟動(dòng)柱告;flags為START_FLAG_REDELIVERY=1:
    3. onStartCommand()返回為START_REDELIVER_INTENT并且服務(wù)異常殺死后由系統(tǒng)啟動(dòng);flags為START_FLAG_REDELIVERY=2:
  • startId:表示此特定啟動(dòng)請(qǐng)求的唯一整數(shù)笑陈。用于唯一表示某次請(qǐng)求际度。大概是onStartCommand()的啟動(dòng)次數(shù),第一次通過(guò)startService啟動(dòng)為是1涵妥,不斷startService啟動(dòng)依次累加乖菱,一般配合stopSelf(startId)使用可以看IntentService中使用。

上面出現(xiàn)了幾個(gè)陌生的面孔:START_STICKY_COMPATIBILITY、START_STICKY窒所、START_REDELIVER_INTENT
這幾個(gè)參數(shù)對(duì)應(yīng)了onStartCommand()的函數(shù)返回值:

  • START_STICKY
    當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后鹉勒,一段時(shí)間后內(nèi)存再次空閑時(shí),系統(tǒng)將會(huì)嘗試重新創(chuàng)建此Service墩新,一旦創(chuàng)建成功后將回調(diào)onStartCommand方法贸弥,但其中的Intent將是null,除非有掛起的Intent海渊,如pendingintent绵疲,這個(gè)狀態(tài)下比較適用于不執(zhí)行命令、但無(wú)限期運(yùn)行并等待作業(yè)的媒體播放器或類(lèi)似服務(wù)臣疑。
  • START_NOT_STICKY
    當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后盔憨,即使系統(tǒng)內(nèi)存再次空閑時(shí),系統(tǒng)也不會(huì)嘗試重新創(chuàng)建此Service讯沈。除非程序中再次調(diào)用startService啟動(dòng)此Service郁岩,這是最安全的選項(xiàng),可以避免在不必要時(shí)以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時(shí)運(yùn)行服務(wù)缺狠。
  • START_REDELIVER_INTENT
    當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后问慎,則會(huì)重建服務(wù),并通過(guò)傳遞給服務(wù)的最后一個(gè) Intent 調(diào)用 onStartCommand()挤茄,任何掛起 Intent均依次傳遞如叼。與START_STICKY不同的是,其中的傳遞的Intent將是非空穷劈,是最后一次調(diào)用startService中的intent笼恰。這個(gè)值適用于主動(dòng)執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)。
  • START_STICKY_COMPATIBILITY
    START_STICKY的兼容版本歇终,但不保證服務(wù)被kill后一定能重啟社证。

有點(diǎn)亂,這里梳理一下思路:使用startService()啟動(dòng)服務(wù)后评凝,在Service中onStartCommand()被調(diào)用(如果是第一次追葡,會(huì)首先調(diào)用onCreate())。我們?cè)?code>onStartCommand()方法中根據(jù)接收傳入的參數(shù)肥哎,并根據(jù)flags和startId確認(rèn)當(dāng)前開(kāi)啟服務(wù)的狀態(tài)辽俗,開(kāi)始處理事務(wù),最后根據(jù)當(dāng)前的需求設(shè)置不同的返回值篡诽,以便程序更好的對(duì)當(dāng)前Service進(jìn)行控制崖飘。

5.Service綁定狀態(tài)

不同于啟動(dòng)狀態(tài)的服務(wù),綁定狀態(tài)的Service不能單獨(dú)存在杈女,需要與其他的組件之間建立聯(lián)系朱浴。從生命周期上看吊圾,只需要通過(guò)onBind()函數(shù)返回一個(gè)IBinder接口的實(shí)現(xiàn)類(lèi),用以提供客戶(hù)端用來(lái)與服務(wù)進(jìn)行交互的編程接口翰蠢,再通過(guò)onUnbind()接觸綁定結(jié)束工作项乒。想起來(lái)總是比實(shí)際簡(jiǎn)單,根據(jù)情況的不同實(shí)際可以通過(guò)三種方法定義接口:

  • 擴(kuò)展 Binder 類(lèi)
    如果服務(wù)是供您的自有應(yīng)用專(zhuān)用梁沧,并且在與客戶(hù)端相同的進(jìn)程中運(yùn)行(常見(jiàn)情況)檀何,則應(yīng)通過(guò)擴(kuò)展 Binder 類(lèi)并從 onBind() 返回它的一個(gè)實(shí)例來(lái)創(chuàng)建接口⊥⒅В客戶(hù)端收到 Binder 后频鉴,可利用它直接訪(fǎng)問(wèn) Binder 實(shí)現(xiàn)中乃至 Service 中可用的公共方法。
    如果服務(wù)只是您的自有應(yīng)用的后臺(tái)工作線(xiàn)程恋拍,則優(yōu)先采用這種方法垛孔。 不以這種方式創(chuàng)建接口的唯一原因是,您的服務(wù)被其他應(yīng)用或不同的進(jìn)程占用施敢。
  • 使用 Messenger
    如需讓接口跨不同的進(jìn)程工作周荐,則可使用 Messenger 為服務(wù)創(chuàng)建接口。服務(wù)可以這種方式定義對(duì)應(yīng)于不同類(lèi)型Message 對(duì)象的 Handler僵娃。此 Handler 是 Messenger 的基礎(chǔ)概作,后者隨后可與客戶(hù)端分享一個(gè) IBinder,從而讓客戶(hù)端能利用 Message 對(duì)象向服務(wù)發(fā)送命令默怨。此外仆嗦,客戶(hù)端還可定義自有 Messenger,以便服務(wù)回傳消息先壕。
    這是執(zhí)行進(jìn)程間通信 (IPC) 的最簡(jiǎn)單方法,因?yàn)?Messenger 會(huì)在單一線(xiàn)程中創(chuàng)建包含所有請(qǐng)求的隊(duì)列谆甜,這樣您就不必對(duì)服務(wù)進(jìn)行線(xiàn)程安全設(shè)計(jì)垃僚。
  • 使用 AIDL
    AIDL(Android 接口定義語(yǔ)言)執(zhí)行所有將對(duì)象分解成原語(yǔ)的工作,操作系統(tǒng)可以識(shí)別這些原語(yǔ)并將它們編組到各進(jìn)程中规辱,以執(zhí)行 IPC谆棺。 之前采用 Messenger 的方法實(shí)際上是以 AIDL 作為其底層結(jié)構(gòu)。 如上所述罕袋,Messenger 會(huì)在單一線(xiàn)程中創(chuàng)建包含所有客戶(hù)端請(qǐng)求的隊(duì)列改淑,以便服務(wù)一次接收一個(gè)請(qǐng)求。 不過(guò)浴讯,如果您想讓服務(wù)同時(shí)處理多個(gè)請(qǐng)求朵夏,則可直接使用 AIDL。 在此情況下榆纽,您的服務(wù)必須具備多線(xiàn)程處理能力仰猖,并采用線(xiàn)程安全式設(shè)計(jì)捏肢。
    如需直接使用 AIDL,您必須創(chuàng)建一個(gè)定義編程接口的 .aidl 文件饥侵。Android SDK 工具利用該文件生成一個(gè)實(shí)現(xiàn)接口并處理 IPC 的抽象類(lèi)鸵赫,您隨后可在服務(wù)內(nèi)對(duì)其進(jìn)行擴(kuò)展。

5.1 擴(kuò)展 Binder 類(lèi)

本方法無(wú)法跨進(jìn)程工作躏升,所以只適用于僅提供本應(yīng)用服務(wù)的 Service辩棒。通過(guò)實(shí)現(xiàn)自有的 IBinder ,讓客戶(hù)端通過(guò)該接口直接訪(fǎng)問(wèn)服務(wù)中的公共方法膨疏。
擴(kuò)展 Binder 類(lèi)的使用方法大致分為三步:

  1. 在您的服務(wù)中一睁,創(chuàng)建一個(gè)可滿(mǎn)足下列任一要求的IBinder 實(shí)例
    • 包含客戶(hù)端可調(diào)用的公共方法
    • 返回當(dāng)前 Service 實(shí)例,其中包含客戶(hù)端可調(diào)用的公共方法
    • 或返回由服務(wù)承載的其他類(lèi)的實(shí)例成肘,其中包含客戶(hù)端可調(diào)用的公共方法
  2. onBind() 回調(diào)方法返回此 Binder 實(shí)例卖局。
  3. 在客戶(hù)端中,從 onServiceConnected() 回調(diào)方法接收 Binder實(shí)例双霍,并使用提供的方法調(diào)用綁定服務(wù)砚偶。

有啟動(dòng)狀態(tài)的鋪墊,這里直接上代碼:

package com.test.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Service 綁定狀態(tài)
 *
 * @author ydd
 */
public class BindServiceTest extends Service{

    private static final String TAG = "BindServiceTest";
    private final IBinder mBinder = new TestBinder();
    
    private int count;
    private boolean stop;

    /**
     * 服務(wù)綁定時(shí)調(diào)用洒闸,且必須要實(shí)現(xiàn)該方法(abstract函數(shù))
     * @param intent 接收 bindService()的傳入
     * @return 返回IBinder接口的實(shí)現(xiàn)
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind()");
        return mBinder;
    }

    /**
     * 首次創(chuàng)建服務(wù)時(shí)調(diào)用染坯,且super中無(wú)任何實(shí)現(xiàn)
     */
    @Override
    public void onCreate() {
        Log.d(TAG,"onCreate()");
        super.onCreate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!stop) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        }).start();
    }

    /**
     * 解除Service綁定
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"onUnbind()");
        return super.onUnbind(intent);
    }

    /**
     * 銷(xiāo)毀服務(wù)時(shí)調(diào)用
     */
    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy()");
        stop = true;
        super.onDestroy();
    }
    
    /**
     * 獲取服務(wù)時(shí)長(zhǎng)
     */
    public int getCount(){
        return count;
    }

    /**
     * 定制需要傳出的Binder
     */
    public class TestBinder extends Binder {
        BindServiceTest getService(){
            return BindServiceTest.this;
        }
    }
}

BindService的創(chuàng)建和StartService的最大的區(qū)別是增加了Binder的實(shí)現(xiàn)以及,在onBind()函數(shù)中不再返回空值丘逸,更改為一個(gè)Binder對(duì)象单鹿。另外既然是可以互相通信的,我們就在Service中記錄開(kāi)啟的時(shí)間長(zhǎng)短深纲,并在開(kāi)啟者組件中獲取一下這個(gè)數(shù)據(jù)看一下仲锄。下面看一下控制界面的代碼。

package com.test.servicetest;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "BindServiceTest";
    private ServiceConnection connection;
    private BindServiceTest mService;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        Button getDataBtn = (Button) findViewById(R.id.btnGetData);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
        getDataBtn.setOnClickListener(this);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG,"——onServiceConnected");
                mService = ((BindServiceTest.TestBinder) service).getService();

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.d(TAG,"——onServiceDisconnected");
                mService = null;
            }
        };
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, BindServiceTest.class);
        switch (v.getId()) {
            case R.id.btnStartService:
                bindService(intent, connection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btnStopService:
                unbindService(connection);
                break;
            case R.id.btnGetData:
                if (mService != null) {
                    Log.d(TAG, "服務(wù)當(dāng)前已經(jīng)開(kāi)啟:" + mService.getCount() + "秒");
                }else {
                    Log.d(TAG, "服務(wù)當(dāng)前未開(kāi)啟湃鹊!");
                }
                break;
            default:
                break;
        }
    }
}

根據(jù)啟動(dòng)模式的Service的講解和兩種狀態(tài)的生命周期儒喊,我們應(yīng)該可以知道兩者的開(kāi)啟和關(guān)閉調(diào)用的方法是不同的。在綁定狀態(tài)下币呵,我們使用的是bindService()unbindService()函數(shù)來(lái)控制Service怀愧。另外這里使用了ServiceConnected,并實(shí)現(xiàn)了兩個(gè)函數(shù):

  • onServiceConnected(ComponentName name, IBinder service)
    該函數(shù)在服務(wù)連接成功后調(diào)用余赢,獲取onBind() 方法返回的 IBinder芯义。這里的service便是w我們?cè)?code>Service中定義的Binder實(shí)現(xiàn)類(lèi)對(duì)象。通過(guò)該對(duì)象我們便可以按照我們?cè)O(shè)計(jì)的方法對(duì)Service進(jìn)行操作妻柒,在上面的例子中我們獲取了BindServiceTest的實(shí)例扛拨,進(jìn)而調(diào)用服務(wù)端的公共方法。

ComponentName是一個(gè)封裝了組件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的類(lèi)蛤奢,如包名鬼癣,組件描述等信息陶贼,較少使用該參數(shù)。

  • onServiceDisconnected(ComponentName name)
    Android 系統(tǒng)會(huì)在與服務(wù)的連接意外中斷時(shí)(例如當(dāng)服務(wù)崩潰或被終止時(shí))調(diào)用該方法待秃。
    注意:當(dāng)客戶(hù)端取消綁定時(shí)拜秧,系統(tǒng)“絕對(duì)不會(huì)”調(diào)用該方法。

看完ServiceConnected我們?cè)僦匦驴匆幌潞瘮?shù)bindService()章郁,在參數(shù)中處理Intent和剛剛提到的ServiceConnected還有一個(gè)int flags,這個(gè)參數(shù)有時(shí)負(fù)責(zé)控制什么的枉氮?
簡(jiǎn)單的說(shuō)就是對(duì)綁定進(jìn)行控制操作(因?yàn)槲抑皇侵篮?jiǎn)單的),例如上面使用到的參數(shù):

  • BIND_AUTO_CREATE:就是用來(lái)控制創(chuàng)建Service暖庄。在啟動(dòng)模式中調(diào)用startService()時(shí)實(shí)際上是通過(guò)啟動(dòng)者來(lái)創(chuàng)建一個(gè)Service實(shí)例聊替。在綁定狀態(tài)中有些區(qū)別,是向serviceMessenger發(fā)送一個(gè)請(qǐng)求培廓,由它來(lái)控制是否創(chuàng)建Service惹悄。

補(bǔ)充一點(diǎn)點(diǎn)

  1. 當(dāng) flags 不等于 BIND_AUTO_CREATE 時(shí),bindService是不會(huì)自動(dòng)啟動(dòng)service的肩钠。
  2. 與啟動(dòng)模式與綁定模式的參數(shù)不同:
    bindService(Intent service, ServiceConnection conn, int flags)
    unbindService(ServiceConnection conn)

5.2 Messenger

終于到Messenger了泣港,一種可以跨進(jìn)程通訊的方式,用起來(lái)稍微比上面的方式復(fù)雜一點(diǎn)价匠,不過(guò)畢竟功能更強(qiáng)(可以跨進(jìn)程通訊)当纱,稍微難一點(diǎn)也是可以理解的。這里想大致介紹一下使用方法:

  • 服務(wù)端(Service)
  1. 創(chuàng)建一個(gè)handler(Service)對(duì)象踩窖,并實(shí)現(xiàn)hanlemessage方法坡氯,用于接收來(lái)自客戶(hù)端的消息,并作處理洋腮;
  2. 創(chuàng)建一個(gè)messenger(Service)箫柳,,封裝handler(Service);
  3. 用messenger(Service)的getBinder()方法獲取一個(gè)IBinder對(duì)象啥供,通過(guò)onBind返回給客戶(hù)端滞时;
  4. 等待客戶(hù)端發(fā)來(lái)的消息;
  • 客戶(hù)端(Activity)
  1. 在activity中綁定服務(wù)滤灯;
  2. 創(chuàng)建一個(gè)handler(Client)對(duì)象,并實(shí)現(xiàn)hanlemessage方法曼玩,用于接收Service返回的消息鳞骤,并作處理;
  3. 創(chuàng)建一個(gè)messenger(Client)黍判,,封裝handler(Client)豫尽;
  4. 創(chuàng)建ServiceConnection并在其中使用 IBinder 將 messenger(Service)實(shí)例化(這里就是Service中的創(chuàng)建一個(gè)messenger,通過(guò)IBinder的方式來(lái)到了客戶(hù)端) 顷帖;
  5. 創(chuàng)建Message并在其中加載數(shù)據(jù)美旧,注意為了能接收Service的回復(fù)信息渤滞,這里將Message.replyTo設(shè)置為messenger(Client);
  6. 使用Messenger(Service)向服務(wù)端發(fā)送消息榴嗅;
  • 服務(wù)端(Service)
  1. 接收到Messager(Service)帶回來(lái)的消息妄呕,進(jìn)行處理;
  2. 創(chuàng)建一個(gè)新的Message嗽测,裝載處理結(jié)果绪励;
  3. 使用Message.replyTo調(diào)用Messenger(Client)向客戶(hù)端發(fā)送消息;
  • 客戶(hù)端(Activity)
  1. 接收到Messager(Client)帶回來(lái)的消息唠粥,進(jìn)行處理進(jìn)行相關(guān)顯示操作疏魏;
  2. 整體操作完成,解除綁定晤愧;

補(bǔ)充一點(diǎn)點(diǎn):
Messenger個(gè)人感覺(jué)大莫,理解為收信人比較好。如果A需要向B發(fā)消息官份,A中就需要有一個(gè)B的收信人B在只厘,A將Message交給收信人B,并由收信人B帶給(send)B贯吓。這時(shí)如果A想知道B的回復(fù)懈凹,就派自己的收信人A,跟著(replyTo)收信人B一起去B,等B的處理結(jié)果,由收信人A帶回A悄谐。

說(shuō)起來(lái)有點(diǎn)繞介评,大家看一下代碼,應(yīng)該會(huì)更好理解:
服務(wù)端

package com.test.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * 綁定服務(wù)使用Messenger
 *
 * @author ydd
 */
public class MessengerService extends Service {

    private static final int SIGN = 1;
    private final Messenger mMessenger = new Messenger(new CustomHandler());

    private class CustomHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            switch (msg.what) {
                case SIGN:
                    message.arg1 = msg.arg1 + msg.arg2;
                    message.what = SIGN;
                    try {
                        msg.replyTo.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

客戶(hù)端

package com.test.servicetest;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
/**
 * 主界面
 *
 * @author ydd
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final int SIGN = 1;
    private ServiceConnection connection;
    private boolean mBound;
    private Messenger mService;

    private Messenger mMessenger = new Messenger(new MainHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startServiceBtn = (Button) findViewById(R.id.btnStartService);
        Button stopServiceBtn = (Button) findViewById(R.id.btnStopService);
        Button setDataBtn = (Button) findViewById(R.id.btnSetData);
        startServiceBtn.setOnClickListener(this);
        stopServiceBtn.setOnClickListener(this);
        setDataBtn.setOnClickListener(this);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mService = new Messenger(service);
                mBound = true;
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mService = null;
                mBound = false;
            }
        };
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, MessengerService.class);
        switch (v.getId()) {
            case R.id.btnStartService:
                bindService(intent, connection, Service.BIND_AUTO_CREATE);
                break;
            case R.id.btnStopService:
                unbindService(connection);
                break;
            case R.id.btnSetData:
                if (mBound) {
                    try {
                        Message message = new Message();
                        message.arg1 = 3;
                        message.arg2 = 4;
                        message.what = SIGN;
                        message.replyTo = mMessenger;
                        mService.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(this,
                            "當(dāng)前服務(wù)未打開(kāi)爬舰,請(qǐng)開(kāi)啟服務(wù)们陆!", Toast.LENGTH_LONG).show();
                }
                break;
            default:
                break;
        }
    }

    private class MainHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SIGN:
                    Toast.makeText(MainActivity.this,
                            "服務(wù)器說(shuō)等于:" + msg.arg1, Toast.LENGTH_LONG).show();
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }

}

客戶(hù)端連接到服務(wù)器后,向服務(wù)器發(fā)送兩個(gè)值“3”“4”情屹,然后服務(wù)器求和計(jì)算回復(fù)“7”坪仇,服務(wù)器接收到回復(fù)消息,并顯示在界面垃你。

補(bǔ)充一點(diǎn)點(diǎn)
想深入了解Messager的話(huà)推薦《Android 基于Message的進(jìn)程間通信 Messenger完全解析》鴻洋大神的文章

6.注意事項(xiàng)

6.1同時(shí)啟動(dòng)和綁定

前面介紹了Service的啟動(dòng)狀態(tài)和綁定狀態(tài)椅文,應(yīng)該還記得在Service中onBind()是必須實(shí)現(xiàn)的方法,那是不是就意味著惜颇,在啟動(dòng)狀態(tài)的Service中皆刺,onBind()不返回null,就可以啟動(dòng)綁定模式凌摄?同理羡蛾,在綁定狀態(tài)的Service中,是不是也可以進(jìn)入啟動(dòng)狀態(tài)锨亏?
答案是肯定的痴怨。
需要注意一點(diǎn)的時(shí)忙干,只要Service進(jìn)入過(guò)啟動(dòng)狀態(tài)(調(diào)用startService()),服務(wù)將在后臺(tái)運(yùn)行,直到有Context調(diào)用了stopService()或是服務(wù)本身調(diào)用了stopSelf()方法抑或內(nèi)存不足時(shí)才會(huì)銷(xiāo)毀服務(wù)浪藻。

6.2生命周期控制

因?yàn)镾ervice本身沒(méi)有界面空著捐迫,為了把控Service的生命周期,通常情況下需要在客戶(hù)端(例如Activity)生命周期中于合適的時(shí)間開(kāi)啟(綁定)和關(guān)閉(解綁)Service珠移。需要注意的是切勿在 Activity 的 onResume() 和 onPause() 期間綁定和取消綁定弓乙,因?yàn)槊恳淮紊芷谵D(zhuǎn)換都會(huì)發(fā)生這些回調(diào),這樣反復(fù)綁定與解綁是不合理的钧惧。

6.3使用服務(wù)還是線(xiàn)程暇韧?

簡(jiǎn)單地說(shuō),服務(wù)是一種即使用戶(hù)未與應(yīng)用交互也可在后臺(tái)運(yùn)行的組件浓瞪。 因此懈玻,您應(yīng)僅在必要時(shí)才創(chuàng)建服務(wù)。
如需在主線(xiàn)程外部執(zhí)行工作乾颁,不過(guò)只是在用戶(hù)正在與應(yīng)用交互時(shí)才有此需要涂乌,則應(yīng)創(chuàng)建新線(xiàn)程而非服務(wù)。 例如英岭,如果您只是想在 Activity 運(yùn)行的同時(shí)播放一些音樂(lè)湾盒,則可在 onCreate() 中創(chuàng)建線(xiàn)程,在 onStart() 中啟動(dòng)線(xiàn)程诅妹,然后在 onStop() 中停止線(xiàn)程罚勾。您還可以考慮使用 AsyncTask 或 HandlerThread,而非傳統(tǒng)的 Thread 類(lèi)吭狡。如需了解有關(guān)線(xiàn)程的詳細(xì)信息尖殃,請(qǐng)參閱進(jìn)程和線(xiàn)程文檔。
請(qǐng)記住划煮,如果您確實(shí)要使用服務(wù)送丰,則默認(rèn)情況下,它仍會(huì)在應(yīng)用的主線(xiàn)程中運(yùn)行弛秋,因此器躏,如果服務(wù)執(zhí)行的是密集型或阻止性操作,則您仍應(yīng)在服務(wù)內(nèi)創(chuàng)建新線(xiàn)程蟹略⊙#——《Android Developers Doc》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市科乎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贼急,老刑警劉巖茅茂,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捏萍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡空闲,警方通過(guò)查閱死者的電腦和手機(jī)令杈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)碴倾,“玉大人逗噩,你說(shuō)我怎么就攤上這事〉疲” “怎么了异雁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)僧须。 經(jīng)常有香客問(wèn)我纲刀,道長(zhǎng),這世上最難降的妖魔是什么担平? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任示绊,我火速辦了婚禮,結(jié)果婚禮上暂论,老公的妹妹穿的比我還像新娘面褐。我一直安慰自己,他們只是感情好取胎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布展哭。 她就那樣靜靜地躺著,像睡著了一般扼菠。 火紅的嫁衣襯著肌膚如雪摄杂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天循榆,我揣著相機(jī)與錄音析恢,去河邊找鬼。 笑死秧饮,一個(gè)胖子當(dāng)著我的面吹牛映挂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盗尸,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柑船,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了泼各?” 一聲冷哼從身側(cè)響起鞍时,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后逆巍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體及塘,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年锐极,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笙僚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灵再,死狀恐怖肋层,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情翎迁,我是刑警寧澤栋猖,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站鸳兽,受9級(jí)特大地震影響掂铐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揍异,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一全陨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衷掷,春花似錦辱姨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至懦胞,卻和暖如春替久,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躏尉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工蚯根, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胀糜。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓颅拦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親教藻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子距帅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • 服務(wù)基本上分為兩種形式 啟動(dòng) 當(dāng)應(yīng)用組件(如 Activity)通過(guò)調(diào)用 startService() 啟動(dòng)服務(wù)時(shí)...
    pifoo閱讀 1,256評(píng)論 0 8
  • Android四大組件之ActivityAndroid四大組件之ServiceAndroid四大組件之Broadc...
    MonkeyLqj閱讀 2,204評(píng)論 0 4
  • Service的相關(guān)知識(shí)雖然簡(jiǎn)單,但是也比較瑣碎括堤,其衍生知識(shí)也比較多碌秸。本篇從Service的生命周期绍移、運(yùn)行和使用方...
    卑鄙的鹿尤菌閱讀 800評(píng)論 0 1
  • 引子在前三節(jié)中對(duì)Android中的Activity進(jìn)行了研究學(xué)習(xí), 本節(jié)開(kāi)始我們繼續(xù)來(lái)學(xué)習(xí)Android中的第二個(gè)...
    pifoo閱讀 601評(píng)論 0 3
  • Service 一讥电、基礎(chǔ)知識(shí) 1登夫、定義 服務(wù),屬于Android中的計(jì)算型組件 2允趟、作用 提供需要在后臺(tái)長(zhǎng)期運(yùn)行的...
    AndroidMaster閱讀 278評(píng)論 0 0