1. 程式人生 > >startService bindService 區別

startService bindService 區別

Android執行Service有兩種方法,一種是startService,一種是bindService。下面讓我們一起來聊一聊這兩種執行Service方法的區別。 
這裡寫圖片描述

1、生命週期上的區別

執行startService時,Service會經歷onCreate->onStartCommand。當執行stopService時,直接呼叫onDestroy方法。呼叫者如果沒有stopService,Service會一直在後臺執行,下次呼叫者再起來仍然可以stopService。

執行bindService時,Service會經歷onCreate->onBind。這個時候呼叫者和Service繫結在一起。呼叫者呼叫unbindService方法或者呼叫者Context不存在了(如Activity被finish了),Service就會呼叫onUnbind->onDestroy。這裡所謂的繫結在一起就是說兩者共存亡了。

多次呼叫startService,該Service只能被建立一次,即該Service的onCreate方法只會被呼叫一次。但是每次呼叫startService,onStartCommand方法都會被呼叫。Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。

第一次執行bindService時,onCreate和onBind方法會被呼叫,但是多次執行bindService時,onCreate和onBind方法並不會被多次呼叫,即並不會多次建立服務和繫結服務。

2、呼叫者如何獲取繫結後的Service的方法

onBind回撥方法將返回給客戶端一個IBinder介面例項,IBinder允許客戶端回撥服務的方法,比如得到Service執行的狀態或其他操作。我們需要IBinder物件返回具體的Service物件才能操作,所以說具體的Service物件必須首先實現Binder物件。

3、既使用startService又使用bindService的情況

如果一個Service又被啟動又被繫結,則該Service會一直在後臺執行。首先不管如何呼叫,onCreate始終只會呼叫一次。對應startService呼叫多少次,Service的onStart方法便會呼叫多少次。Service的終止,需要unbindService和stopService同時呼叫才行。不管startService與bindService的呼叫順序,如果先呼叫unbindService,此時服務不會自動終止,再呼叫stopService之後,服務才會終止;如果先呼叫stopService,此時服務也不會終止,而再呼叫unbindService或者之前呼叫bindService的Context不存在了(如Activity被finish的時候)之後,服務才會自動停止。

那麼,什麼情況下既使用startService,又使用bindService呢?

如果你只是想要啟動一個後臺服務長期進行某項任務,那麼使用startService便可以了。如果你還想要與正在執行的Service取得聯絡,那麼有兩種方法:一種是使用broadcast,另一種是使用bindService。前者的缺點是如果交流較為頻繁,容易造成效能上的問題,而後者則沒有這些問題。因此,這種情況就需要startService和bindService一起使用了。

另外,如果你的服務只是公開一個遠端介面,供連線上的客戶端(Android的Service是C/S架構)遠端呼叫執行方法,這個時候你可以不讓服務一開始就執行,而只是bindService,這樣在第一次bindService的時候才會建立服務的例項執行它,這會節約很多系統資源,特別是如果你的服務是遠端服務,那麼效果會越明顯(當然在Servcie建立的是偶會花去一定時間,這點需要注意)。

4、本地服務與遠端服務

本地服務依附在主程序上,在一定程度上節約了資源。本地服務因為是在同一程序,因此不需要IPC,也不需要AIDL。相應bindService會方便很多。缺點是主程序被kill後,服務變會終止。

遠端服務是獨立的程序,對應程序名格式為所在包名加上你指定的android:process字串。由於是獨立的程序,因此在Activity所在程序被kill的是偶,該服務依然在執行。缺點是該服務是獨立的程序,會佔用一定資源,並且使用AIDL進行IPC稍微麻煩一點。

對於startService來說,不管是本地服務還是遠端服務,我們需要做的工作都一樣簡單。

5、程式碼例項

startService啟動服務

public class LocalService1 extends Service {
    /**
    * onBind 是 Service 的虛方法,因此我們不得不實現它。
    * 返回 null,表示客服端不能建立到此服務的連線。
    */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onStartCommand(Intent intent, int startId, int flags) {
        super.onStartCommand(intent, startId, flags);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

bindService繫結服務

public class LocalService extends Service {
/**
* 在 Local Service 中我們直接繼承 Binder 而不是 IBinder,因為 Binder 實現了 IBinder 介面,這樣我們可以** 少做很多工作。
*/
public class SimpleBinder extends Binder{
/**
* 獲取 Service 例項
* @return
*/
public LocalService getService(){
return LocalService.this;
}

public int add(int a, int b){
return a + b;
}
}

public SimpleBinder sBinder;

@Override
public void onCreate() {
super.onCreate();
// 建立 SimpleBinder
sBinder = new SimpleBinder();
}

@Override
public IBinder onBind(Intent intent) {
// 返回 SimpleBinder 物件
return sBinder;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

上面的程式碼關鍵之處,在於 onBind(Intent) 這個方法 返回了一個實現了 IBinder 介面的物件,這個物件將用於繫結Service 的 Activity 與 Local Service 通訊。

下面是 Activity 中的程式碼:

public class Main extends Activity {
    private final static String TAG = "SERVICE_TEST";
    private ServiceConnection sc;
    private boolean isBind;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sc = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                LocalService.SimpleBinder sBinder = (LocalService.SimpleBinder)service;
                Log.v(TAG, "3 + 5 = " + sBinder.add(3, 5));
                Log.v(TAG, sBinder.getService().toString());
            }
        };

        findViewById(R.id.btnBind).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(new Intent(Main.this, LocalService.class), sc, Context.BIND_AUTO_CREATE);
                isBind = true;
            }
        });

        findViewById(R.id.btnUnbind).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isBind){
                    unbindService(sc);
                    isBind = false;
                }
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

6、在AndroidManifest.xml裡Service元素常見選項

android:name  -------------  服務類名

android:label  --------------  服務的名字,如果此項不設定,那麼預設顯示的服務名則為類名

android:icon  --------------  服務的圖示

android:permission  -------  申明此服務的許可權,這意味著只有提供了該許可權的應用才能控制或連線此服務

android:process  ----------  表示該服務是否執行在另外一個程序,如果設定了此項,那麼將會在包名後面加上這段字串表示另一程序的名字

android:enabled  ----------  表示是否能被系統例項化,為true表示可以,為false表示不可以,預設為true

android:exported  ---------  表示該服務是否能夠被其他應用程式所控制或連線,不設定預設此項為 false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

轉載地址: 

Android startservice & bindservice的區別

作為一個android開發者,service大家應該已經接觸過了,接觸Android的第一節課,就是將Android的四大元件,那麼作為四大元件之一的service,你是否足夠了解呢?

  1. service和thread的區別你是否知道?service真的能執行長時間的後臺操作麼?

  2. 你是否知道startservice和bindservice的區別呢?

  3. service的生命週期,我們是否應該主動結束一個service呢?service是否會導致記憶體洩露呢?

  4. service與intentservice的區別是什麼?

我們主要解答問題2,startservice與bindservice的區別。

Developer上提到,service主要有兩種形式,

  1. started
  2. bound

上邊的started呢,對應與startservice,bound對應於bindservice,他們都是啟動service的方式。

startservice

我們可以通過startservice來啟動一個service,啟動後,service在後臺執行。通常來說,該service是無法返回結果的(這也是與bindservice的區別之一),比如我們可以下載一個檔案。 
和startservice對應的是stopservice,我們可以來顯式的結束一個service。 
service的生命週期也比較簡單,和startservice相關的有三個函式,分別是onCreate,onStartCommand,onDestory。

當我們首次startserivce啟動一個service的時候,會呼叫service的onCreate函式,建立該服務,然後呼叫onStartCommand函式,執行操作。如果我們多次通過startservice啟動服務,那麼onCreate只會呼叫一次,直接呼叫onStartCommand。 
我們可以呼叫stopsevice來結束一個service。同樣,我們也可以多次呼叫(第一次呼叫已經結束,但是後來繼續呼叫並不會產生異常)。

當一個service通過startservice啟動後,它就獨立於呼叫者而執行(也就是說,呼叫者的生命週期和它的生命週期沒有關係),因此呢,service應該在任務完成的時候呼叫stopSelf函式或者呼叫者stopservice來結束該服務

Developer上提到,當我們的service執行完的時候,為了避免消耗系統資源或電量,應該結束該service。否則,該服務將會一直執行在後臺,直到裝置記憶體不足等原因把他關閉掉。

然而,startservice並不能解決我們所有的需求,比如,我有時候,需要service的返回結果,我需要和service互動,startservice顯然不能完成。那麼我們可以使用到bindservice

bindservice

Developer這樣提到,

A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC).

我們可以利用bindservice來和service繫結,繫結後,我們可以和service互動,傳送請求,得到結果甚至執行IPC通訊。

那麼,我們應該如何使用bindservice呢?

  1. 首先我們需要過載service的onBinder,返回一個IBinder物件。該物件是 
    呼叫者和serivce互動的介面。
  2. 新建一個ServiceConnection變數,該類是監聽Service是否bound的介面,我們過載其onServiceConnected和onServiceDisconnected方法。onServiceConnected方法中,我們得到了Service中onBinder返回的IBinder介面。
  3. 呼叫bindservice來繫結服務。

按照上邊的步驟,我們的程式碼如下, 
1. 過載onBinder並返回一個IBinder物件。在LocalBinder中,提供getSercive方法,返回service例項。

    public class LocalBinder extends Binder {
        MyService getService() {
            return MyService.this;
        }
    }

    private final IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return mBinder;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.呼叫者中新建ServiceConnection介面,在onServiceConnected方法中,將IBinder物件轉為LocalBinder物件,並呼叫其getService方法,得到Service。

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBoundService = ((MyService.LocalBinder) service).getService();
        }

        public void onServiceDisconnected(ComponentName className) {
              mBoundService = null;
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.呼叫者執行bindservice,其中mConnection為ServiceConnection介面。

bindService(new Intent(MainActivity.this,MyService.class), mConnection, Context.BIND_AUTO_CREATE);
  • 1

當用戶執行bindservice後,bind的結果會回撥mConnection介面,若bind成功,就會回撥onServiceConnected方法,然後我們在此方法中,得到了Service例項,得到Service例項後,然後我們就可以和Service互動了

我們繼續完善程式碼,在Service的onCreate方法中,我們新建一個執行緒,每隔1s,計數器加1.其中count為成員變數,表示當前計數值,threadDisable表示是否service關閉。

        new Thread(new Runnable() {
            // @Override
            public void run() {
                while (!threadDisable) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    count++;
                    System.out.println("CountService Count is " + count);
                }
            }
        }).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然後,我們在Service中新建一個方法,返回計數值。

    public int getCount() {
        return count;
    }
  • 1
  • 2
  • 3

那麼我們在呼叫者中,就可以利用上邊得到的mBoundService來呼叫getCount方法,達到與service互動的目的。 
專案地址,https://github.com/KingPaul/ServiceDemo