欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android bindService的使用與Service生命周期案例詳解

 更新時(shí)間:2021年09月08日 09:12:03   作者:oudetu  
這篇文章主要介紹了Android bindService的使用與Service生命周期案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

Android中有兩種主要方式使用Service,通過(guò)調(diào)用Context的startService方法或調(diào)用Context的bindService方法,本文只探討純bindService的使用,不涉及任何startService方法調(diào)用的情況。如果想了解startService相關(guān)的使用,請(qǐng)參見(jiàn)《Android中startService的使用及Service生命周期》。

bindService啟動(dòng)服務(wù)的特點(diǎn)

相比于用startService啟動(dòng)的Service,bindService啟動(dòng)的服務(wù)具有如下特點(diǎn):
1. bindService啟動(dòng)的服務(wù)在調(diào)用者和服務(wù)之間是典型的client-server的接口,即調(diào)用者是客戶端,service是服務(wù)端,service就一個(gè),但是連接綁定到service上面的客戶端client可以是一個(gè)或多個(gè)。這里特別要說(shuō)明的是,這里所提到的client指的是組件,比如某個(gè)Activity。
2. 客戶端client(即調(diào)用bindService的一方,比如某個(gè)Activity)可以通過(guò)IBinder接口獲取Service的實(shí)例,從而可以實(shí)現(xiàn)在client端直接調(diào)用Service中的方法以實(shí)現(xiàn)靈活的交互,并且可借助IBinder實(shí)現(xiàn)跨進(jìn)程的client-server的交互,這在純startService啟動(dòng)的Service中是無(wú)法實(shí)現(xiàn)的。
3. 不同于startService啟動(dòng)的服務(wù)默認(rèn)無(wú)限期執(zhí)行(可以通過(guò)Context的stopService或Service的stopSelf方法停止運(yùn)行),bindService啟動(dòng)的服務(wù)的生命周期與其綁定的client息息相關(guān)。當(dāng)client銷毀的時(shí)候,client會(huì)自動(dòng)與Service解除綁定,當(dāng)然client也可以通過(guò)明確調(diào)用Context的unbindService方法與Service解除綁定。當(dāng)沒(méi)有任何client與Service綁定的時(shí)候,Service會(huì)自行銷毀(通過(guò)startService啟動(dòng)的除外)。
4. startService和bindService二者執(zhí)行的回調(diào)方法不同:startService啟動(dòng)的服務(wù)會(huì)涉及Service的的onStartCommand回調(diào)方法,而通過(guò)bindService啟動(dòng)的服務(wù)會(huì)涉及Service的onBind、onUnbind等回調(diào)方法。

bindService代碼示例

使用bindService主要分兩種情形:
1. Service的調(diào)用者client與Service在同一個(gè)App中;
2. Service的調(diào)用者client是App1中的一個(gè)Activity,而Service是App2中的Service,client與service分屬兩個(gè)App,這種情形下主要用于實(shí)現(xiàn)跨進(jìn)程的通信。

為了簡(jiǎn)單起見(jiàn),本文只討論第一種情形,即Service的調(diào)用者client與Service在同一個(gè)App中,該情形也是我們?cè)趯?shí)際開(kāi)發(fā)中用到最多的情形。如果想了解通過(guò)bindService在兩個(gè)不同的進(jìn)程中讓客戶端與Service通信,可參見(jiàn)另一篇博文《Android中通過(guò)Messenger與Service實(shí)現(xiàn)進(jìn)程間雙向通信》。

下面我們通過(guò)一個(gè)例子演示一下第一種情形下bindService的基本使用流程。

首先我們有一個(gè)TestService,該類繼承自Service,其是client-server接口中的server端。我們?cè)谄渲饕纳芷诨卣{(diào)方法中都加入了輸出語(yǔ)句。TestService代碼如下:

package com.ispring.startservicedemo;

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

import java.util.Random;

public class TestService extends Service {

    public class MyBinder extends Binder{

        public TestService getService(){
            return TestService.this;
        }

    }

    //通過(guò)binder實(shí)現(xiàn)調(diào)用者client與Service之間的通信
    private MyBinder binder = new MyBinder();

    private final Random generator = new Random();

    @Override
    public void onCreate() {
        Log.i("DemoLog","TestService -> onCreate, Thread: " + Thread.currentThread().getName());
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread: " + Thread.currentThread().getName());
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("DemoLog", "TestService -> onBind, Thread: " + Thread.currentThread().getName());
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("DemoLog", "TestService -> onUnbind, from:" + intent.getStringExtra("from"));
        return false;
    }

    @Override
    public void onDestroy() {
        Log.i("DemoLog", "TestService -> onDestroy, Thread: " + Thread.currentThread().getName());
        super.onDestroy();
    }

    //getRandomNumber是Service暴露出去供client調(diào)用的公共方法
    public int getRandomNumber(){
        return generator.nextInt();
    }
}

在該App中,除了TestService,還有兩個(gè)Activity: ActivityA和ActivityB,它們都是Service的調(diào)用者,即client-server接口中的client。

ActivityA是App的啟動(dòng)界面,界面如下:

這里寫圖片描述

ActivityA的代碼如下:

package com.ispring.startservicedemo;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;


public class ActivityA extends Activity implements Button.OnClickListener {

    private TestService service = null;

    private boolean isBound = false;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound = true;
            TestService.MyBinder myBinder = (TestService.MyBinder)binder;
            service = myBinder.getService();
            Log.i("DemoLog", "ActivityA onServiceConnected");
            int num = service.getRandomNumber();
            Log.i("DemoLog", "ActivityA 中調(diào)用 TestService的getRandomNumber方法, 結(jié)果: " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            Log.i("DemoLog", "ActivityA onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        Log.i("DemoLog", "ActivityA -> onCreate, Thread: " + Thread.currentThread().getName());
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btnBindService){
            //單擊了“bindService”按鈕
            Intent intent = new Intent(this, TestService.class);
            intent.putExtra("from", "ActivityA");
            Log.i("DemoLog", "-------------------------------------------------------------");
            Log.i("DemoLog", "ActivityA 執(zhí)行 bindService");
            bindService(intent, conn, BIND_AUTO_CREATE);
        }else if(v.getId() == R.id.btnUnbindService){
            //單擊了“unbindService”按鈕
            if(isBound){
                Log.i("DemoLog", "-------------------------------------------------------------");
                Log.i("DemoLog", "ActivityA 執(zhí)行 unbindService");
                unbindService(conn);
            }
        }else if(v.getId() == R.id.btnStartActivityB){
            //單擊了“start ActivityB”按鈕
            Intent intent = new Intent(this, ActivityB.class);
            Log.i("DemoLog", "-------------------------------------------------------------");
            Log.i("DemoLog", "ActivityA 啟動(dòng) ActivityB");
            startActivity(intent);
        }else if(v.getId() == R.id.btnFinish){
            //單擊了“Finish”按鈕
            Log.i("DemoLog", "-------------------------------------------------------------");
            Log.i("DemoLog", "ActivityA 執(zhí)行 finish");
            this.finish();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("DemoLog", "ActivityA -> onDestroy");
    }
}

通過(guò)單擊ActivityA上的“start ActivityB”可以啟動(dòng)ActivityB,ActivityB的界面如下:

這里寫圖片描述

ActivityB的代碼如下:

package com.ispring.startservicedemo;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;


public class ActivityB extends Activity implements Button.OnClickListener {

    private TestService service = null;

    private boolean isBound = false;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound = true;
            TestService.MyBinder myBinder = (TestService.MyBinder)binder;
            service = myBinder.getService();
            Log.i("DemoLog", "ActivityB onServiceConnected");
            int num = service.getRandomNumber();
            Log.i("DemoLog", "ActivityB 中調(diào)用 TestService的getRandomNumber方法, 結(jié)果: " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            Log.i("DemoLog", "ActivityB onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btnBindService){
            Intent intent = new Intent(this, TestService.class);
            intent.putExtra("from", "ActivityB");
            Log.i("DemoLog", "----------------------------------------------------------------------");
            Log.i("DemoLog", "ActivityB 執(zhí)行 bindService");
            bindService(intent, conn, BIND_AUTO_CREATE);
        }else if(v.getId() == R.id.btnUnbindService){
            if(isBound){
                Log.i("DemoLog", "----------------------------------------------------------------------");
                Log.i("DemoLog", "ActivityB 執(zhí)行 unbindService");
                unbindService(conn);
            }
        }else if(v.getId() == R.id.btnFinish){
            //單擊了“Finish”按鈕
            Log.i("DemoLog", "----------------------------------------------------------------------");
            Log.i("DemoLog", "ActivityB 執(zhí)行 finish");
            this.finish();
        }
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        Log.i("DemoLog", "ActivityB -> onDestroy");
    }
}

我們暫時(shí)不點(diǎn)擊上面的按鈕,先看一下TestService和ActivityA的代碼。

調(diào)用者(客戶端client)要想和Service進(jìn)行交互,那么Service和調(diào)用者必須都要做好準(zhǔn)備。

我們先看Service要做的工作。
使用bindService將client與server聯(lián)系在一起的關(guān)鍵是binder,在TestService中,我們?cè)谄渲袑懥艘粋€(gè)內(nèi)部類MyBinder,該類有個(gè)公共方法getService,通過(guò)該方法我們可以獲取包含MyBinder的TestService。如果想要自己的Service支持bindService啟動(dòng)方式,就必須在Service的onBind中返回一個(gè)IBinder類型的實(shí)例。在示例中,我們實(shí)例化了一個(gè)MyBinder的實(shí)例binder作為TestService的字段,并且將其作為onBind的返回值。
我們總結(jié)一下如果想讓Service支持bindService調(diào)用方式,Service需要做以下事情:
1. 在Service的onBind方法中返回IBinder類型的實(shí)例。
2. onBind方法返回的IBinder的實(shí)例需要能夠返回Service實(shí)例本身或者通過(guò)binder暴露出Service公共方法。通常情況下,最簡(jiǎn)單明了的做法就是將binder弄成Service的內(nèi)部類,然后在binder中加入類似于getService之類的方法返回包含binder的Service,這樣client可以通過(guò)該方法得到Service實(shí)例。

我們已經(jīng)知道了Service需要做的事情,我們接下來(lái)看一下調(diào)用者需要做的工作。
在我們的示例中,調(diào)用者(也就是客戶端client)是ActivityA,我們?cè)谄渲谐跏蓟艘粋€(gè)ServiceConnection類型的實(shí)例,需要重寫其onServiceConnected方法以及onServiceDisconnected方法。我們需要將這個(gè)ServiceConnection類型的實(shí)例作為參數(shù)傳給bindService方法,當(dāng)Service還沒(méi)有創(chuàng)建的時(shí)候,Android會(huì)先創(chuàng)建Service的實(shí)例,然后執(zhí)行Service的onBind方法,得到IBinder類型的實(shí)例,將該方法作為參數(shù)傳入client端的ServiceConnection的onServiceConnected方法中,onServiceConnected方法的執(zhí)行表明client端可以獲取到Service的IBinder類型的實(shí)例,然后將IBinder轉(zhuǎn)換為自己實(shí)際的Binder類型,然后可以通過(guò)其直接獲取Service的實(shí)例或者通過(guò)Binder直接執(zhí)行公共方法,這取決于Service中Binder的具體實(shí)現(xiàn)。在本例中,在onServiceConnected方法中,調(diào)用者ActivityA通過(guò)binder的getService方法獲取到了與其對(duì)應(yīng)的Service,然后我們就可以直接調(diào)用Service的公共方法以達(dá)到使用Service的目的,這樣client與Service之間就通過(guò)IBinder建立了連接,從而進(jìn)行交互。當(dāng)client與Service失去連接時(shí)會(huì)觸發(fā)onServiceDisconnected方法。
我們總結(jié)一下client端要做的事情:
1. 創(chuàng)建ServiceConnection類型的實(shí)例,并重寫其onServiceConnected方法和onServiceDisconnected方法。
2. 當(dāng)Android執(zhí)行onServiceConnected回調(diào)方法時(shí),我們可以通過(guò)IBinder實(shí)例得到Service的實(shí)例對(duì)象或直接調(diào)用binder的公共方法,這樣就實(shí)現(xiàn)了client與Service的連接。
3. 當(dāng)Android執(zhí)行onServiceDisconnected回調(diào)方法時(shí),表示client與Service之間斷開(kāi)了連接,我們?cè)诖颂幰獙懸恍嚅_(kāi)連接后需要做的處理。

在知道了如何讓client與Service進(jìn)行交互之后,我們運(yùn)行我們的App,觀察各個(gè)回調(diào)方法的執(zhí)行過(guò)程,我們有三個(gè)測(cè)試流程。

測(cè)試流程A

該測(cè)試涉及到ActivityA,但不涉及ActivityB.
首先我們點(diǎn)擊ActivityA中的“bindService”按鈕,然后點(diǎn)擊”unbindService”按鈕,輸出結(jié)果如下所示:

這里寫圖片描述

首先,通過(guò)上面的代碼我們可以看到Service中執(zhí)行的回調(diào)方法都是執(zhí)行在主線程中的。
當(dāng)我們調(diào)用bindService方法時(shí),我們需要將Intent、ServiceConnection等實(shí)例傳入,Intent包含了我們要綁定的Service,ServiceConnection我們?cè)谏厦嫣岬竭^(guò),實(shí)現(xiàn)了其onServiceConnected方法和onServiceDisconnected方法。 在調(diào)用了bindService之后,由于Service此時(shí)還不存在,那么Android就會(huì)首先創(chuàng)建一個(gè)TestService的實(shí)例,并執(zhí)行其onCreate回調(diào)方法,onCreate方法在其生命周期中只會(huì)被調(diào)用一次。然后會(huì)調(diào)用Service的onBind方法,該方法只有在第一次bindService調(diào)用后才會(huì)執(zhí)行,onBind執(zhí)行后會(huì)返回一個(gè)IBinder類型的實(shí)例,此時(shí)Android會(huì)將該IBinder實(shí)例存起來(lái),這個(gè)IBinder實(shí)例是對(duì)所有client共享的。當(dāng)下次其他的client執(zhí)行bindService的時(shí)候,不會(huì)再執(zhí)行onBind方法,因?yàn)槲覀冎耙呀?jīng)得到了一個(gè)IBinder實(shí)例,Android會(huì)直接使用這個(gè)IBinder實(shí)例。 在得到了IBinder實(shí)例之后,Android會(huì)執(zhí)行client端ServiceConnection中的onServiceConnected方法,在該方法中我們會(huì)得到IBinder實(shí)例,并通過(guò)該IBinder實(shí)例得到了TestService實(shí)例,這樣我們的客戶端ActivityA就通過(guò)IBinder與TestService建立了連接,我們就可以調(diào)用TestService的公共方法,比如調(diào)用其getRandomNumber方法獲得隨機(jī)數(shù)。

總結(jié)一下調(diào)用bindService之后發(fā)生的事情:
client 執(zhí)行 bindService ->
如果Service不存在,Service 執(zhí)行 onCreate ->
如果沒(méi)有執(zhí)行過(guò)onBind,Service 執(zhí)行 onBind ->
client的實(shí)例ServiceConnection 執(zhí)行 onServiceConnected

在執(zhí)行了bindService之后,一共有一個(gè)client連接到了TestService,即ActivityA,每次client在調(diào)用了unbindService方法之后,該client會(huì)與Service解除綁定,在與某個(gè)client解除綁定之后,Service會(huì)檢測(cè)是否還有其他的client與其連接綁定,如果沒(méi)有其他任何client與其處于連接狀態(tài),那么Service會(huì)執(zhí)行onUnbind方法,然后執(zhí)行onDestroy方法,最終銷毀自己。當(dāng)ActivityA執(zhí)行unbindService的時(shí)候,唯一的一個(gè)client與TestService解除了綁定的關(guān)系,TestService就執(zhí)行了onUnbind方法,進(jìn)而執(zhí)行onDestroy方法。

總結(jié)一下調(diào)用unbindService之后發(fā)生的事情:
client 執(zhí)行 unbindService ->
client 與 Service 解除綁定連接狀態(tài) ->
Service 檢測(cè)是否還有其他client與其連接,如果沒(méi)有 ->
Service 執(zhí)行onUnbind ->
Service 執(zhí)行onDestroy

測(cè)試流程B

我們?cè)跍y(cè)試完第一種流程后,關(guān)掉App,重啟App,進(jìn)行第二種測(cè)試流程。
該測(cè)試也只涉及ActivityA,不涉及ActivityB。首先先點(diǎn)擊ActivityA中的“bindService”按鈕,然后點(diǎn)擊”Finish”按鈕,輸出結(jié)果如下圖所示:

這里寫圖片描述

在該測(cè)試中,我們首先通過(guò)點(diǎn)擊”bindService”按鈕,使得ActivityA綁定了TestService,但是我們沒(méi)有調(diào)用unbindService,而是直接通過(guò)調(diào)用“Finish”按鈕讓ActivityA直接銷毀,通過(guò)上面的輸出結(jié)果我們可以看到,在ActivityA銷毀的時(shí)候,執(zhí)行了ActivityA的onDestroy回調(diào)方法,之后TestService依次執(zhí)行了onUnbind、onDestroy回調(diào)方法,TestService銷毀。client與Service通過(guò)bindService連接起來(lái)之后,如果client銷毀,那么client會(huì)自動(dòng)與Service解除綁定,相當(dāng)于在destroy之前會(huì)執(zhí)行unbindService,在ActivityA銷毀之后,ActivityA與Service解除了綁定,此時(shí)再?zèng)]有client與Service處于連接綁定狀態(tài),這樣Service就會(huì)執(zhí)行onUnbind回調(diào)方法,表示沒(méi)有client和我玩了,最后執(zhí)行onDestroy回調(diào)方法。

測(cè)試流程C

我們?cè)谥暗膬纱螠y(cè)試流程中都只涉及ActivtityA,本測(cè)試流程會(huì)同時(shí)涉及ActivityA以及ActivityB。
首先關(guān)掉App,重啟App,按照以下步驟測(cè)試:
1. 點(diǎn)擊ActivityA中的”bindService”按鈕
2. 點(diǎn)擊ActivityA中的”start ActivityB”按鈕,界面切換到ActivityB
3. 點(diǎn)擊ActivityB中的”bindService”按鈕
4. 點(diǎn)擊ActivityB中的”unbindService”按鈕
5. 點(diǎn)擊ActivityB中的”Finish”按鈕
6. 點(diǎn)擊ActivityA中的”unbindService”按鈕

LogCat輸出結(jié)果如下:

這里寫圖片描述

下面我們依次分析每一步產(chǎn)生的影響,以便于完整地理解通過(guò)bindService啟動(dòng)的Service的生命周期:

  • 點(diǎn)擊ActivityA中的”bindService”按鈕
    由于初始情況下TestService實(shí)例不存在,也就是TestService沒(méi)有運(yùn)行。第一次調(diào)用bindService會(huì)實(shí)例化TestService,然后會(huì)執(zhí)行其onBind方法,得到IBinder類型的實(shí)例,然后將其作為參數(shù)傳入ActivityA的ServiceConnection的onServiceConnected方法中,標(biāo)志著ActivityA與TestService建立了綁定連接,此時(shí)只有ActivityA這一個(gè)客戶端client與TestService綁定。
  • 點(diǎn)擊ActivityA中的”start ActivityB”按鈕,界面切換到ActivityB
  • 點(diǎn)擊ActivityB中的”bindService”按鈕
    由于TestService已經(jīng)處于運(yùn)行狀態(tài),所以ActivityB調(diào)用bindService時(shí),不會(huì)重新創(chuàng)建TestService的實(shí)例,所以也不會(huì)執(zhí)行TestService的onCreate回調(diào)方法,由于在ActivityA執(zhí)行bindService的時(shí)候就已經(jīng)執(zhí)行了TestService的onBind回調(diào)方法而獲取IBinder實(shí)例,并且該IBinder實(shí)例在所有的client之間是共享的,所以當(dāng)ActivityB執(zhí)行bindService的時(shí)候,不會(huì)執(zhí)行其onBind回調(diào)方法,而是直接獲取上次已經(jīng)獲取到的IBinder實(shí)例。并將其作為參數(shù)傳入ActivityB的ServiceConnection的onServiceConnected方法中,標(biāo)志著ActivityB與TestService建立了綁定連接,此時(shí)有兩個(gè)客戶單client(ActivityA和ActivityB)與TestService綁定。
  • 點(diǎn)擊ActivityB中的”unbindService”按鈕
    ActivityB執(zhí)行了unbindService之后,ActivityB就與TestService解除了綁定。當(dāng)沒(méi)有任何client與Service處于綁定連接狀態(tài)的時(shí)候,TestService才會(huì)執(zhí)行onUnbind方法、onDestroy方法。但是由于此時(shí)還有ActivityA這個(gè)client與TestService處于綁定連接中,所以不會(huì)執(zhí)行Service的onBind及onDestroy回調(diào)方法。
  • 點(diǎn)擊ActivityB中的”Finish”按鈕
    執(zhí)行了ActivityB的finish方法后,ActivityB銷毀了,界面返回到ActivityA
  • 點(diǎn)擊ActivityA中的”unbindService”按鈕
    ActivityA執(zhí)行unbindService之后,ActivityA與TestService就解除綁定了,這樣就沒(méi)有客戶端client與TestService相連,這時(shí)候Android會(huì)銷毀TestService,在銷毀前會(huì)先執(zhí)行TestService的onUnbind方法,然后才會(huì)執(zhí)行其onDestroy方法,這樣TestService就銷毀了。

bindService生命周期流程圖

這里特別要說(shuō)明的是,本文所提到的client指的是組件Component,比如某個(gè)Activity。如果在某一個(gè)Activity中,多次調(diào)用bindService方法連接Service,那么對(duì)于Service來(lái)說(shuō),這個(gè)Activity也只是一個(gè)client,而不是多個(gè)client。

最后我們將bindService啟動(dòng)的Service的生命周期總結(jié)為如下的流程圖:

這里寫圖片描述

希望本文對(duì)大家了解bindService的使用有所幫助。

到此這篇關(guān)于Android bindService的使用與Service生命周期案例詳解的文章就介紹到這了,更多相關(guān)Android bindService的使用與Service生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論