bound service是CS接口中的服務(wù)端憔维。一個bound service允許組件(例如activity)與它綁定,發(fā)送請求毁枯,接受回應(yīng)甚至執(zhí)行IPC。一般情況下叮称,一個bound service只有在服務(wù)其余應(yīng)用組件的時候才存活而不會單獨(dú)的在后臺運(yùn)行种玛。
基礎(chǔ)
bound service實(shí)現(xiàn)了service的接口,允許其他組件與它綁定并與它交互颅拦。你必須實(shí)現(xiàn)onBind回調(diào)方法來給service提供綁定蒂誉。此方法返回一個IBinder對象,該對象定義了客戶端可用于與service交互的編程接口距帅。
一個客戶端可以調(diào)用bindService
來與service綁定右锨。這時客戶端必須提供一個ServiceConnection
的實(shí)現(xiàn)來監(jiān)控與service的連接。bindService
方法會直接返回碌秸,不帶任何值绍移。但是如果Android系統(tǒng)創(chuàng)建了客戶端與service之間的連接,它會調(diào)用ServiceConnection
的onServiceConnected
方法來傳遞一個IBinder
對象給客戶端讥电,這個IBinder
可以用來與service通訊蹂窖。
多個客戶端可以立刻連接到一個service。然而恩敌,系統(tǒng)只會在第一個客戶端與service綁定的時候調(diào)用onBind方法來檢索IBinder瞬测。然后系統(tǒng)會返回相同的IBinder給其余的綁定客戶端,而不是再次調(diào)用onBind纠炮。
當(dāng)最后一個客戶端與service解綁的時候月趟,系統(tǒng)會銷毀該service。(除非該service已經(jīng)被startServie方法啟動過)
如果你允許你的service可以被start也可以被綁定恢口,那么當(dāng)它被start后孝宗,所有客戶端與它解綁的時候,系統(tǒng)是不會銷毀它的耕肩。你還需要調(diào)用stopSelf或者stopService來停止它因妇。
當(dāng)你實(shí)現(xiàn)你的bound service的時候,最重要的部分就是定義onBind回調(diào)方法返回的接口猿诸。有幾種不同的方式來定義你的IBinder接口婚被,下面將討論每一種技術(shù)。
創(chuàng)建一個bound service
當(dāng)創(chuàng)建一個可以綁定的service的時候梳虽,你必須提供一個IBinder摔寨,該IBinder提供了客戶端可以與service交互的編程接口。這里有三種方式可以定義這個接口:
繼承Binder類
如果你的service是你應(yīng)用私有的怖辆,和客戶端運(yùn)行在一個進(jìn)程的是复,你應(yīng)該繼承實(shí)現(xiàn)Binder類來創(chuàng)建你的接口删顶,并且在onBind方法中返回該接口的實(shí)例∈缋龋客戶端接收到Binder后可以用它來直接訪問Binder實(shí)現(xiàn)中的甚至service中的公開方法逗余。
當(dāng)你的service僅僅是你應(yīng)用程序的后臺工作人員的時候,這是首選的技術(shù)季惩。你不使用這種方式創(chuàng)建你的service的唯一的原因是你的service可以被外部應(yīng)用使用或者可以跨進(jìn)程录粱。
使用Messenger
如果你需要你的接口在不同的線程中工作,你可以用Messenger來給service創(chuàng)建一個接口画拾。以這種方式啥繁,service定義了一個handler來回應(yīng)不同類型的Message對象,這個Handler是Messenger的基礎(chǔ)青抛,可以和客戶端分享一個IBinder,允許客戶端用Message向service發(fā)送指令旗闽。客戶端可以定義自己的Messenger,這樣service就可以發(fā)送Message回來蜜另。
這是最簡單的方式來進(jìn)程間通訊适室,因?yàn)镸essenger將所有的請求在一個線程中排出隊(duì)列所以你不需要考慮你的service要是線程安全的。
使用AIDL
AIDL(Android接口定義語言)執(zhí)行所有的工作举瑰,將對象分解成操作系統(tǒng)可以理解的原語捣辆,并在進(jìn)程間編組它們以執(zhí)行IPC。前面的技術(shù)使用了Messenger,這也是基于AIDL作為它的底層結(jié)構(gòu)此迅。像上面提到的汽畴,Messenger在單個線程中給所有的客戶端請求創(chuàng)建了一個隊(duì)列,所以service同一時間只能接收到一個請求耸序。如果你想要你的service可以同時處理多個請求整袁,你可以直接使用AIDL。這時候佑吝,你的service必須能支持多線程且是線程安全的。
要直接使用AIDL绳匀,您必須創(chuàng)建一個定義編程接口的.aidl文件芋忿。Android SDK工具使用此文件生成一個抽象類,該類實(shí)現(xiàn)接口并處理IPC疾棵,然后可以在你的service中繼承它戈钢。
注意:大多數(shù)的應(yīng)用不需要通過AIDL來創(chuàng)建一個bound service。因?yàn)檫@需要支持多線程且導(dǎo)致更加復(fù)雜的實(shí)現(xiàn)是尔。因此殉了,AIDL不適合大多數(shù)的應(yīng)用,這篇文章也不會介紹如何給service使用它拟枚,如果你確定要直接使用AIDL薪铜,請查看專門的AIDL文章众弓。
繼承Binder類
如果你的service只在本應(yīng)用中使用,不需要跨進(jìn)程工作隔箍,那么你可以實(shí)現(xiàn)你自己的IBinder來讓你的客戶端直接訪問service中的公開方法谓娃。
注意:這只有在service和client在同一個進(jìn)程中才有效。
以下是設(shè)置方法:
-
在你的service中蜒滩,用以下方法中的一個來創(chuàng)建IBinder實(shí)例:
- 包含client可以調(diào)用的公開方法
- 返回當(dāng)前的service實(shí)例滨达,該service包含client可以調(diào)用的公開方法
- 返回一個service持有的對象,client可以訪問該對象的公開方法
在
onBind
回調(diào)方法中返回該IBinder
的實(shí)例俯艰。在client中捡遍,接受從
onServiceConnected
回調(diào)方法中傳來的IBinder
,然后可以通過提供的方法調(diào)用service竹握。
說明:service和client必須在同一個線程中的原因是client可以將返回的對象轉(zhuǎn)型并正確的調(diào)用它的API画株。服務(wù)和客戶端也必須處于同一個進(jìn)程中,因?yàn)榇思夹g(shù)不會跨進(jìn)程執(zhí)行任何編組涩搓。
下面有個例子污秆,這個service通過實(shí)現(xiàn)Binder來讓client可以訪問service的方法:
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
方法給client來獲取當(dāng)前LocalService
的實(shí)例。這將允許client調(diào)用service的公開方法昧甘。例如client可以調(diào)用service的getRandomNumber
方法良拼。
下面是一個activity與LocalService
綁定并在按鈕被點(diǎn)擊的時候調(diào)用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;
}
};
}
使用Messenger
如果你的service需要和遠(yuǎn)程進(jìn)程通信,你可以使用一個Messenger來給你的service提供接口充边。這項(xiàng)技術(shù)可以讓你不使用AIDL就能進(jìn)行IPC庸推。
以下是如何使用Messenger的摘要:
- 該service實(shí)現(xiàn)了一個handler來接受來自client的每個調(diào)用的回調(diào)。
- Messenger創(chuàng)建了一個IBinder浇冰,service將該IBinder通過onBinder方法返回給client贬媒。
- 客戶端使用IBinder實(shí)例化Messenger(引用服務(wù)的Handler),客戶端用它將Message對象發(fā)送到service肘习。
- service在它的Handler中接收到每個Message际乘,明確的說,在handleMessage方法中漂佩。
以這種方式脖含,client在service上沒有方法可以調(diào)用,取而代之的是投蝉,client可以發(fā)送信息养葵,service可以在Handler中接受信息。
以下是一個service使用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();
}
}
client需要做的就是使用service返回的IBinder
創(chuàng)建一個Messenger
并且使用它的send
方法發(fā)送信息瘩缆。下面是一個activity綁定到上面service并發(fā)送信息給該service的例子:
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;
}
}
}
注意关拒,這個例子沒有展示service如何對client做出回應(yīng)。如果你想要你的service做出回應(yīng),那么你也需要在client中創(chuàng)建一個Messenger
着绊。然后當(dāng)client接收到onServiceConnected
回調(diào)的時候谐算,client發(fā)送了一條Message
給service,這條Message
在它的reply
參數(shù)中包含了client的Messenger
畔柔。
綁定一個service
應(yīng)用組件(client)可以 通過調(diào)用bindService
來和一個service進(jìn)行綁定氯夷。Android系統(tǒng)會調(diào)用service的OnBind
方法,該方法返回一個可以與service交互的IBinder
靶擦。
綁定是異步的腮考,bindSercice
會立刻返回但是不會返回IBinder
給client,client必須創(chuàng)建一個ServiceConnection
實(shí)例并將它傳遞給bindService
玄捕。ServiceConnection
包含了一個方法踩蔚,系統(tǒng)會調(diào)用這個方法傳遞IBinder
。
說明:只有activity枚粘,service和content provider可以和service綁定-你不能將broadcast receiver和service綁定馅闽。
所以,為了能后綁定service馍迄,你必須要做到:
- 實(shí)現(xiàn)ServiceConnection
你的實(shí)現(xiàn)必須重寫兩個方法:
onServiceConnected()
系統(tǒng)調(diào)用這個方法來傳送從onBind
方法返回的IBinder
給clientonServiceDisconnected()
Android系統(tǒng)會在client與service的連接異常斷開的時候(例如service崩潰或者被殺死)調(diào)用該方法福也。client解綁的時候不會調(diào)用該方法。
調(diào)用bindService,傳遞ServiceConnection的實(shí)現(xiàn)攀圈。
當(dāng)系統(tǒng)調(diào)用onServiceConnected回調(diào)方法時暴凑,你可以使用接口定義的方法開始調(diào)用service。
調(diào)用unBindService來解綁赘来。
當(dāng)client銷毀的時候现喳,會與service解綁。但是在你與service交互完成之后或者在activity pause的時候犬辰,你應(yīng)該解綁嗦篱。以便service可以在不使用的時候關(guān)閉。
下面的例子連接了client和一個通過繼承Binder對象來創(chuàng)建的service幌缝。所有要做的就是將返回的IBinder轉(zhuǎn)型為LocalBinder且獲取LocalService的實(shí)例灸促。
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;
}
};
client將該ServiceConnection
傳遞給bindService
來使用它。例如:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService()
的第一個參數(shù)是一個Intent
涵卵,它明確地命名要綁定的服務(wù)(該intent可能是隱式的)浴栽。
第二個參數(shù)是ServiceConnetction
對象
第三個參數(shù)是一個指示綁定選項(xiàng)的標(biāo)志。它通常應(yīng)該是BIND_AUTO_CREATE
以便于在service沒有存活的時候創(chuàng)建它缘厢。其余可能的值為BIND_DEBUG_UNBIND
、BIND_NOT_FOREGROUND
或者0表示沒有甩挫。
附加說明:
以下有一些關(guān)于綁定service的重要說明:
你應(yīng)該總是捕獲DeadObjectException贴硫,這會在連接被打斷的時候拋出。這是遠(yuǎn)程方法唯一拋出的一個異常。
對象是跨進(jìn)程引用計(jì)數(shù)的
你通常應(yīng)該將綁定和解綁與對應(yīng)的client的生命周期中的創(chuàng)建和銷毀對應(yīng)起來英遭。例如:
- 如果你想你的activity在可見的時候與service交互间护,你應(yīng)該在onSatrt期間綁定在onStop期間解綁。
- 如果你想你的activity在stop的時候仍然可也接受到回應(yīng)挖诸,那么你可以在onCreate中綁定汁尺,在onDestroy中解綁。注意多律,這意味著你的activity需要在整個生命周期中使用service(即使是在后臺)痴突,如果該service是在另一個進(jìn)程。你增加了該進(jìn)程的負(fù)擔(dān)狼荞,系統(tǒng)更有可能殺死它辽装。
在activity的
onResume
和onPause
期間,通常不應(yīng)該綁定和解除綁定相味,因?yàn)檫@些回調(diào)會在每個生命周期轉(zhuǎn)換時發(fā)生拾积,并且應(yīng)該將在這些轉(zhuǎn)換時發(fā)生的處理保持在最低限度。 另外丰涉,如果應(yīng)用程序中的多個activity綁定到相同的service拓巧,并且這兩個活動之間存在轉(zhuǎn)換,則可能會因?yàn)楫?dāng)前activity在下一個activity綁定(在resume期間)之前解除綁定(在pause期間)而被破壞并重新創(chuàng)建一死。
管理bound service的生命周期
如果一個service被所有的組件解綁肛度,那么Android系統(tǒng)就會銷毀它(除非它也被onStartCommand
啟動了)。因此摘符,如果你的service是一個單純的bound service贤斜,那么你不用管理它的生命周期-Android系統(tǒng)會根據(jù)是否有client與之綁定來為你管理。
然而逛裤,如果你選擇了實(shí)現(xiàn)onSatrtCommand
回調(diào)方法瘩绒,那么你必須明確的停止service,因?yàn)楝F(xiàn)在該service被認(rèn)為是started带族。這種情況下锁荔,該service會一直運(yùn)行到自己調(diào)用stopSelf
或者其余組件調(diào)用stopService
來停止它,而不管它是否與所有的client解綁蝙砌。
除此之外阳堕,如果你的service是started且接受綁定,那么當(dāng)系統(tǒng)調(diào)用onUnbind方法的時候择克,你可選擇返回true恬总,如果你想在下次一個client與這個service綁定的時候接受到一個onRebind
的回調(diào)。onRebind
返回void肚邢,但是client還是會在onServiceConnected
回調(diào)中接收到IBinder
壹堰。下面的圖說明了這種生命周期的邏輯: