Android Service 之三(Bind Service, 繼承自 Binder 類)
之前提及過,啟動Service有兩種方式:startService 與 bindService。前者已經說過如何使用,所以,這篇貼子主要是關於 bind service的。 這裡所討論的是僅針對那些被綁定的service的,而那些既被startService() 又被 bindService() 的 service 不在此範圍內。
① Bind Service就像是C/S架構中的服務端,其他組件(比如 Activity)綁定到它(通過 bindService()),可以向它發送請求,可以接受從它返回的響應,它甚至還提供了進程間通信(IPC)功能。
② 一個service要想能夠被其他組件綁定,那麼它的 onBind() 方法必須被實現,且必須返回一個 IBinder 對象,然後其他組件可以通過這個 IBinder 對象與該 service 進行通訊。
③ 多個client可以綁定至同一個service,但該 service 的onBind() 方法只會在第一個 client 綁定至其的時候被調用,當其他 client 再次綁定到它的時候,並不會調用 onBind() 方法,而是直接返回第一次被調用時產生的那個 IBinder 對象。也就是說,在其生命週期內,onBind() 只會被調用一次。
④ Bind Service 的生命週期如下圖所示:
⑤ Bind Service 不會在後臺無限期的一直運行,而是當所有綁定至其的組件都調用了 unbindService() 進行解綁之後,系統就會將其停掉以回收資源。
⑥ 當我們要實現一個 Bind Service 的時候,最重要的就是實現它的 onBind() 方法以返回一個 IBinder 對象
要生成一個 Bound Service ,共有三種方式:繼承自 Binder 類,使用 Messenger ,使用 AIDL。下面且聽小生一一道來。
第一種:繼承自 Binder 類
需要注意的是,這種方式僅僅適用於這種場合:service 與 application 在同一個進程中。這種場合也是最最常見的。
它分以下幾個步驟:
a. 在你的 service 類中聲明一個內部類來繼承 Binder 類。在該內部類中,最好提供一個公共方法來返回你的 service 實例。
b. 在你的 service 類中需要聲明一個這個內部類的實例,以供在 onBind() 方法中返回
c. 在 client 端,在 onServiceConnected() 方法中得到從 onBind() 方法中返回的 IBinder 對象,然後可以通過該 對象中的公共方法得到相應的 service 實例,正如 第一個步驟 所說的那樣。
d. 在 service 中提供公共方法, 這樣就可以在組件(如 Activity 中調用這些公共方法了)
下面給出一例:
service 代碼
- public class BindServiceWithIBinder extends Service {
- private static final String TAG = "BindServiceWithIBinder";
- private final MyIBinder myIBinder = new MyIBinder();
- /**
- * bindService() 時,調用的是這個方法,而非 onStartCommnad() 方法
- */
- @Override
- public IBinder onBind(Intent intent) {
- // 在主 Activity 上的 TextView 中打印出一行LOG
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onBind"));
- return myIBinder;
- }
- @Override
- public void onCreate() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onCreate"));
- }
- @Override
- public void onDestroy() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onDestroy"));
- }
- @Override
- public void onRebind(Intent intent) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onRebind"));
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onStartCommand"));
- return START_STICKY;
- }
- @Override
- public boolean onUnbind(Intent intent) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onUnbind"));
- return super.onUnbind(intent);
- }
- /**
- * 聲明一個 Binder 類的實現類,供在 onBind() 方法中返回該類的一個實例
- * @author 001718
- *
- */
- public class MyIBinder extends Binder {
- public Service getService() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "BindServiceWithIBinder.MyIBinder.getService()"));
- return BindServiceWithIBinder.this;
- }
- }
- /**
- * service 提供的公共方法,在activity中可以調用
- */
- public void download() {
- try {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG
- + " ----> download(): 文件下載中..."));
- Thread.sleep(3000);
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG
- + " ----> download(): 文件下載完成..."));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
主 Activity 中的相應關鍵代碼為:
- private void doUnbindService() {
- if (isBound) {
- unbindService(myLocalServiceConnection);
- isBound = false;
- }
- }
- private void doBindService() {
- Log.i("bind", "begin to bind");
- bindService(intent, myLocalServiceConnection, Context.BIND_AUTO_CREATE);
- }
- private ServiceConnection myLocalServiceConnection = new ServiceConnection() {
- public void onServiceConnected(android.content.ComponentName name,
- android.os.IBinder service) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "myServiceConnection.onServiceConnected"));
- // 因為 客戶端 與 服務 在同一個進程內,這樣一來,就可以知道參數 "service"的類型了,也就可以進行顯示的強制類型轉換了。
- // 而如果 客戶端與服務不在同一個進程中的話,那麼此處是不可以進行顯示強制類型轉換的,
- // 因為,通過Debug,可以發現此時傳進來的 Service 的類型是 BinderProxy
- MyIBinder myIBinder = (MyIBinder) service;
- bsi = (BindServiceWithIBinder) myIBinder.getService();
- isBound = true;
- bsi.download();
- };
- public void onServiceDisconnected(android.content.ComponentName name) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "myServiceConnection.onServiceDisconnected"));
- isBound = false;
- };
- };
下面來看運行效果:
連續點擊兩次 Bind Service
從此圖中可以看出,bind service 的響應過程。也可以看到,第二次點擊時,service 沒作任何反應,因為當前 Activity 在第一次點擊後就已經跟此service綁定了。
點擊 Unbind Service
至此,該 service 的生命週期結束,它也會被系統給停掉以回收資源。