繫結服務是指用戶端與伺服器介面中的伺服器。Kubernetes 可讓您 例如繫結至服務的活動、傳送要求、接收回應,以及執行 處理序間通訊 (IPC)綁定服務通常只能在服務中 應用程式元件,而且不會無限期在背景中執行。
本文件說明如何建立繫結服務,包括如何繫結服務 將其他應用程式元件轉移至服務如要進一步瞭解 Google Cloud Platform 中的 例如如何傳送服務通知及設定執行服務 在前景執行,請參閱 服務總覽。
基本概念
繫結服務是 Service
類別的實作,
與其他應用程式繫結並與其互動如要為
即可實作 onBind()
回呼方法。這個
方法會傳回 IBinder
物件,定義要搜尋的程式設計介面
用戶端都可以使用此服務與服務互動。
繫結至已啟動的服務
如服務總覽中所述,
您可以建立已啟動和繫結的服務也就是說,您可以發起
呼叫 startService()
來允許
就會無限期執行您也可以透過下列方式,使用戶端繫結至服務:
正在撥打 bindService()
。
如果您讓系統啟動並繫結服務,則服務啟動時。
所有用戶端解除繫結時,系統「不會」刪除服務。
相反地
呼叫 stopSelf()
或 stopService()
即可明確停止服務。
雖然您通常會導入onBind()
或 onStartCommand()
,
將
兩者都先完成舉例來說,音樂播放器可能覺得讓服務執行
也會無限期提供繫結。如此一來,活動就能啟動服務,
就算使用者退出應用程式,音樂仍會繼續播放。之後當使用者
返回應用程式,該活動可以繫結至服務,重新取得
播放。
如要進一步瞭解將服務生命週期新增至已啟動服務時的生命週期, 請參閱「管理繫結服務的生命週期」一節。
用戶端會透過
bindService()
。執行時
提供 ServiceConnection
的實作,
監控與服務的連線傳回值
bindService()
會指出
要求的服務存在,以及客戶是否獲準存取該服務。
時間
Android 系統會在用戶端與服務之間建立連線
致電「onServiceConnected()
」
ServiceConnection
。
onServiceConnected()
方法包含 IBinder
引數,接著用戶端會使用此引數與繫結的服務通訊。
您可以將多個用戶端同時連上服務。不過,
系統會快取 IBinder
服務通訊管道。
換句話說,系統會呼叫服務的 onBind()
產生 IBinder
的這個方法
用戶端繫結。接著,系統會把相同的 IBinder
提供給
繫結至同一項服務的所有額外用戶端,不必呼叫
onBind()
。
當最後一個用戶端與服務解除繫結時,系統會刪除服務,除非
已使用「startService()
」開始使用服務。
實作繫結服務最重要的部分是定義介面
onBind()
回呼方法傳回的字串。下列
一節將討論定義 Service 的多種方式
IBinder
介面。
建立繫結服務
建立提供繫結的服務時,您必須提供 IBinder
提供程式設計介面,供用戶端用於與服務互動。有
定義介面的方法有三種:
- 擴充 Binder 類別
- 如果服務僅供您自己的應用程式使用,且可在同一個程序中執行
做為用戶端,如要建立介面,請擴充
Binder
類別 並傳回該物件的例項onBind()
。用戶端收到Binder
和 可用來直接存取Binder
中可用的公開方法 實作或Service
如果您的服務只是自己的背景工作站,建議您使用這個方法 應用程式。唯一的用途是建立介面時不是建議做法 您的服務供其他應用程式或不同程序使用。
- 使用 Messenger
- 如果你需要讓介面在不同程序中執行,可以建立
含有
Messenger
的服務介面。在此方法中 定義用於回應不同Message
物件類型的Handler
。此
Handler
是Messenger
的基礎,之後可共用IBinder
用戶端,讓用戶端使用Message
物件傳送指令給服務。此外,用戶端可以定義Messenger
如此服務才能傳回訊息這是執行處理序間通訊 (IPC) 最簡單的方法,因為
Messenger
會將所有要求排入單一執行緒中,讓您無需設計。 確保服務符合執行緒安全要求 - 使用 AIDL
- Android 介面定義語言 (AIDL) 會將物件分解為
作業系統可瞭解這些基本功能,並組織在不同程序中執行的組合
處理序間通訊 (IPC)上一個技巧使用
Messenger
實際上是以 AIDL 為基礎, 模型的底層結構如上一節所述,
Messenger
會建立 單一執行緒中的所有用戶端要求,因此服務會一次接收一個要求。如果 但如果希望您的服務同時處理多個要求,就可以使用 AIDL 在這種情況下,您的服務必須是執行緒安全的,且支援多執行緒。如要直接使用 AIDL, 建立定義程式設計介面的
.aidl
檔案。Android SDK 工具會使用 這個檔案會產生一個抽象類別,用來實作介面並處理處理序間通訊 (IPC) 即可擴充您的服務
注意:就大多數應用程式而言,AIDL 並非最佳選擇 就會建立繫結服務,因為可能需要多執行緒功能,並 導致實作上更複雜的因此,本文件不會討論 以便在服務中使用如果您確信 如要直接使用 AIDL,請參閱 AIDL 文件。
擴充 Binder 類別
如果只有本機應用程式使用您的服務,且不需要
如何跨程序運作
接著,您便可實作自己的 Binder
類別,以便直接提供用戶端
可在服務中存取公開方法
注意:只有在用戶端和服務都是相同的時,才能使用這種做法 最常見的用途舉例來說,在音樂情境中 應用程式,必須將活動繫結至服務,而該服務會在 背景。
設定方式如下:
- 在服務中,建立具有此需求的
Binder
執行個體 下列其中一項:- 包含用戶端可呼叫的公開方法。
- 傳回目前的
Service
執行個體,這個例項具有 呼叫。 - 傳回由服務代管的其他類別的例項,其公用方法為 呼叫。
- 從
onBind()
回呼方法傳回這個Binder
例項。 - 在用戶端中,接收來自
onServiceConnected()
回呼方法的Binder
,並 使用提供的方法呼叫繫結的服務。
注意:服務和用戶端必須相同 應用程式,讓用戶端可以投放傳回的物件,並正確呼叫其 API。 「服務」 和用戶端必須在同一個程序進行,因為這項技術不會執行任何 跨程序進行管理
舉例來說,以下服務可讓用戶端透過
Binder
實作方式:
Kotlin
class LocalService : Service() { // Binder given to clients. private val binder = LocalBinder() // Random number generator. private val mGenerator = Random() /** Method for clients. */ val randomNumber: Int get() = mGenerator.nextInt(100) /** * 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. */ inner class LocalBinder : Binder() { // Return this instance of LocalService so clients can call public methods. fun getService(): LocalService = this@LocalService } override fun onBind(intent: Intent): IBinder { return binder } }
Java
public class LocalService extends Service { // Binder given to clients. private final IBinder binder = 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 binder; } /** Method for clients. */ public int getRandomNumber() { return mGenerator.nextInt(100); } }
LocalBinder
提供 getService()
方法,可讓用戶端擷取
目前的 LocalService
執行個體。這樣一來,用戶端就能在
課程中也會快速介紹 Memorystore
這是 Google Cloud 的全代管 Redis 服務舉例來說,用戶端可以從服務呼叫 getRandomNumber()
。
以下活動會繫結至 LocalService
並呼叫 getRandomNumber()
使用者按一下按鈕時:
Kotlin
class BindingActivity : Activity() { private lateinit var mService: LocalService private var mBound: Boolean = false /** Defines callbacks for service binding, passed to bindService(). */ private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // We've bound to LocalService, cast the IBinder and get LocalService instance. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } override fun onServiceDisconnected(arg0: ComponentName) { mBound = false } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to LocalService. Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() unbindService(connection) mBound = false } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute). */ fun onButtonClick(v: View) { if (mBound) { // Call a method from the LocalService. // However, if this call is something that might hang, then put this request // in a separate thread to avoid slowing down the activity performance. val num: Int = mService.randomNumber Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show() } } }
Java
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, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); unbindService(connection); 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 is something that might hang, then put this request // 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 connection = 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
和 onServiceConnected()
回呼。下一個
一節將進一步說明這項繫結至服務的程序。
注意:在上述範例中,
onStop()
方法會將用戶端與服務解除繫結。
如
其他注意事項:
如需更多程式碼範例,請參閱
LocalService.java
類別和
ApiDemos 中的 LocalServiceActivities.java
類別。
使用 Messenger
如果您需要服務與遠端程序通訊,可以使用
Messenger
為服務提供介面。這項技巧讓
可以執行處理序間通訊 (IPC),而不必使用 AIDL。
使用 Messenger
做為介面的做法如下:
比使用 AIDL 更容易,因為 Messenger
佇列
所有呼叫都會傳送至服務。純 AIDL 介面會同時將並行要求傳送至
因此必須處理多執行緒
大多數應用程式不需要執行多執行緒,因此使用 Messenger
可讓服務一次處理一個呼叫。重要性
您需要使用 AIDL 定義介面。
以下摘要說明如何使用 Messenger
:
- 服務會實作
Handler
,針對每個項目接收回呼 呼叫。 - 服務使用
Handler
建立Messenger
物體 (這是Handler
的參照)。 Messenger
會建立IBinder
提供給服務 會傳回onBind()
。- 用戶端會使用
IBinder
將Messenger
例項化 (參照服務的Handler
),供用戶端Message
物件新增至服務。 - 服務會在其
Handler
中收到每個Message
,尤其是在handleMessage()
方法中。
如此一來,用戶端就無法在服務上呼叫方法。相反地
用戶端會傳送訊息 (Message
物件) 給服務
收到
其 Handler
。
以下是使用 Messenger
介面的簡易服務範例:
Kotlin
/** Command to the service to display a message. */ private const val MSG_SAY_HELLO = 1 class MessengerService : Service() { /** * Target we publish for clients to send messages to IncomingHandler. */ private lateinit var mMessenger: Messenger /** * Handler of incoming messages from clients. */ internal class IncomingHandler( context: Context, private val applicationContext: Context = context.applicationContext ) : Handler() { override fun handleMessage(msg: Message) { when (msg.what) { MSG_SAY_HELLO -> Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show() else -> super.handleMessage(msg) } } } /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ override fun onBind(intent: Intent): IBinder? { Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show() mMessenger = Messenger(IncomingHandler(this)) return mMessenger.binder } }
Java
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. */ static class IncomingHandler extends Handler { private Context applicationContext; IncomingHandler(Context context) { applicationContext = context.getApplicationContext(); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ Messenger mMessenger; /** * 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(); mMessenger = new Messenger(new IncomingHandler(this)); return mMessenger.getBinder(); } }
字串中的 handleMessage()
方法
Handler
是服務接收傳入的 Message
的位置
並根據 what
成員決定要採取的行動。
用戶端只需根據服務傳回的 IBinder
建立 Messenger
,並使用 send()
傳送訊息。例如,以下是繫結至
服務並將 MSG_SAY_HELLO
訊息傳送至服務:
Kotlin
class ActivityMessenger : Activity() { /** Messenger for communicating with the service. */ private var mService: Messenger? = null /** Flag indicating whether we have called bind on the service. */ private var bound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // 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 = Messenger(service) bound = true } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service has been // unexpectedly disconnected—that is, its process crashed. mService = null bound = false } } fun sayHello(v: View) { if (!bound) return // Create and send a message to the service, using a supported 'what' value. val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0) try { mService?.send(msg) } catch (e: RemoteException) { e.printStackTrace() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to the service. Intent(this, MessengerService::class.java).also { intent -> bindService(intent, mConnection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() // Unbind from the service. if (bound) { unbindService(mConnection) bound = false } } }
Java
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 bound; /** * 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); bound = 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; bound = false; } }; public void sayHello(View v) { if (!bound) 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 (bound) { unbindService(mConnection); bound = false; } } }
這個範例並未說明服務如何回應用戶端。
如果您希望
您必須在用戶端中建立 Messenger
。
用戶端收到 onServiceConnected()
回呼時,會將 Message
傳送至包含
replyTo
參數中用戶端的 Messenger
send()
方法的一部分
您可以在
MessengerService.java
(服務) 和
MessengerServiceActivities.java
(用戶端) 樣本。
繫結至服務
應用程式元件 (用戶端) 可透過呼叫
bindService()
。Android
系統會呼叫服務的 onBind()
方法,此方法會傳回 IBinder
以便與
。
繫結為非同步性質,且 bindService()
會立即傳回「不」將 IBinder
傳回給
用戶端。如要接收 IBinder
,用戶端必須建立
ServiceConnection
的例項並傳遞至 bindService()
。ServiceConnection
包含一個回呼方法,
進行系統呼叫來傳遞 IBinder
。
注意:只有活動、服務和內容供應者可以繫結 - 您「無法」從廣播接收器繫結至服務。
如要從用戶端繫結至服務,請按照下列步驟操作:
- 導入
ServiceConnection
。您的實作必須覆寫兩種回呼方法:
onServiceConnected()
- 系統會呼叫此方法來傳遞
IBinder
服務的onBind()
方法。 onServiceDisconnected()
- Android 系統會在與非預期連線的情況下呼叫此方法 例如服務當機或終止時這不是 呼叫 用戶端解除繫結。
- 呼叫
bindService()
並傳遞ServiceConnection
實作。注意:如果這個方法傳回 false,表示 用戶端與服務之間的有效連線。不過,請務必撥打
unbindService()
在用戶端內取得)。否則,您的用戶端 並在閒置時關閉。 - 系統呼叫
onServiceConnected()
回呼方法時,您可以使用 介面定義的方法。 - 如要中斷與該服務的連線,請呼叫
unbindService()
。在應用程式刪除用戶端時,如果用戶端仍繫結至服務,請刪除 會導致用戶端解除繫結。建議您在完成後立即解除繫結 才能與服務互動這麼做可讓閒置服務關閉。如要進一步瞭解 以及進行繫結和解除繫結的適當時間,請參閱「其他附註」一節。
以下範例將用戶端連線至
擴充 Binder 類別,所以只需投放傳回的
將 IBinder
傳送至 LocalBinder
類別,並要求 LocalService
例項:
Kotlin
var mService: LocalService val mConnection = object : ServiceConnection { // Called when the connection with the service is established. override fun onServiceConnected(className: ComponentName, service: IBinder) { // 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. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } // Called when the connection with the service disconnects unexpectedly. override fun onServiceDisconnected(className: ComponentName) { Log.e(TAG, "onServiceDisconnected") mBound = false } }
Java
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()
,如以下範例所示:
Kotlin
Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) }
Java
Intent intent = new Intent(this, LocalService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE);
bindService()
的第一個參數是Intent
,可明確命名要繫結服務。注意:如果您使用意圖繫結至
Service
。請使用明確政策,確保應用程式安全無虞 意圖。使用隱含意圖啟動服務是 安全性風險,因為您無法確定哪個服務會回應意圖 導致使用者看不到哪個服務啟動了。從 Android 5.0 (API 級別 21) 開始。 系統 若呼叫bindService()
,就會擲回例外狀況 針對隱含意圖- 第二個參數是
ServiceConnection
物件。 - 第三個參數是指出繫結選項的旗標 (通常是
BIND_AUTO_CREATE
),如果還未建立服務,則用於建立服務 仍處於運作狀態。 其他可能的值包括BIND_DEBUG_UNBIND
、BIND_NOT_FOREGROUND
,或0
表示無。
其他注意事項
以下是繫結至服務的重要注意事項:
- 一律忽略擲回的
DeadObjectException
例外狀況 連線中斷時。這是遠端方法擲回的唯一例外狀況。 - 物件會跨程序計算的參考資料。
- 您通常會在變更期間
用戶端生命週期的比對接軌和拆除時刻,如
範例:
- 如果您只需要在顯示活動時與服務互動,請在
onStart()
期間繫結並在onStop()
期間解除繫結。 - 如果您希望活動在以下位置停止時也收到回應:
背景,在
onCreate()
期間繫結後取消繫結 期間onDestroy()
。請注意,這意味著 活動需要全程使用服務,即使是在背景運作, 服務正在其他流程中,您會增加該程序的權重 系統終止攻擊的可能性
注意:您「不會」建立繫結和解除繫結 發生在活動的
onResume()
和onPause()
回呼期間,因為系統會在每次 生命週期轉換。 請盡量減少在這些轉換期間執行的處理程序。此外,如果 應用程式中的多項活動會繫結至同一項服務, 轉場效果 其中一個活動會遭到刪除,重新建立服務後,將成為 解除活動繫結 (暫停期間)。這項活動會 如需活動安排其生命週期的說明,請參閱「活動生命週期」。
- 如果您只需要在顯示活動時與服務互動,請在
如需示範如何繫結至服務的程式碼範例,請參閱
RemoteService.java
類別 (位於 ApiDemos 中)。
管理繫結服務的生命週期
取消服務與所有用戶端的繫結時,Android 系統會刪除該服務
(除非一開始使用
startService()
)。
因此,如果服務
服務只有一項繫結的服務Android 系統會為您管理
是否有繫結至任何用戶端。
不過,如果您選擇實作 onStartCommand()
回呼方法,就必須明確停止服務,因為
服務就會視為「啟動」。在本範例中,服務會持續運作到服務結束為止
透過 stopSelf()
或其他元件呼叫 stopService()
時,不論是否繫結至任何
用戶端。
此外,如果您的服務已經啟動並接受繫結,則系統呼叫
onUnbind()
方法,您可以選擇傳回
true
:onRebind()
onRebind()
傳回無效,但用戶端仍會在其IBinder
onServiceConnected()
回呼。
下圖說明這種生命週期的邏輯。
![](https://developer.android.com/static/images/fundamentals/service_binding_tree_lifecycle.png?authuser=0000&hl=zh-tw)
圖 1. 啟動服務的生命週期 並允許繫結
如要進一步瞭解已啟動服務的生命週期,請參閱服務總覽。