綁定服務:
綁定服務是客戶端-服務器接口中的服務器。綁定服務可讓組件(例如 Activity)綁定到服務、發(fā)送請求、接收響應,甚至執(zhí)行進程間通信 (IPC)。 綁定服務通常只在為其他應用組件服務時處于活動狀態(tài),不會無限期在后臺運行。
本文向您介紹如何創(chuàng)建綁定服務,包括如何綁定到來自其他應用組件的服務。 不過,您還應參閱服務文檔,了解有關一般服務的更多信息,例如:如何利用服務傳送通知、如何將服務設置為在前臺運行等等。
基礎知識
綁定服務是 Service類的實現(xiàn),可讓其他應用與其綁定和交互。要提供服務綁定,您必須實現(xiàn) onBind()回調方法。該方法返回的 IBinder對象定義了客戶端用來與服務進行交互的編程接口。
客戶端可通過調用 [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務。調用時,它必須提供 ServiceConnection的實現(xiàn),后者會監(jiān)控與服務的連接。[bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))方法會立即無值返回,但當 Android 系統(tǒng)創(chuàng)建客戶端與服務之間的連接時,會調用 ServiceConnection上的 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder)),向客戶端傳遞用來與服務通信的IBinder。
多個客戶端可同時連接到一個服務。不過,只有在第一個客戶端綁定時,系統(tǒng)才會調用服務的 onBind()方法來檢索 IBinder。系統(tǒng)隨后無需再次調用 onBind(),便可將同一 IBinder傳遞至任何其他綁定的客戶端。
當最后一個客戶端取消與服務的綁定時,系統(tǒng)會將服務銷毀(除非startService()也啟動了該服務)。
當您實現(xiàn)綁定服務時,最重要的環(huán)節(jié)是定義您的 onBind()回調方法返回的接口。您可以通過幾種不同的方法定義服務的IBinder接口,下文對這些方法逐一做了闡述。
綁定到已啟動服務
正如服務文檔中所述,您可以創(chuàng)建同時具有已啟動和綁定兩種狀態(tài)的服務。 也就是說,可通過調用startService()啟動該服務,讓服務無限期運行;此外,還可通過調用[bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))使客戶端綁定到服務。
如果您確實允許服務同時具有已啟動和綁定狀態(tài),則服務啟動后,系統(tǒng)“絕對不會”在所有客戶端都取消綁定時銷毀服務。 為此,您必須通過調用[stopSelf()](http://developer.android.com/reference/android/app/Service.html#stopSelf()或 stopService()顯式停止服務。
盡管您通常應該實現(xiàn) onBind()或[onStartCommand()](http://developer.android.com/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int)),但有時需要同時實現(xiàn)這兩者。例如,音樂播放器可能發(fā)現(xiàn)讓其服務無限期運行并同時提供綁定很有用處。 這樣一來,Activity 便可啟動服務進行音樂播放,即使用戶離開應用,音樂播放也不會停止。 然后,當用戶返回應用時,Activity 可綁定到服務,重新獲得回放控制權。
請務必閱讀管理綁定服務的生命周期部分,詳細了解有關添加綁定已啟動服務時該服務的生命周期信息。
創(chuàng)建綁定服務
創(chuàng)建提供綁定的服務時,您必須提供 IBinder,用以提供客戶端用來與服務進行交互的編程接口。
您可以通過三種方法定義接口:
擴展 Binder 類
如果服務是供您的自有應用專用,并且在與客戶端相同的進程中運行(常見情況),則應通過擴展 Binder類并從onBind()返回它的一個實例來創(chuàng)建接口??蛻舳耸盏?a target="_blank" rel="nofollow">Binder后,可利用它直接訪問 Binder實現(xiàn)中乃至Service中可用的公共方法。
如果服務只是您的自有應用的后臺工作線程,則優(yōu)先采用這種方法。 不以這種方式創(chuàng)建接口的唯一原因是,您的服務被其他應用或不同的進程占用。使用 Messenger
如需讓接口跨不同的進程工作,則可使用 Messenger為服務創(chuàng)建接口。服務可以這種方式定義對應于不同類型 Message對象的 Handler。此 Handler是 Messenger的基礎,后者隨后可與客戶端分享一個 IBinder,從而讓客戶端能利用 Message對象向服務發(fā)送命令。此外,客戶端還可定義自有Messenger,以便服務回傳消息。
這是執(zhí)行進程間通信 (IPC) 的最簡單方法,因為 Messenger會在單一線程中創(chuàng)建包含所有請求的隊列,這樣您就不必對服務進行線程安全設計。使用 AIDL
AIDL(Android 接口定義語言)執(zhí)行所有將對象分解成原語的工作,操作系統(tǒng)可以識別這些原語并將它們編組到各進程中,以執(zhí)行 IPC。之前采用 Messenger的方法實際上是以 AIDL 作為其底層結構。如上所述,Messenger會在單一線程中創(chuàng)建包含所有客戶端請求的隊列,以便服務一次接收一個請求。不過,如果您想讓服務同時處理多個請求,則可直接使用 AIDL。 在此情況下,您的服務必須具備多線程處理能力,并采用線程安全式設計。
如需直接使用 AIDL,您必須創(chuàng)建一個定義編程接口的 .aidl文件。Android SDK 工具利用該文件生成一個實現(xiàn)接口并處理 IPC 的抽象類,您隨后可在服務內對其進行擴展。
注:大多數(shù)應用“都不會”****使用 AIDL 來創(chuàng)建綁定服務,因為它可能要求具備多線程處理能力,并可能導致實現(xiàn)的復雜性增加。因此,AIDL 并不適合大多數(shù)應用,本文也不會闡述如何將其用于您的服務。如果您確定自己需要直接使用 AIDL,請參閱 AIDL 文檔。
擴展 Binder 類
如果您的服務僅供本地應用使用,不需要跨進程工作,則可以實現(xiàn)自有 Binder類,讓您的客戶端通過該類直接訪問服務中的公共方法。
注:此方法只有在客戶端和服務位于同一應用和進程內這一最常見的情況下方才有效。 例如,對于需要將 Activity 綁定到在后臺播放音樂的自有服務的音樂應用,此方法非常有效。
以下是具體的設置方法:
- 在您的服務中,創(chuàng)建一個可滿足下列任一要求的 Binder實例:
- 包含客戶端可調用的公共方法
- 返回當前 Service 實例,其中包含客戶端可調用的公共方法
- 或返回由服務承載的其他類的實例,其中包含客戶端可調用的公共方法
- 從 onBind()回調方法返回此 Binder實例。
- 在客戶端中,從 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調方法接收 Binder,并使用提供的方法調用綁定服務。
注:之所以要求服務和客戶端必須在同一應用內,是為了便于客戶端轉換返回的對象和正確調用其 API。服務和客戶端還必須在同一進程內,因為此方法不執(zhí)行任何跨進程編組。
例如,以下這個服務可讓客戶端通過 Binder實現(xiàn)訪問服務中的方法:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
LocalBinder為客戶端提供 getService()方法,以檢索 LocalService的當前實例。這樣,客戶端便可調用服務中的公共方法。 例如,客戶端可調用服務中的 getRandomNumber()。
點擊按鈕時,以下這個 Activity 會綁定到 LocalService并調用 getRandomNumber():
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
上例說明了客戶端如何使用 ServiceConnection的實現(xiàn)和 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調綁定到服務。下文更詳細介紹了綁定到服務的過程。
注:上例并未顯式取消與服務的綁定,但所有客戶端都應在適當?shù)臅r間(例如當 Activity 暫停時)取消綁定。
如需查看更多示例代碼,請參閱 ApiDemos 中的 LocalService.java
類和 LocalServiceActivities.java
類。
使用 Messenger
如需讓服務與遠程進程通信,則可使用 Messenger為您的服務提供接口。利用此方法,您無需使用 AIDL 便可執(zhí)行進程間通信 (IPC)。
當您需要執(zhí)行 IPC 時,為您的接口使用Messenger要比使用 AIDL 實現(xiàn)它更加簡單,因為 Messenger會將所有服務調用排入隊列,而純粹的 AIDL 接口會同時向服務發(fā)送多個請求,服務隨后必須應對多線程處理。
對于大多數(shù)應用,服務不需要執(zhí)行多線程處理,因此使用 Messenger可讓服務一次處理一個調用。如果您的服務必須執(zhí)行多線程處理,則應使用 AIDL來定義接口。
以下是 Messenger 的使用方法摘要:
- 服務實現(xiàn)一個 Handler,由其接收來自客戶端的每個調用的回調
- Handler 用于創(chuàng)建 Messenger 對象(對 Handler 的引用)
- Messenger 創(chuàng)建一個 IBinder,服務通過 onBind() 使其返回客戶端
- 客戶端使用 IBinder 將 Messenger(引用服務的 Handler)實例化,然后使用后者將 Message 對象發(fā)送給服務
- 服務在其 Handler 中(具體地講,是在 handleMessage() 方法中)接收每個 Message
這樣,客戶端并沒有調用服務的“方法”。而客戶端傳遞的“消息”(Message對象)是服務在其 Handler
中接收的。
以下是一個使用 Messenger接口的簡單服務示例:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
請注意,服務就是在 Handler的 handleMessage()方法中接收傳入的 Message,并根據(jù) what成員決定下一步操作。
客戶端只需根據(jù)服務返回的 IBinder創(chuàng)建一個 Messenger,然后利用 send()發(fā)送一條消息。例如,以下就是一個綁定到服務并向服務傳遞 MSG_SAY_HELLO消息的簡單 Activity:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
請注意,此示例并未說明服務如何對客戶端作出響應。如果您想讓服務作出響應,則還需要在客戶端中創(chuàng)建一個Messenger。然后,當客戶端收到 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調時,會向服務發(fā)送一條 Message,并在其send()方法的 replyTo參數(shù)中包含客戶端的 Messenger。
如需查看如何提供雙向消息傳遞的示例,請參閱 MessengerService.java
(服務)和MessengerServiceActivities.java
(客戶端)示例。
綁定到服務
應用組件(客戶端)可通過調用 [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務。Android 系統(tǒng)隨后調用服務的 onBind()方法,該方法返回用于與服務交互的 IBinder。
綁定是異步的。[bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))會立即返回,“絕對不會”**使 IBinder返回客戶端。要接收 IBinder,客戶端必須創(chuàng)建一個 ServiceConnection實例,并將其傳遞給 [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))。ServiceConnection
包括一個回調方法,系統(tǒng)通過調用它來傳遞 IBinder。
注:只有 Activity、服務和內容提供程序可以綁定到服務—您無法從廣播接收器綁定到服務。
因此,要想從客戶端綁定到服務,必須:
- 實現(xiàn) ServiceConnection。您的實現(xiàn)必須重寫兩個回調方法:
- [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))
系統(tǒng)會調用該方法以傳遞服務的 onBind()方法返回的 IBinder。 -
onServiceDisconnected()
Android 系統(tǒng)會在與服務的連接意外中斷時(例如當服務崩潰或被終止時)調用該方法。當客戶端取消綁定時,系統(tǒng)“絕對不會”調用該方法。
調用 [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))以傳遞 ServiceConnection實現(xiàn)。
當系統(tǒng)調用您的 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調方法時,您可以使用接口定義的方法開始調用服務。
要斷開與服務的連接,請調用 unbindService()。
當您的客戶端被銷毀時,它將取消與服務的綁定,但您應該始終在完成與服務的交互時或您的 Activity 暫停時取消綁定,以便服務能夠在未被占用時關閉。 (下文更詳細地闡述了綁定和取消綁定的適當時機。)
例如,以下代碼段通過擴展 Binder 類將客戶端與上面創(chuàng)建的服務相連,因此它只需將返回的 IBinder
轉換為LocalService類并請求 LocalService實例:
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};
客戶端可通過將此 ServiceConnection傳遞至 [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))綁定到服務。例如:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- [bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))
的第一個參數(shù)是一個 Intent,用于顯式命名要綁定的服務(但 Intent 可能是隱式的) - 第二個參數(shù)是 ServiceConnection對象
- 第三個參數(shù)是一個指示綁定選項的標志。它通常應該是 BIND_AUTO_CREATE,以便創(chuàng)建尚未激活的服務。 其他可能的值為 BIND_DEBUG_UNBIND和 BIND_NOT_FOREGROUND,或 0(表示無)。
附加說明
以下是一些有關綁定到服務的重要說明:
- 您應該始終捕獲 DeadObjectException
異常,它們是在連接中斷時引發(fā)的。這是遠程方法引發(fā)的唯一異常 - 對象是跨進程計數(shù)的引用
- 您通常應該在客戶端生命周期的匹配引入 (bring-up) 和退出 (tear-down) 時刻期間配對綁定和取消綁定。 例如:
- 如果您只需要在 Activity 可見時與服務交互,則應在 onStart()期間綁定,在 onStop()期間取消綁定。
- 如果您希望 Activity 在后臺停止運行狀態(tài)下仍可接收響應,則可在 onCreate()期間綁定,在onDestroy()期間取消綁定。請注意,這意味著您的 Activity 在其整個運行過程中(甚至包括后臺運行期間)都需要使用服務,因此如果服務位于其他進程內,那么當您提高該進程的權重時,系統(tǒng)終止該進程的可能性會增加。
注:通常情況下,切勿在 Activity 的 onResume()和 onPause()期間綁定和取消綁定,因為每一次生命周期轉換都會發(fā)生這些回調,您應該使發(fā)生在這些轉換期間的處理保持在最低水平。此外,如果您的應用內的多個 Activity 綁定到同一服務,并且其中兩個 Activity 之間發(fā)生了轉換,則如果當前 Activity 在下一次綁定(恢復期間)之前取消綁定(暫停期間),系統(tǒng)可能會銷毀服務并重建服務。 (Activity文檔中介紹了這種有關 Activity 如何協(xié)調其生命周期的 Activity 轉換。)
如需查看更多顯示如何綁定到服務的示例代碼,請參閱 ApiDemos 中的 RemoteService.java
類。
管理綁定服務的生命周期
當服務與所有客戶端之間的綁定全部取消時,Android 系統(tǒng)便會銷毀服務(除非還使用 [onStartCommand()](http://developer.android.com/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int))啟動了該服務)。因此,如果您的服務是純粹的綁定服務,則無需對其生命周期進行管理—Android 系統(tǒng)會根據(jù)它是否綁定到任何客戶端代您管理。
不過,如果您選擇實現(xiàn) [onStartCommand()](http://developer.android.com/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int))回調方法,則您必須顯式停止服務,因為系統(tǒng)現(xiàn)在已將服務視為已啟動。在此情況下,服務將一直運行到其通過 stopSelf()自行停止,或其他組件調用 stopService()為止,無論其是否綁定到任何客戶端。
此外,如果您的服務已啟動并接受綁定,則當系統(tǒng)調用您的 onUnbind()方法時,如果您想在客戶端下一次綁定到服務時接收 onRebind()調用(而不是接收 onBind()調用),則可選擇返回 true。onRebind()返回空值,但客戶端仍在其 [onServiceConnected()](http://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))
回調中接收 IBinder。下文圖 1 說明了這種生命周期的邏輯。

如需了解有關已啟動服務生命周期的詳細信息,請參閱服務文檔。