Ein gebundener Dienst ist der Server in einer Client-Server-Schnittstelle. Damit können Komponenten wie Aktivitäten an den Dienst gebunden werden, Anfragen senden, Antworten empfangen und Interprocess Communication (IPC) ausführen. Ein gebundener Dienst ist in der Regel nur so lange aktiv, wie er eine andere Anwendungskomponente bedient, und wird nicht unbegrenzt im Hintergrund ausgeführt.
In diesem Dokument wird beschrieben, wie Sie einen gebundenen Dienst erstellen und wie Sie von anderen Anwendungskomponenten aus eine Bindung an den Dienst herstellen. Weitere Informationen zu Diensten im Allgemeinen, z. B. wie Benachrichtigungen von einem Dienst gesendet werden und wie der Dienst im Vordergrund ausgeführt wird, finden Sie in der Übersicht über Dienste.
Grundlagen
Ein gebundener Dienst ist eine Implementierung der Klasse Service, die es anderen Anwendungen ermöglicht, sich an ihn zu binden und mit ihm zu interagieren. Wenn Sie eine Bindung für einen Dienst bereitstellen möchten, implementieren Sie die Callback-Methode onBind(). Diese Methode gibt ein IBinder-Objekt zurück, das die Programmierschnittstelle definiert, über die Clients mit dem Dienst interagieren können.
An einen gestarteten Dienst binden
Wie in der Übersicht über Dienste beschrieben, können Sie einen Dienst erstellen, der sowohl gestartet als auch gebunden ist. Sie können einen Dienst starten, indem Sie startService() aufrufen. Dadurch wird der Dienst unbegrenzt ausgeführt. Sie können auch zulassen, dass ein Client durch Aufrufen von bindService() an den Dienst gebunden wird.
Wenn Sie zulassen, dass Ihr Dienst gestartet und gebunden wird, wird der Dienst beim Starten nicht vom System beendet, wenn alle Clients die Bindung aufheben.
Stattdessen müssen Sie den Dienst explizit beenden, indem Sie stopSelf() oder stopService() aufrufen.
In der Regel implementieren Sie entweder onBind()
oder onStartCommand(), manchmal ist es jedoch erforderlich, beide zu implementieren. Bei einem Musikplayer kann es beispielsweise nützlich sein, den Dienst unbegrenzt laufen zu lassen und auch eine Bindung bereitzustellen. So kann eine Aktivität den Dienst starten, um Musik abzuspielen, und die Musik wird auch dann weiter abgespielt, wenn der Nutzer die Anwendung verlässt. Wenn der Nutzer dann zur Anwendung zurückkehrt, kann die Aktivität an den Dienst gebunden werden, um die Wiedergabe wieder zu steuern.
Weitere Informationen zum Dienstlebenszyklus beim Hinzufügen einer Bindung zu einem gestarteten Dienst finden Sie im Abschnitt Lebenszyklus eines gebundenen Dienstes verwalten.
Ein Client bindet sich an einen Dienst, indem er bindService() aufruft. In diesem Fall muss sie eine Implementierung von ServiceConnection bereitstellen, die die Verbindung mit dem Dienst überwacht. Der Rückgabewert von bindService() gibt an, ob der angeforderte Dienst vorhanden ist und ob der Client darauf zugreifen darf.
Wenn das Android-System die Verbindung zwischen dem Client und dem Dienst herstellt, ruft es onServiceConnected() für ServiceConnection auf. Die Methode onServiceConnected() enthält ein IBinder-Argument, das der Client dann für die Kommunikation mit dem gebundenen Dienst verwendet.
Sie können mehrere Clients gleichzeitig mit einem Dienst verbinden. Das System speichert jedoch den IBinder-Dienstkommunikationskanal im Cache.
Das System ruft also die Methode onBind() des Dienstes auf, um den IBinder nur dann zu generieren, wenn der erste Client eine Bindung herstellt. Das System liefert dann dieselbe IBinder an alle zusätzlichen Clients, die an denselben Dienst gebunden sind, ohne onBind() noch einmal aufzurufen.
Wenn der letzte Client die Bindung zum Dienst aufhebt, wird der Dienst vom System gelöscht, es sei denn, er wurde mit startService() gestartet.
Der wichtigste Teil der Implementierung eines gebundenen Dienstes ist die Definition der Schnittstelle, die von der Callback-Methode onBind() zurückgegeben wird. Im folgenden Abschnitt werden verschiedene Möglichkeiten zum Definieren der IBinder-Schnittstelle Ihres Dienstes erläutert.
Gebundenen Dienst erstellen
Wenn Sie einen Dienst erstellen, der eine Bindung bereitstellt, müssen Sie eine IBinder angeben, die die Programmierschnittstelle bereitstellt, über die Clients mit dem Dienst interagieren können. Es gibt drei Möglichkeiten, die Schnittstelle zu definieren:
- Binder-Klasse erweitern
- Wenn Ihr Dienst nur für Ihre eigene Anwendung bestimmt ist und im selben Prozess wie der Client ausgeführt wird, was häufig der Fall ist, erstellen Sie Ihre Schnittstelle, indem Sie die Klasse
Bindererweitern und eine Instanz davon vononBind()zurückgeben. Der Client empfängt dieBinderund kann damit direkt auf öffentliche Methoden zugreifen, die entweder in derBinder-Implementierung oder in derServiceverfügbar sind.Dies ist die bevorzugte Technik, wenn Ihr Dienst lediglich ein Hintergrund-Worker für Ihre eigene Anwendung ist. Der einzige Anwendungsfall, in dem dies nicht die bevorzugte Methode zum Erstellen Ihrer Schnittstelle ist, ist, wenn Ihr Dienst von anderen Anwendungen oder über separate Prozesse verwendet wird.
- Messenger verwenden
- Wenn Ihre Schnittstelle prozessübergreifend funktionieren soll, können Sie eine Schnittstelle für den Dienst mit einem
Messengererstellen. Auf diese Weise definiert der Dienst einenHandler, der auf verschiedene Arten vonMessage-Objekten reagiert.Dieser
Handlerist die Grundlage für einenMessenger, der dann einenIBindermit dem Client teilen kann, sodass der Client mithilfe vonMessage-Objekten Befehle an den Dienst senden kann. Außerdem kann der Client einen eigenenMessengerdefinieren, damit der Dienst Nachrichten zurücksenden kann.Dies ist die einfachste Möglichkeit, Interprocess Communication (IPC) durchzuführen, da der
Messengeralle Anfragen in einem einzigen Thread in die Warteschlange stellt. Du musst deinen Dienst also nicht threadsicher gestalten. - AIDL verwenden
- Die Android Interface-Definitionssprache (AIDL) zerlegt Objekte in Primitive, die das Betriebssystem verstehen kann, und marshallt sie über Prozesse hinweg, um IPC auszuführen. Die vorherige Technik, bei der ein
Messengerverwendet wird, basiert auf AIDL als zugrunde liegender Struktur.Wie im vorherigen Abschnitt erwähnt, erstellt der
Messengereine Warteschlange aller Clientanfragen in einem einzelnen Thread, sodass der Dienst die Anfragen nacheinander empfängt. Wenn Ihr Dienst jedoch mehrere Anfragen gleichzeitig verarbeiten soll, können Sie AIDL direkt verwenden. In diesem Fall muss Ihr Dienst threadsicher und multithreadingfähig sein.Wenn Sie AIDL direkt verwenden möchten, erstellen Sie eine
.aidl-Datei, die die Programmierschnittstelle definiert. Die Android SDK-Tools verwenden diese Datei, um eine abstrakte Klasse zu generieren, die die Schnittstelle implementiert und die IPC verarbeitet. Diese Klasse können Sie dann in Ihrem Dienst erweitern.
Hinweis:Für die meisten Anwendungen ist AIDL nicht die beste Wahl, um einen gebundenen Dienst zu erstellen, da möglicherweise Multithreading-Funktionen erforderlich sind und die Implementierung komplizierter sein kann. In diesem Dokument wird daher nicht erläutert, wie Sie es für Ihren Dienst verwenden. Wenn du dir sicher bist, dass du AIDL direkt verwenden musst, lies das AIDL-Dokument.
Binder-Klasse erweitern
Wenn nur die lokale Anwendung Ihren Dienst verwendet und er nicht prozessübergreifend funktionieren muss, können Sie eine eigene Binder-Klasse implementieren, die Ihrem Client direkten Zugriff auf öffentliche Methoden im Dienst bietet.
Hinweis: Dies funktioniert nur, wenn sich Client und Dienst in derselben Anwendung und demselben Prozess befinden, was am häufigsten der Fall ist. Das funktioniert beispielsweise gut für eine Musik-App, die eine Aktivität an einen eigenen Dienst binden muss, der Musik im Hintergrund abspielt.
Und so funktioniert es:
- Erstellen Sie in Ihrem Dienst eine Instanz von
Binder, die eine der folgenden Aktionen ausführt:- Enthält öffentliche Methoden, die der Client aufrufen kann.
- Gibt die aktuelle
Service-Instanz zurück, die öffentliche Methoden hat, die der Client aufrufen kann. - Gibt eine Instanz einer anderen Klasse zurück, die vom Dienst gehostet wird, mit öffentlichen Methoden, die der Client aufrufen kann.
- Gib diese Instanz von
Binderüber die Callback-MethodeonBind()zurück. - Empfange im Client die
Binderüber die Callback-MethodeonServiceConnected()und rufe den gebundenen Dienst mit den bereitgestellten Methoden auf.
Hinweis: Der Dienst und der Client müssen sich in derselben Anwendung befinden, damit der Client das zurückgegebene Objekt umwandeln und seine APIs ordnungsgemäß aufrufen kann. Der Dienst und der Client müssen sich außerdem im selben Prozess befinden, da diese Technik kein prozessübergreifendes Marshalling durchführt.
Hier ist ein Beispiel für einen Dienst, der Clients über eine Binder-Implementierung Zugriff auf Methoden im Dienst gewährt:
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 stellt die Methode getService() bereit, mit der Clients die aktuelle Instanz von LocalService abrufen können. Dadurch können Clients öffentliche Methoden im Dienst aufrufen. Clients können beispielsweise getRandomNumber() über den Dienst aufrufen.
Hier ist eine Aktivität, die an LocalService gebunden ist und getRandomNumber() aufruft, wenn auf einen Button geklickt wird:
Kotlin
class BindingActivity : Activity() { private var mService: LocalService? = null 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) { mService = null 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) // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null 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 ?: return 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); // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null; 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) { mService = null; mBound = false; } }; }
Das vorherige Beispiel zeigt, wie der Client mithilfe einer Implementierung von ServiceConnection und dem Callback onServiceConnected() an den Dienst gebunden wird. Im nächsten Abschnitt finden Sie weitere Informationen zu diesem Prozess der Bindung an den Dienst.
Hinweis: Im vorherigen Beispiel wird die Bindung des Clients an den Dienst mit der Methode onStop() aufgehoben und der Verweis des Clients auf den Dienst freigegeben.
Trennen Sie die Bindung von Clients an Dienste zu geeigneten Zeiten, wie im Abschnitt Zusätzliche Hinweise beschrieben.
Weitere Codebeispiele finden Sie in den Klassen
LocalService.java und
LocalServiceActivities.java in ApiDemos.
Messenger verwenden
Wenn Ihr Dienst mit Remote-Prozessen kommunizieren muss, können Sie einen Messenger verwenden, um die Schnittstelle für Ihren Dienst bereitzustellen. Mit dieser Technik kannst du Interprocess Communication (IPC) ausführen, ohne AIDL verwenden zu müssen.
Die Verwendung eines Messenger für Ihre Schnittstelle ist einfacher als die Verwendung von AIDL, da Messenger alle Aufrufe an den Dienst in eine Warteschlange stellt. Eine reine AIDL-Schnittstelle sendet gleichzeitige Anfragen an den Dienst, der dann Multithreading verarbeiten muss.
Bei den meisten Anwendungen muss der Dienst kein Multithreading ausführen. Wenn Sie also Messenger verwenden, kann der Dienst jeweils einen Aufruf verarbeiten. Wenn es wichtig ist, dass dein Dienst Multithreading unterstützt, verwende AIDL, um die Schnittstelle zu definieren.
Hier eine Zusammenfassung zur Verwendung eines Messenger:
- Der Dienst implementiert einen
Handler, der für jeden Aufruf von einem Client einen Callback empfängt. - Der Dienst verwendet die
Handler, um einMessenger-Objekt zu erstellen (das ein Verweis auf dieHandlerist). - Der
Messengererstellt einenIBinder, den der Dienst vononBind()an Clients zurückgibt. - Clients verwenden den
IBinder, um denMessengerzu instanziieren (der auf denHandlerdes Dienstes verweist), mit dem der ClientMessage-Objekte an den Dienst sendet. - Der Dienst empfängt jede
Messagein seinerHandler, genauer gesagt in der MethodehandleMessage().
Auf diese Weise gibt es keine Methoden, die der Client für den Dienst aufrufen kann. Stattdessen liefert der Client Nachrichten (Message-Objekte), die der Dienst in seiner Handler empfängt.
Hier ein Beispieldienst, der eine Messenger-Schnittstelle verwendet:
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(); } }
In der Methode handleMessage() im Handler empfängt der Dienst die eingehende Message und entscheidet anhand des what-Mitglieds, was zu tun ist.
Ein Client muss lediglich eine Messenger basierend auf der vom Dienst zurückgegebenen IBinder erstellen und eine Nachricht mit send() senden. Hier ist ein Beispiel für eine Aktivität, die an den Dienst gebunden wird und die MSG_SAY_HELLO-Nachricht an den Dienst sendet:
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) mService = null 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); mService = null; bound = false; } } }
In diesem Beispiel wird nicht gezeigt, wie der Dienst auf den Client reagieren kann.
Wenn der Dienst antworten soll, müssen Sie im Client auch ein Messenger erstellen.
Wenn der Client den Callback onServiceConnected() empfängt, sendet er eine Message an den Dienst, die die Messenger des Clients im Parameter replyTo der Methode send() enthält.
Ein Beispiel für die Bereitstellung von bidirektionalem Messaging finden Sie in den Beispielen
MessengerService.java (Dienst) und
MessengerServiceActivities.java (Client).
An einen Dienst binden
Anwendungskomponenten (Clients) können durch Aufrufen von bindService() an einen Dienst gebunden werden. Das Android-System ruft dann die Methode onBind() des Dienstes auf, die ein IBinder für die Interaktion mit dem Dienst zurückgibt.
Die Bindung ist asynchron und bindService() wird sofort zurückgegeben, ohne die IBinder an den Client zurückzugeben. Um die IBinder zu erhalten, muss der Client eine Instanz von ServiceConnection erstellen und an bindService() übergeben. Der ServiceConnection enthält eine Callback-Methode, die das System aufruft, um die IBinder zu übermitteln.
Hinweis:Nur Aktivitäten, Dienste und Contentanbieter können an einen Dienst gebunden werden. Sie können nicht von einem Übertragungsempfänger aus an einen Dienst binden.
So binden Sie einen Dienst von Ihrem Client aus:
ServiceConnectionimplementieren.In Ihrer Implementierung müssen zwei Callback-Methoden überschrieben werden:
onServiceConnected()- Das System ruft diese Methode auf, um die
IBinderzu liefern, die von deronBind()-Methode des Dienstes zurückgegeben wird. onServiceDisconnected()- Diese Methode wird vom Android-System aufgerufen, wenn die Verbindung zum Dienst unerwartet unterbrochen wird, z. B. wenn der Dienst abstürzt oder beendet wird. Diese Methode wird nicht aufgerufen, wenn der Client die Bindung aufhebt.
- Rufen Sie
bindService()auf und übergeben Sie dieServiceConnection-Implementierung.Hinweis:Wenn die Methode „false“ zurückgibt, hat Ihr Client keine gültige Verbindung zum Dienst. Rufen Sie jedoch
unbindService()in Ihrem Client auf. Andernfalls verhindert Ihr Client, dass der Dienst heruntergefahren wird, wenn er inaktiv ist. - Wenn das System die Callback-Methode
onServiceConnected()aufruft, können Sie mit den von der Schnittstelle definierten Methoden Aufrufe an den Dienst senden. - Wenn Sie die Verbindung zum Dienst trennen möchten, rufen Sie
unbindService()auf.Wenn der Client noch an einen Dienst gebunden ist, wenn Ihre Anwendung den Client löscht, wird die Bindung des Clients aufgehoben. Es empfiehlt sich, die Bindung des Clients aufzuheben, sobald die Interaktion mit dem Dienst abgeschlossen ist. Dadurch kann der inaktive Dienst heruntergefahren werden. Weitere Informationen zu geeigneten Zeitpunkten für das Binden und Aufheben der Bindung finden Sie im Abschnitt Zusätzliche Hinweise.
Im folgenden Beispiel wird der Client mit dem zuvor erstellten Dienst verbunden, indem die Binder-Klasse erweitert wird. Dazu muss nur der zurückgegebene IBinder in die LocalBinder-Klasse umgewandelt und die LocalService-Instanz angefordert werden:
Kotlin
var mService: LocalService? = null 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") mService = null 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"); mService = null; mBound = false; } };
Mit diesem ServiceConnection kann der Client eine Bindung an einen Dienst herstellen, indem er ihn an bindService() übergibt, wie im folgenden Beispiel gezeigt:
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);
- Der erste Parameter von
bindService()ist einIntent, der den zu bindenden Dienst explizit benennt.Achtung:Wenn du einen Intent verwendest, um eine Bindung an einen
Serviceherzustellen, muss deine App durch einen expliziten Intent geschützt sein. Die Verwendung eines impliziten Intents zum Starten eines Dienstes ist ein Sicherheitsrisiko, da Sie nicht sicher sein können, welcher Dienst auf den Intent reagiert, und der Nutzer nicht sehen kann, welcher Dienst gestartet wird. Ab Android 5.0 (API-Level 21) löst das System eine Ausnahme aus, wenn SiebindService()mit einem impliziten Intent aufrufen. - Der zweite Parameter ist das
ServiceConnection-Objekt. - Der dritte Parameter ist ein Flag, das Optionen für die Bindung angibt – normalerweise
BIND_AUTO_CREATE, um den Dienst zu erstellen, falls er noch nicht aktiv ist. Andere mögliche Werte sindBIND_DEBUG_UNBIND,BIND_NOT_FOREGROUNDoder0für „keine“.
Zusätzliche Hinweise
Hier sind einige wichtige Hinweise zur Bindung an einen Dienst:
- Fangen Sie immer
DeadObjectException-Ausnahmen ab, die ausgelöst werden, wenn die Verbindung unterbrochen wurde. Dies ist die einzige Ausnahme, die von Remote-Methoden ausgegeben wird. - Objekte werden prozessübergreifend referenziert.
- In der Regel werden die Bindung und die Aufhebung der Bindung während der entsprechenden Start- und Beendigungsmomente des Lebenszyklus des Clients gekoppelt, wie in den folgenden Beispielen beschrieben:
- Wenn Sie nur mit dem Dienst interagieren müssen, während Ihre Aktivität sichtbar ist, binden Sie ihn während
onStart()und heben Sie die Bindung währendonStop()auf. - Wenn du möchtest, dass deine Aktivität auch dann Antworten erhält, wenn sie im Hintergrund angehalten wird, binde sie während
onCreate()und hebe die Bindung währendonDestroy()auf. Beachten Sie, dass Ihre Aktivität den Dienst während der gesamten Laufzeit verwenden muss, auch im Hintergrund. Wenn sich der Dienst in einem anderen Prozess befindet, erhöhen Sie die Gewichtung des Prozesses und es ist wahrscheinlicher, dass er vom System beendet wird.
Hinweis: Normalerweise binden Sie die Aktivität nicht während der Callbacks
onResume()undonPause(), da diese Callbacks bei jedem Übergang im Lebenszyklus auftreten. Die Verarbeitung bei diesen Übergängen sollte auf ein Minimum beschränkt werden.Wenn mehrere Aktivitäten in Ihrer Anwendung an denselben Dienst gebunden sind und ein Übergang zwischen zwei dieser Aktivitäten stattfindet, wird der Dienst möglicherweise zerstört und neu erstellt, wenn die aktuelle Aktivität (während der Pause) die Bindung aufhebt, bevor die nächste Aktivität (während der Fortsetzung) die Bindung herstellt. Dieser Aktivitätsübergang, der beschreibt, wie Aktivitäten ihre Lebenszyklen koordinieren, wird unter Der Aktivitätslebenszyklus beschrieben.
- Wenn Sie nur mit dem Dienst interagieren müssen, während Ihre Aktivität sichtbar ist, binden Sie ihn während
Weitere Codebeispiele zum Binden an einen Dienst finden Sie in der Klasse
RemoteService.java in ApiDemos.
Lebenszyklus eines gebundenen Dienstes verwalten
Wenn ein Dienst von allen Clients getrennt wird, wird er vom Android-System gelöscht (es sei denn, er wurde mit startService() gestartet). Sie müssen den Lebenszyklus Ihres Dienstes also nicht verwalten, wenn es sich um einen reinen gebundenen Dienst handelt. Das Android-System verwaltet ihn für Sie, je nachdem, ob er an Clients gebunden ist.
Wenn Sie jedoch die Callback-Methode onStartCommand() implementieren, müssen Sie den Dienst explizit beenden, da er jetzt als gestartet gilt. In diesem Fall wird der Dienst ausgeführt, bis er sich selbst mit stopSelf() beendet oder eine andere Komponente stopService() aufruft, unabhängig davon, ob er an Clients gebunden ist.
Wenn Ihr Dienst gestartet wurde und Bindungen akzeptiert, können Sie optional true zurückgeben, wenn das System Ihre onUnbind()-Methode aufruft, wenn Sie einen Aufruf von onRebind() erhalten möchten, wenn sich ein Client das nächste Mal an den Dienst bindet. onRebind() gibt „void“ zurück, aber der Client empfängt den IBinder trotzdem im onServiceConnected()-Callback.
Die folgende Abbildung veranschaulicht die Logik für diese Art von Lebenszyklus.
Weitere Informationen zum Lebenszyklus eines gestarteten Dienstes finden Sie in der Übersicht über Dienste.