深入理解Android的startservice和bindservice

深入理解Android的startservice和bindservice

一、首先,讓我們確認下什麼是service? 
        service就是android系統中的服務,它有這麼幾個特點:它無法與用戶直接進行交互、它必須由用戶或者其他程序顯式的啓動、它的優先級比較高,它比處於前臺的應用優先級低,但是比後臺的其他應用優先級高,這就決定了當系統因爲缺少內存而銷燬某些沒被利用的資源時,它被銷燬的概率很小哦。 
二、那麼,什麼時候,我們需要使用service呢? 
        我們知道,service是運行在後臺的應用,對於用戶來說失去了被關注的焦點。這就跟我們打開了音樂播放之後,便想去看看圖片,這時候我們還不想音樂停止,這裏就會用到service;又例如,我們打開了一個下載鏈接之後,我們肯定不想瞪着眼睛等他下載完再去做別的事情,對吧?這時候如果我們想手機一邊在後臺下載,一邊可以讓我去看看新聞啥的,就要用到service。 
三、service分類: 
       一般我們認爲service分爲兩類,本地service遠程service。 
       本地service顧名思義,那就是和當前應用在同一個進程中的service,彼此之間擁有共同的內存區域,所以對於某些數據的共享特別的方便和簡單; 
       遠程service:主要牽扯到不同進程間的service訪問。因爲android的系統安全的原因導致了我們在不同的進程間無法使用一般的方式共享數據。在這裏android爲我們提供了一個AIDL工具。(android interface description language)android接口描述語言。在後邊我們將會對其進行詳細的介紹。 
四、service生命週期: 
        和Activity相比,service的生命週期已經簡單的不能再簡單了,只有onCreate()->onStart()->onDestroy()三個方法。 
       Activity中和service有關的方法: 
       startService(Intent intent):啓動一個service 
       stopService(Intent intent) :停止一個service 
        如果我們想使用service中的一些數據或者訪問其中的一些方法,那麼我們就要通過下面的方法: 
        public boolean bindService(Intent intent, ServiceConnection conn, int flags) ; 
        public void unbindService(ServiceConnection conn); 
        intent是跳轉到service的intent,如 Intent intent = new Intent(); intent.setClass(this,MyService.class); 
        conn則是一個代表與service連接狀態的類,當我們連接service成功或失敗時,會主動觸發其內部的onServiceConnectedonServiceDisconnected方法。如果我們想要訪問service中的數據,可以在onServiceConnected()方法中進行實現,

 

使用service的步驟: 
        第一步:我們要繼承service類,實現自己的service。 
        如果想要訪問service中的某些值,我們通常會提供一個繼承了Binder的內部類,通過onBund()方法返回給service請求。這裏實際上巧妙的利用了內部類能夠訪問外部類屬性的特點。 
第二步:在androidManifest.xml中進行註冊,如: 
        <!-- service配置開始 --> 
        <service android:name="MyService"></service> 
        <!-- service配置結束--> 
第三步:在activity中進行啓動、綁定、解綁或者停止service。 
        (很多書上說,service與用戶是不能交互的,其實這話很不正確,我們完全可以通過activity與service進行交互!我認爲,確切的說法應該是service與用戶不能進行直接的交互)。

-----------------------------

bindService介紹

一、bindService簡介

bindService是綁定Service服務,執行service服務中的邏輯流程。

service通過Context.startService()方法開始,通過Context.stopService()方法停止;也可以通過Service.stopSelf()方法或者Service.stopSelfResult()方法來停止自己。只要調用一次stopService()方法便可以停止服務,無論之前它被調用了多少次的啓動服務方法。

客戶端建立一個與Service的連接,並使用此連接與Service進行通話,通過Context.bindService()方法來綁定服務,Context.unbindService()方法來關閉服務。多個客戶端可以綁定同一個服務,如果Service還未被啓動,bindService()方法可以啓動服務。

上面startService()和bindService()兩種模式是完全獨立的。你可以綁定一個已經通過startService()方法啓動的服務。例如:一個後臺播放音樂服務可以通過startService(intend)對象來播放音樂。可能用戶在播放過程中要執行一些操作比如獲取歌曲的一些信息,此時activity可以通過調用bindServices()方法與Service建立連接。這種情況下,stopServices()方法實際上不會停止服務,直到最後一次綁定關閉。

如果沒有程序停止它或者它自己停止,service將一直運行。在這種模式下,service開始於調用Context.startService() ,停止於Context.stopService(). service可以通過調用Android Service 生命週期() 或 Service.stopSelfResult()停止自己。不管調用多少次startService() ,只需要調用一次 stopService() 就可以停止service。

可以通過接口被外部程序調用。外部程序建立到service的連接,通過連接來操作service。建立連接調開始於Context.bindService(), 結束於Context.unbindService(). 多個客戶端可以綁定到同一個service,如果service沒有啓動, bindService() 可以選擇啓動它。

這2種模式不是完全分離的。你可以可以綁定到一個通過startService()啓動的服務。如一個intent想要播放音樂,通過startService() 方法啓動後臺播放音樂的service。然後,也許用戶想要操作播放器或者獲取當前正在播放的樂曲的信息,一個activity就會通過bindService()建立一個到此service的連接. 這種情況下 stopService() 在全部的連接關閉後纔會真正停止service。

二、bindService啓動流程

context.bindService()  ——> onCreate()  ——> onBind()  ——> Service running  ——> onUnbind()  ——> onDestroy()  ——> Service stop

onBind()將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service的實例、運行狀態或其他操作。這個時候把調用者(Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調用onUnbind->onDestroy相應退出。

所以調用bindService的生命週期爲:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。

在Service每一次的開啓關閉過程中,只有onStart可被多次調用(通過多次startService調用),其他onCreate,onBind,onUnbind,onDestory在一個生命週期中只能被調用一次。

三、bindService生命週期

像一個activity那樣,一個service有些可以用來改變狀態的生命週期方法,但是比activity的方法少,service生命週期方法只有三個public

  void onCreate()

  void onStart(Intent intent)

  void onDestroy()

通過實現這三個生命週期方法,你可以監聽service的兩個嵌套循環的生命週期:

1、整個生命週期

service的整個生命週期是在onCreate()和onDestroy()方法之間。和activity一樣,在onCreate()方法裏初始化,在onDestroy()方法裏釋放資源。例如,一個背景音樂播放服務可以在onCreate()方法裏播放,在onDestroy()方法裏停止。

2、活動的生命週期

service的活動生命週期是在onStart()之後,這個方法會處理通過startServices()方法傳遞來的Intent對象。音樂service可以通過開打intent對象來找到要播放的音樂,然後開始後臺播放。注: service停止時沒有相應的回調方法,即沒有onStop()方法,只有onDestroy()銷燬方法。

onCreate()方法和onDestroy()方法是針對所有的services,無論它們是否啓動,通過Context.startService()和Context.bindService()方法都可以訪問執行。然而,只有通過startService()方法啓動service服務時纔會調用onStart()方法

 

如果一個service允許別人綁定,那麼需要實現以下額外的方法:

      IBinder onBind(Intent intent)

      boolean onUnbind(Intent intent)

      void onRebind(Intent intent)

onBind()回調方法會繼續傳遞通過bindService()傳遞來的intent對象

onUnbind()會處理傳遞給unbindService()的intent對象。如果service允許綁定,onBind()會返回客戶端與服務互相聯繫的通信句柄(實例)。

如果建立了一個新的客戶端與服務的連接,onUnbind()方法可以請求調用onRebind()方法。

記住: 任何服務無論它怎樣建立,默認客戶端都可以連接,所以任何service都能夠接收onBind()和onUnbind()方法

四、bindService和startservice示例

(1)mainactivity

複製代碼
public class MainActivity extends Activity {
    Button startServiceButton;// 啓動服務按鈕
    Button shutDownServiceButton;// 關閉服務按鈕
    Button startBindServiceButton;// 啓動綁定服務按鈕
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        getWidget();
        regiestListener();
    }

    /** 獲得組件 */
    public void getWidget() {
        startServiceButton = (Button) findViewById(R.id.startServerButton);
        startBindServiceButton = (Button) findViewById(R.id.startBindServerButton);
        shutDownServiceButton = (Button) findViewById(R.id.sutdownServerButton);
    }

    /** 爲按鈕添加監聽 */
    public void regiestListener() {
        startServiceButton.setOnClickListener(startService);
        shutDownServiceButton.setOnClickListener(shutdownService);
        startBindServiceButton.setOnClickListener(startBinderService);
    }
    
    
    /** 啓動服務的事件監聽 */
    public Button.OnClickListener startService = new Button.OnClickListener() {
        public void onClick(View view) {
            /** 單擊按鈕時啓動服務 */
            Intent intent = new Intent(MainActivity.this,
                    CountService.class);
            startService(intent);
            
            Log.v("MainStadyServics", "start Service");
        }
    };
    /** 關閉服務 */
    public Button.OnClickListener shutdownService = new Button.OnClickListener() {
        public void onClick(View view) {
            /** 單擊按鈕時啓動服務 */
            Intent intent = new Intent(MainActivity.this,
                    CountService.class);
            /** 退出Activity是,停止服務 */
            stopService(intent);
            Log.v("MainStadyServics", "shutDown serveice");
        }
    };
    /** 打開綁定服務的Activity */
    public Button.OnClickListener startBinderService = new Button.OnClickListener() {
        public void onClick(View view) {
            /** 單擊按鈕時啓動服務 */
            Intent intent = new Intent(MainActivity.this, UseBrider.class);
            startActivity(intent);
            Log.v("MainStadyServics", "start Binder Service");
        }
    };
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
複製代碼

(2)service

複製代碼
package com.example.testservice;

/**引入包*/
import android.app.Service;// 服務的類
import android.os.IBinder;
import android.os.Binder;
import android.content.Intent;
import android.util.Log;

/** 計數的服務 */
public class CountService extends Service {
    /** 創建參數 */
    boolean threadDisable;
    int count;

    public IBinder onBind(Intent intent) {
        return null;
    }

    public void onCreate() {
        super.onCreate();
        /** 創建一個線程,每秒計數器加一,並在控制檯進行Log輸出 */
        new Thread(new Runnable() {
            public void run() {
                while (!threadDisable) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {

                    }
                    count++;
                    Log.v("CountService", "Count is" + count);
                }
            }
        }).start();
    }

    public void onDestroy() {
        super.onDestroy();
        /** 服務停止時,終止計數進程 */
        this.threadDisable = true;
    }

    public int getConunt() {
        return count;
    }

//此方法是爲了可以在Acitity中獲得服務的實例   
class ServiceBinder extends Binder {
        public CountService getService() {
            return CountService.this;
        }
    }
}
複製代碼

(3)bindservice(一定要記着這個是要獲得,鏈接的對象)

複製代碼
package com.example.testservice;

/**引入包*/
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

/** 通過bindService和unBindSerivce的方式啓動和結束服務 */
public class UseBrider extends Activity {
    /** 參數設置 */
    CountService countService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new UseBriderFace(this));
        
        Intent intent = new Intent(UseBrider.this, CountService.class);
        /** 進入Activity開始服務 */
        bindService(intent, conn, Context.BIND_AUTO_CREATE);

    }

    
    private ServiceConnection conn = new ServiceConnection() {
        /** 獲取服務對象時的操作 */
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            countService = ((CountService.ServiceBinder) service).getService();

        }

        /** 無法獲取到服務對象時的操作 */
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            countService = null;
        }

    };

    protected void onDestroy() {
        super.onDestroy();
        this.unbindService(conn);
        Log.v("MainStadyServics", "out");
    }
}
複製代碼

注意:這個地方有朋友可能會出現onServiceConnected不調用的情況。

這個問題當調用bindService方法後就會回調Activity的onServiceConnected,在這個方法中會向Activity中傳遞一個IBinder的實例,Acitity需要保存這個實例

在Service中需要創建一個實現IBinder的內部類(這個內部類不一定在Service中實現,但必須在Service中創建它)。

在OnBind()方法中需返回一個IBinder實例,不然onServiceConnected方法不會調用。

不過,我在這裏傳遞null也能夠調用,大家根據情況進行判定吧,如果是返回一個ibinder實例的話,示例代碼如下:

複製代碼
public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        System.out.println("onBind.....");
         IBinder result = null;  
            if ( null == result ) result = new MyBinder() ;
        Toast.makeText(this, "onBind",Toast.LENGTH_LONG);
        return result;
    }
複製代碼

 

 

至於startservice和bindservice的使用場景,有網友這麼說:

1.通過startservice開啓的服務.一旦服務開啓, 這個服務和開啓他的調用者之間就沒有任何的關係了. 
調用者不可以訪問 service裏面的方法. 調用者如果被系統回收了或者調用了ondestroy方法, service還會繼續存在  
2.通過bindService開啓的服務,服務開啓之後,調用者和服務之間 還存在着聯繫 , 
一旦調用者掛掉了.service也會跟着掛掉 .

總結:onRebind()方法執行的時機是Service在內存中已經存在,然後使用bindService()方法再次與Service取得通信,這時onRebind()方法被調用。

 

1.先講講怎麼使用bindService()綁定服務

    應用組件(客戶端)可以調用bindService()綁定到一個service.Android系統之後調用service的onBind()方法,它返回一個用來與service交互的IBinder

  綁定是異步的.bindService()會立即返回,它不會返回IBinder給客戶端.要接收IBinder,客戶端必須創建一個ServiceConnection的實例並傳

給bindService().ServiceConnection包含一個回調方法,系統調用這個方法來傳遞要返回的IBinder.

注:只有activities,services,和contentproviders可以綁定到一個service—你不能從一個broadcastreceiver綁定到service.

所以,從你的客戶端綁定到一個service,你必須:

  • 1實現ServiceConnection.

    你的實現必須重寫兩個回調方法:

    • onServiceConnected()

      系統調用這個來傳送在service的onBind()中返回的IBinder.

    • OnServiceDisconnected()

      Android系統在同service的連接意外丟失時調用這個.比如當service崩潰了或被強殺了.當客戶端解除綁定時,這個方法不會被調用.

  • 2調用bindService(),傳給它ServiceConnection的實現.

  • 3當系統調用你的onServiceConnected()方法時,你就可以使用接口定義的方法們開始調用service了.

  • 4要與service斷開連接,調用unbindService().

      當你的客戶端被銷燬,它將從service解除綁定,但是你必須總是在你完成與service的交互時或當你的activity暫停於是service在不被使用時可以關閉此兩種情況下解

    除綁定.(下面會討論更多在適當的時候綁定和解除綁定的問題.

    使用這個ServiceConnection,客戶端可以綁定到一個service,通過把它傳給bindService().例如:

    Intentintent = new Intent(this, LocalService.class);

    bindService(intent,mConnection, Context.BIND_AUTO_CREATE);

  • 第一個bindService()的參數是一個明確指定了要綁定的service的Intent.

  • 第二個參數是ServiceConnection對象.

  • 第三個參數是一個標誌,它表明綁定中的操作.它一般應是BIND_AUTO_CREATE,這樣就會在service不存在時創建一個.其它可選的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,不想指定時設爲0即可.。

補充事項

下面是一些關於綁定到service的重要事項:

  • 你總是需要捕獲DeadObjectException異常.它會在連接被打斷時拋出.這是被遠程方法拋出的唯一異常.

  • 對象引用計數是跨進程的作用的.

  • 你應該在客戶端的生命期內使綁定和解除綁定配對進行,例如:

    • 如果你需要在你的activity可見時與service交互,你應該在onStart()綁定並在onStop()中解除綁定.

    • 如果你想讓你的activity即使在它停止時也能接收回應,那麼你可以在onCreate()中綁定並在onDestroy()中解除綁定.注意這意味着你的activity需要使用在自己整

      個運行期間使用service(即使位於後臺),所以如果service在另一個進程中,那麼你增加了這個進程的負擔而使它變得更容易被系統殺掉.

      注:你一般不應該在你的activity的onResume()和onPause()中綁定和解除綁定到service,因爲這些回調方法,出現在每個生命期變化中,並且你需要使發生在這

      些變化中的處理最小化.還有,如果你應用中的多個activity綁定到同一個service,並且有一個變化發生在其中兩個activity之間,service可能在當前activity解除綁

      定(pause中)和下一個綁定前(rusume中)被銷燬又重建.

2.管理(多個客戶端)BoundService的生命期

  當一個service的所有客戶端都解除綁定,Android系統就銷燬它(除非它是從onStartCommand()啓動).如果你的service是一個純boundservice,你不需管理它的生命

期—Android系統會爲你管理它.。

  然而,如果你選擇了實現onStartCommand()回調方法,那麼你必須明確地停止service,因爲service現在被認爲是"開始的".在此情況下,service會一直運行,直

到service使用stopSelf()停止它自己或另外的組件調用了stopService()停止了它,不管是否有客戶端綁定了它.

  另外,如果你的service已經啓動並且接受綁定,那麼當系統調用你的onUnbind()方法,你可以選擇返回true表示你想在客戶端下一次綁定到service時接受一個

對onRebind()的調用(而不是一個對onBind()的調用).onRebind()返回void,但是客戶端依然會在它的onServiceConnected()回調中接收到IBinder.下圖演示了這種生命其的

邏輯:


3。bindService和startService混合使用時

1.如果先bindService,再startService:

在bind的Activity退出的時候,Service會執行unBind方法而不執行onDestory方法,因爲有startService方法調用過,所以Activity與Service解除綁定後會有一個與調用者沒有關連的Service存在

2.如果先bindService,再startService,再調用Context.stopService

Service的onDestory方法不會立刻執行,因爲有一個與Service綁定的Activity,但是在Activity退出的時候,會執行onDestory,如果要立刻執行stopService,就得先解除綁定

 ---------------------------

把上面的"如果先bindService,再startService"換成"如果先startService,再bindService",結果是一樣的 

問題:

如果在一個Activity的onCreate方法中,

bindService(serviceIntent, conn, Context.BIND_AUTO_CREATE);

再startService(serviceIntent);

退出這個Activity時,會執行onUnBind

但是再次進入這個Activity的時候,爲什麼不執行onBind方法了?只有在這個Service銷燬後(執行onDestory),再進這個Activity纔會執行onBind,還有就是當有兩個客戶端時,在第一個客戶端startServie啓動服務再bindService綁定服務,這時跳到第二個客戶端裏(啓動時會調用onBind()),再客戶端startServie啓動服務再bindService綁定服務,啓動時不會調用用onBind()了(因爲之前客戶端已經啓動後沒有onDestory()銷燬Service,所以再客戶端第二次綁定服務時,只會返回IBinder對象給onServiceConnected()),而且要注意的是當,當第一個服務啓動並綁定一個服務時,再跳去第二個服務端啓動並綁定這個服務時,第二個服務端再解綁時,不會調用onUnbind(),只有回到第一個客戶端時,解綁這是纔會調用onUnbind(),順序反過來結果是一樣的。得出一個結論是:當一個服務沒被onDestory()銷燬之前,只有第一個啓動它的客戶端能調用它的onBind()和onUnbind()。

示例下載地址:http://pan.baidu.com/share/link?shareid=1614272126&uk=1428765741

還有一個多樣化的demo學習地址:http://pan.baidu.com/share/link?shareid=1616100229&uk=1428765741


發佈了7 篇原創文章 · 獲贊 7 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章