Service組件

一、Service概述

Service是Android中實現(xiàn)程序后臺運行的解決方案,非常適合用于去執(zhí)行那些不需要和用戶交互但還需要長期運行的任務(wù)。

Service不能運行在一個獨立的進(jìn)程當(dāng)中,而是依賴與創(chuàng)建服務(wù)時所在的應(yīng)用程序進(jìn)程。并且只能在后臺運行,并且可以和其他組件進(jìn)行交互。

Service不會自動開啟一個線程,需要我們在Service中手動創(chuàng)建子線程,并在這里執(zhí)行具體的任務(wù)。

二、如何定義Service

步驟如下:

  • 創(chuàng)建一個類,繼承android.app.service類。實現(xiàn)抽象方法onBind(),重寫onCreate()、onStartCommand()、onDestroy()。
  • 在清單文件AndroidManifest.xml中注冊Service。

1、新建一個MyService類,繼承自Service

public class MyService extends Service {  
      
    public static final String TAG = "MyService";  
  
    //創(chuàng)建服務(wù)時調(diào)用
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate");  
    }  
  
    //服務(wù)執(zhí)行的操作
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand");  
        return super.onStartCommand(intent, flags, startId);  
    }  
      
    //銷毀服務(wù)時調(diào)用
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy");  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
}

2、在清單文件中注冊

<service android:name=".MyService">        </service>

Service可以有兩種啟動方式:一種是startService(),另一種是bindService(),下面分別進(jìn)行說明。

三、啟動和停止Service

分別構(gòu)建出兩個Intent對象,用來調(diào)用startService()方法和stopService()方法,來啟動和停止MyService。
注意:startService()和stopService()方法都是定義在Context類當(dāng)中的,所以可以在Activity中直接調(diào)用這兩個方法。

@Override  
public void onClick(View v) {  
    switch (v.getId()) {  
    case R.id.button1_start_service:  
        Intent startIntent = new Intent(this, MyService.class);  
        startService(startIntent);  
        break;  
    case R.id.button2_stop_service:  
        Intent stopIntent = new Intent(this, MyService.class);  
        stopService(stopIntent);  
        break;  
    default:  
        break;  
    }  
}  

1、啟動服務(wù)
點擊button1_start_service按鈕,啟動服務(wù),后臺打印日志如下:

image.png

再連續(xù)點三次button1_start_service按鈕,后臺增加的日志如下:

image

結(jié)論:onCreate()方法只會在Service第一次被創(chuàng)建的時候調(diào)用,而onStartCommand()方法在每次啟動服務(wù)的時候都會調(diào)用。
注意:

  • 服務(wù)對象同時只會有一個。
  • 默認(rèn)情況下,一個started的Service與啟動他的組件在同一個線程中。上面的實例中,服務(wù)就是在主線程中運行的,如果是在服務(wù)中完成耗時操作的話,容易造成主線程阻塞。

2、停止服務(wù)
停止一個started服務(wù)有兩種方法:
(1)在外部使用stopService()。
(2)在服務(wù)內(nèi)部(onStartCommand方法內(nèi)部)使用stopSelf()方法。

3、onStartCommand方法的返回值:
onStartCommand方法執(zhí)行時,返回的是一個int型。這個整型可以有三個返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT。

(1)START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執(zhí)行完onStartCommand方法后,服務(wù)被異常kill掉,系統(tǒng)不會自動重啟該服務(wù)。

(2)START_STICKY:如果Service進(jìn)程被kill掉,保留Service的狀態(tài)為開始狀態(tài),但不保留遞送的intent對象。隨后系統(tǒng)會嘗試重新創(chuàng)建Service,由于服務(wù)狀態(tài)為開始狀態(tài),所以創(chuàng)建服務(wù)后一定會調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到Service,那么參數(shù)Intent將為null。

(3)START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,系統(tǒng)會自動重啟該服務(wù),并將Intent的值傳入。

四、綁定Service

1、bindService介紹:

應(yīng)用程序組件(客戶端)通過調(diào)用bindService()方法能夠綁定服務(wù),然后Android系統(tǒng)會調(diào)用服務(wù)的onBind()回調(diào)方法,則個方法會返回一個跟服務(wù)器端交互的Binder對象。

這個綁定是異步的,bindService()方法立即返回,并且不給客戶端返回IBinder對象。要接收IBinder對象,客戶端必須創(chuàng)建一個ServiceConnection類的實例,并且把這個實例傳遞給bindService()方法。ServiceConnection對象包含了一個系統(tǒng)調(diào)用的傳遞IBinder對象的回調(diào)方法。

注意:只有Activity、Service、Content Provider能夠綁定服務(wù);BroadcastReceiver廣播接收器不能綁定服務(wù)。

2、bindService()、unbindService()

public class MyService extends Service {  
      
    public static final String TAG = "MyService";  
  
    private MyBinder mBinder = new MyBinder();  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate");  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand");  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy");  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  //在這里返回新建的MyBinder類
    }  
  
    //MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度
    class MyBinder extends Binder {  
  
        public void startDownload() {  
            Log.d("TAG", "startDownload() executed");  
            // 執(zhí)行具體的下載任務(wù)  
        }

        public int getProgress(){
            Log.d("TAG", "getProgress() executed");  
            return 0;
        }  
    }    
}  
  • 新建一個MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度。當(dāng)然,這里只是兩個模擬方法,并沒有實現(xiàn)真正的功能,我們通過打印日志的形式來體現(xiàn)。

  • 接著創(chuàng)建MyBinder的實例,然后在onBind()方法里返回這個實例。這樣綁定服務(wù)的組件通過獲取這個IBinder類型的實例,從而調(diào)用Service里面的方法。

private MyService.MyBinder myBinder;

//匿名內(nèi)部類:服務(wù)連接對象
private ServiceConnection connection = new ServiceConnection() {
    
    //當(dāng)服務(wù)異常終止時會調(diào)用。注意,解除綁定服務(wù)時不會調(diào)用
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
    
    //和服務(wù)綁定成功后,服務(wù)會回調(diào)該方法
    @Override
    public void onServiceConnected(ComponentName name, IBinder ) {
        myBinder = (MyService.MyBinder) service;
        //在Activity中調(diào)用Service里面的方法
        myBinder.startDownload();
        myBinder.getProgress();
    }
};

Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button3_bind_service:
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, connection, BIND_AUTO_CREATE);
        break;
    case R.id.button4_unbind_service:
        unbindService(connection);
        break;
       default:
        break;
    }
}
  • 首先創(chuàng)建了一個ServiceConnection的匿名類,在里面重寫了onServiceConnected()方法和onServiceDisconnected()方法,如果當(dāng)前Activity與服務(wù)連接成功后,服務(wù)會回調(diào)onServiceConnected()方法。

  • 在onServiceConnected()方法中,我們又通過向下轉(zhuǎn)型得到了MyBinder的實例,有了這個實例,Activity和Service之間的關(guān)系就變得非常緊密了。現(xiàn)在我們可以在Activity中根據(jù)具體的場景來調(diào)用MyBinder中的任何public方法,即實現(xiàn)了Activity調(diào)用Service中的功能。

  • Activity和Service真正進(jìn)行關(guān)聯(lián),是在Bind Service按鈕的點擊事件里完成的。這里需要構(gòu)建一個Intent對象,然后調(diào)用bindService()方法將Activity和Service進(jìn)行綁定。bindService()方法接收三個參數(shù),第一個參數(shù)就是剛剛構(gòu)建出的Intent對象,第二個參數(shù)是前面創(chuàng)建出的ServiceConnection的實例,第三個參數(shù)是一個標(biāo)志位,這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后會自動創(chuàng)建Service(即使之前沒有創(chuàng)建Service也沒有關(guān)系),這會使得MyService中的onCreate()方法得到執(zhí)行,但onStartCommand()方法不會執(zhí)行。

  • 如果想解除Activity和Service之間的關(guān)聯(lián),調(diào)用一下unbindService()方法就可以了,這也是Unbind Service按鈕的點擊事件里實現(xiàn)的邏輯。


    image.png

運行一下程序,點擊一下Bind Service按鈕。可以看到,只點擊了Bind Service按鈕,但是oncreate()方法得到了執(zhí)行,而onStartCommand()方法不會執(zhí)行。

注意:任何一個Service在整個應(yīng)用程序范圍內(nèi)都是通用的,即MyService不僅可以和MainActivity建立關(guān)聯(lián),還可以和任何一個Activity建立關(guān)聯(lián),而且在建立關(guān)聯(lián)時它們都可以獲取到相同的MyBinder實例。

五、銷毀Service

1、關(guān)于銷毀
根據(jù)上面的知識,我們介紹了銷毀Service的第一種情況:現(xiàn)在卸載程序,重新運行程序,點擊Start Service按鈕啟動Service,再點擊Stop Service按鈕停止Service,這樣MyService就被銷毀了:

image

卸載程序,重新開始。那么如果我們只點擊的Bind Service按鈕呢?由于在綁定Service的時候指定的標(biāo)志位是BIND_AUTO_CREATE,說明點擊Bind Service按鈕的時候Service也會被創(chuàng)建,這時應(yīng)該怎么銷毀Service呢?其實也很簡單,點擊一下Unbind Service按鈕,將Activity和Service的關(guān)聯(lián)解除就可以了:

image

以上這兩種銷毀的方式都很好理解。那么如果我們既點擊了Start Service按鈕,又點擊了Bind Service按鈕會怎么樣呢?這個時候你會發(fā)現(xiàn),不管你是單獨點擊Stop Service按鈕還是Unbind Service按鈕,Service都不會被銷毀,必要將Unbind Service按鈕和Stop Service按鈕都點擊一下(沒有先后順序),Service才會被銷毀。也就是說,點擊Stop Service按鈕只會讓Service停止,點擊Unbind Service按鈕只會讓Service和Activity解除關(guān)聯(lián),一個Service必須要在既沒有和任何Activity關(guān)聯(lián)又處理停止?fàn)顟B(tài)的時候才會被銷毀。

2、Unbind銷毀的問題
點擊Unbind Service按鈕后,再次點擊Unbind Service按鈕按鈕引發(fā)的問題:

假設(shè)現(xiàn)在Service和Activity已經(jīng)相關(guān)聯(lián)了,點擊Unbind Service按鈕能夠解除綁定,如果繼續(xù)點擊Unbind Service按鈕,程序會異常退出,這說明代碼不夠完善,我們需要在代碼中加一個判斷是否綁定的標(biāo)記mBound。優(yōu)化后的代碼如下:

boolean mBound = false; //一開始,并沒有和Service綁定.這個參數(shù)是用來顯示綁定狀態(tài)

//匿名內(nèi)部類:服務(wù)連接對象
private ServiceConnection connection = new ServiceConnection() {
    
    //當(dāng)服務(wù)異常終止時會調(diào)用。注意,解除綁定服務(wù)時不會調(diào)用
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBound = false; //服務(wù)異常終止時,狀態(tài)為未綁定
    }
    
    //和服務(wù)綁定成功后,服務(wù)會回調(diào)該方法
    @Override
    public void onServiceConnected(ComponentName name, IBinder ) {
        myBinder = (MyService.MyBinder) service;
        //在Activity中調(diào)用Service里面的方法
        myBinder.startDownload();
        myBinder.getProgress();
        mBound = true; //true說明是綁定狀態(tài)
    }
};

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button3_bind_service:
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, connection, BIND_AUTO_CREATE);
        break;
    case R.id.button4_unbind_service:
        //如果和Service是綁定的狀態(tài),就解除綁定。
        if(mBound){
            unbindService(connection);
            mBound=false;
        }
        break;
        default:
        break;
    }
}
六、started服務(wù)與bind服務(wù)總結(jié)

區(qū)別一:生命周期

  • 通過started方式的服務(wù)會一直運行在后臺,需要由組件本身或外部組件來停止服務(wù)才會以結(jié)束運行。
  • bind方式的服務(wù),生命周期就要依賴綁定的組件。

區(qū)別二:參數(shù)傳遞

  • started服務(wù)可以給啟動的服務(wù)對象傳遞參數(shù),但無法獲取服務(wù)中方法的返回值。
  • bind服務(wù)可以給啟動的服務(wù)對象傳遞參數(shù),也可以通過綁定的業(yè)務(wù)對象獲取返回結(jié)果。

實際開發(fā)中的技巧;

  • 第一次先使用started方式來啟動一個服務(wù)。
  • 之后可以使用bind的方式綁定服務(wù),從而可以直接調(diào)用業(yè)務(wù)方法獲取返回值。

生命周期

image.png

一旦在項目的任何位置調(diào)用了Context的startService()方法,相應(yīng)的服務(wù)就會啟動起來,并回調(diào)onstartCommand()方法。如果這個服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會先于onstartCommand()方法執(zhí)行。服務(wù)啟動過后,會一直保持運行狀態(tài),直到stopService()或stopself()方法被調(diào)用。注意雖然每次調(diào)用一次startService()方法,onstartCommand()方法就會以執(zhí)行一次,但實際上每個服務(wù)都只會存在一個實例。所以不管你調(diào)用了多少次startService()方法,只需調(diào)用一次stopService()或stopself()方法,服務(wù)就會停止。

另外,還可以調(diào)用Context的bindService()來獲取一個服務(wù)的持久連接,這時就會回調(diào)服務(wù)中的onBind()方法。類似地,如果這個服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會先于onBind()方法執(zhí)行。之后調(diào)用方可以獲取到onBind()方法里返回的IBinder對象的實例,這樣,就能自由地和服務(wù)進(jìn)行通信了。只要調(diào)用方和服務(wù)之間的連接沒有斷開,服務(wù)就會一直保持運行狀態(tài)。

android 5.0 之后隱式啟動service

參考文章:
1、Android組件系列----Android Service組件深入解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容