-
前面描述過,如果我們的服務僅供本地應用使用,不需要跨進程工作,則可以實現自有 Binder 類,讓客戶端通過該類直接訪問服務中的公共方法。其使用開發步驟如下
-
此方式只有在客戶端和服務位於同一應用和進程內纔有效,
-
如對於需要將 Activity 綁定到在後臺播放音樂的自有服務的音樂應用,此方式非常有效。
-
另一點之所以要求服務和客戶端必須在同一應用內,是爲了便於客戶端轉換返回的對象和正確調用其 API。服務和客戶端還必須在同一進程內,因爲此方式不執行任何跨進程編組。
-
BindService類繼承自Service
-
在該類中創建了一個LocalBinder繼承自Binder類,LocalBinder中聲明瞭一個getService方法,客戶端可訪問該方法獲取LocalService對象的實例
-
只要客戶端獲取到LocalService對象的實例就可調用LocalService服務端的公共方法,如getCount方法,
-
值得注意的是,我們在onBind方法中返回了binder對象,該對象便是LocalBinder的具體實例,而binder對象最終會返回給客戶端,客戶端通過返回的binder對象便可以與服務端實現交互。
package com.ipctest.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Description:綁定服務簡單實例--服務端
*/
public class LocalService extends Service{
private final static String TAG = "wzj";
private int count;
private boolean quit;
private Thread thread;
private LocalBinder binder = new LocalBinder();
/**
* 創建Binder對象,返回給客戶端即Activity使用,提供數據交換的接口
*/
public class LocalBinder extends Binder {
// 聲明一個方法,getService。(提供給客戶端調用)
LocalService getService() {
// 返回當前對象LocalService,這樣我們就可在客戶端端調用Service的公共方法了
return LocalService.this;
}
}
/**
* 把Binder類返回給客戶端
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service is invoke Created");
thread = new Thread(new Runnable() {
@Override
public void run() {
// 每間隔一秒count加1 ,直到quit爲true。
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
});
thread.start();
}
/**
* 公共方法
* @return
*/
public int getCount(){
return count;
}
/**
* 解除綁定時調用
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Service is invoke onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "Service is invoke Destroyed");
this.quit = true;
super.onDestroy();
}
}
package com.ipctest.service;
import android.app.Activity;
import android.app.Service;
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;
import com.zejian.ipctest.R;
/**
* Description:綁定服務實例--客戶端
*/
public class BindActivity extends Activity {
protected static final String TAG = "wzj";
Button btnBind;
Button btnUnBind;
Button btnGetDatas;
/**
* ServiceConnection代表與服務的連接,它只有兩個方法,
* onServiceConnected和onServiceDisconnected,
* 前者是在操作者在連接一個服務成功時被調用,而後者是在服務崩潰或被殺死導致的連接中斷時被調用
*/
private ServiceConnection conn;
private LocalService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind);
btnBind = (Button) findViewById(R.id.BindService);
btnUnBind = (Button) findViewById(R.id.unBindService);
btnGetDatas = (Button) findViewById(R.id.getServiceDatas);
//創建綁定對象
final Intent intent = new Intent(this, LocalService.class);
// 開啓綁定
btnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "綁定調用:bindService");
//調用綁定方法
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
// 解除綁定
btnUnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "解除綁定調用:unbindService");
// 解除綁定
if(mService!=null) {
mService = null;
unbindService(conn);
}
}
});
// 獲取數據
btnGetDatas.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mService != null) {
// 通過綁定服務傳遞的Binder對象,獲取Service暴露出來的數據
Log.d(TAG, "從服務端獲取數據:" + mService.getCount());
} else {
Log.d(TAG, "還沒綁定呢,先綁定,無法從服務端獲取數據");
}
}
});
conn = new ServiceConnection() {
/**
* 與服務器端交互的接口方法 綁定服務的時候被回調,在這個方法獲取綁定Service傳遞過來的IBinder對象,
* 通過這個IBinder對象,實現宿主和Service的交互。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "綁定成功調用:onServiceConnected");
// 獲取Binder
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
}
/**
* 當取消綁定的時候被回調。但正常情況下是不被調用的,它的調用時機是當Service服務被意外銷燬時,
* 例如內存的資源不足時這個方法才被自動調用。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mService=null;
}
};
}
}
-
在客戶端中我們創建了一個ServiceConnection對象,該代表與服務的連接,它只有兩個方法, onServiceConnected和onServiceDisconnected
onServiceConnected(ComponentName name, IBinder service)
|
系統會調用該方法以傳遞服務的 onBind() 方法返回的 IBinder。其中service便是服務端返回的IBinder實現類對象,通過該對象我們便可以調用獲取LocalService實例對象,進而調用服務端的公共方法。而ComponentName是一個封裝了組件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的類,如包名,組件描述等信息,較少使用該參數。
|
onServiceDisconnected(ComponentName name)
|
Android 系統會在與服務的連接意外中斷時(例如當服務崩潰或被終止時)調用該方法。注意:當客戶端取消綁定時,系統“絕對不會”調用該方法。
|
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "綁定成功調用:onServiceConnected");
// 獲取Binder
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService=null;
}
};
-
在onServiceConnected()被回調前,我們還需先把當前Activity綁定到服務LocalService上,綁定服務是通過通過bindService()方法,解綁服務則使用unbindService()方法,這兩個方法解析如下:
bindService(Intent service, ServiceConnection conn, int flags)
|
該方法執行綁定服務操作,其中Intent是我們要綁定的服務(也就是LocalService)的意圖,而ServiceConnection代表與服務的連接,它只有兩個方法,前面已分析過,flags則是指定綁定時是否自動創建Service。0代表不自動創建、BIND_AUTO_CREATE則代表自動創建。
|
unbindService(ServiceConnection conn)
|
該方法執行解除綁定的操作,其中ServiceConnection代表與服務的連接,它只有兩個方法,前面已分析過。
|
-
Activity通過bindService()綁定到LocalService後,ServiceConnection#onServiceConnected()便會被回調並可以獲取到LocalService實例對象mService,之後我們就可以調用LocalService服務端的公共方法了,最後還需要在清單文件中聲明該Service。而客戶端佈局文件實現如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/BindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="綁定服務器"
/>
<Button
android:id="@+id/unBindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="解除綁定"
/>
<Button
android:id="@+id/getServiceDatas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="獲取服務方數據"
/>
</LinearLayout>
-
當我們第一次點擊綁定服務時,LocalService服務端的onCreate()、onBind方法會依次被調用,
-
此時客戶端的ServiceConnection#onServiceConnected()被調用並返回LocalBinder對象,
-
接着調用LocalBinder#getService方法返回LocalService實例對象,
-
此時客戶端便持有了LocalService的實例對象,也就可以任意調用LocalService類中的聲明公共方法了。
-
更值得注意的是,我們多次調用bindService方法綁定LocalService服務端,而LocalService得onBind方法只調用了一次,那就是在第一次調用bindService時纔會回調onBind方法。接着我們點擊獲取服務端的數據,
-
從Log中看出我們點擊了3次通過getCount()獲取了服務端的3個不同數據,
-
最後點擊解除綁定,此時LocalService的onUnBind、onDestroy方法依次被回調,並且多次綁定只需一次解綁即可。
-
此情景也就說明了綁定狀態下的Service生命週期方法的調用依次爲onCreate()、onBind、onUnBind、onDestroy。