Gebundene Dienste – Übersicht

Ein gebundener Dienst ist der Server in einer Client-Server-Schnittstelle. Damit können Komponenten wie Aktivitäten sich an den Dienst binden, Anfragen senden, Antworten empfangen und Interprocess Communication (IPC) ausführen. Ein gebundener Dienst wird normalerweise nur dann ausgeführt, wenn er eine andere Anwendungskomponente bereitstellt. Außerdem wird er nicht auf unbestimmte Zeit im Hintergrund ausgeführt.

In diesem Dokument wird beschrieben, wie Sie einen gebundenen Dienst erstellen, einschließlich einer Bindung von anderen Anwendungskomponenten an den Dienst. Weitere Informationen zu Diensten im Allgemeinen, z. B. wie Sie Benachrichtigungen von einem Dienst senden und den Dienst für die Ausführung im Vordergrund festlegen, finden Sie in der Übersicht über Dienste.

Grundlagen

Ein gebundener Dienst ist eine Implementierung der Klasse Service, mit der sich andere Anwendungen an ihn binden und mit ihr interagieren können. Implementieren Sie dazu 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 also einen Dienst starten, indem Sie startService() aufrufen. Der Dienst kann dann auf unbestimmte Zeit ausgeführt werden. Sie können auch bindService() aufrufen, damit sich ein Client an den Dienst binden kann.

Wenn Sie Ihren Dienst starten und gebunden lassen, löscht das System den Dienst beim Start des Dienstes nicht, wenn alle Clients die Bindung aufheben. Stattdessen müssen Sie den Dienst explizit durch Aufrufen von stopSelf() oder stopService() beenden.

Obwohl du normalerweise entweder onBind() oder onStartCommand() implementierst, ist es manchmal erforderlich, beide zu implementieren. Für einen Musikplayer kann es beispielsweise nützlich sein, seinen Dienst auf unbestimmte Zeit ausführen zu lassen und zusätzlich eine Bindung bereitzustellen. Auf diese Weise kann der Dienst durch eine Aktivität gestartet und etwas Musik abgespielt werden, und die Musik wird weiter abgespielt, auch wenn der Nutzer die App verlässt. Wenn der Nutzer dann zur App zurückkehrt, kann die Aktivität an den Dienst gebunden werden, um wieder die Kontrolle über die Wiedergabe zu erlangen.

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 durch Aufrufen von bindService() an einen Dienst. Ist dies der Fall, muss eine Implementierung von ServiceConnection bereitgestellt werden, die die Verbindung mit dem Dienst überwacht. Der Rückgabewert von bindService() gibt an, ob der angeforderte Dienst vorhanden ist und ob der Client Zugriff darauf hat.

Wenn das Android-System die Verbindung zwischen dem Client und dem Dienst erstellt, wird onServiceConnected() im ServiceConnection aufgerufen. Die Methode onServiceConnected() enthält ein IBinder-Argument, das der Client dann verwendet, um mit dem gebundenen Dienst zu kommunizieren.

Sie können mehrere Clients gleichzeitig mit einem Dienst verbinden. Das System speichert jedoch den Kommunikationskanal des IBinder-Dienstes im Cache. Mit anderen Worten: Das System ruft die Methode onBind() des Dienstes auf, um IBinder nur dann zu generieren, wenn der erste Client gebunden wird. 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, löscht das System den Dienst, sofern er nicht mit startService() gestartet wurde.

Der wichtigste Teil der Implementierung des gebundenen Dienstes ist die Definition der Schnittstelle, die von der Callback-Methode onBind() zurückgegeben wird. Im folgenden Abschnitt werden verschiedene Möglichkeiten beschrieben, wie Sie die IBinder-Schnittstelle Ihres Dienstes definieren können.

Gebundenen Dienst erstellen

Wenn Sie einen Dienst erstellen, der eine Bindung bereitstellt, müssen Sie einen IBinder mit der Programmierschnittstelle angeben, über die Clients mit dem Dienst interagieren können. Es gibt drei Möglichkeiten, die Schnittstelle zu definieren:

Binder-Klasse erweitern
Wenn der Dienst nur für Ihre eigene Anwendung zugänglich ist und im selben Prozess wie der Client ausgeführt wird (was üblich ist), erstellen Sie Ihre Schnittstelle, indem Sie die Klasse Binder erweitern und eine Instanz davon aus onBind() zurückgeben. Der Client empfängt die Binder und kann damit direkt auf öffentliche Methoden zugreifen, die entweder in der Binder- oder der Service-Implementierung verfügbar sind.

Dies ist die bevorzugte Methode, wenn Ihr Dienst nur ein Hintergrund-Worker für Ihre eigene Anwendung ist. Wenn dies nicht die bevorzugte Methode zum Erstellen Ihrer Schnittstelle ist, besteht der einzige Anwendungsfall darin, dass Ihr Dienst von anderen Anwendungen oder über separate Prozesse hinweg verwendet wird.

Messenger verwenden
Wenn Ihre Schnittstelle über verschiedene Prozesse hinweg funktionieren soll, können Sie eine Schnittstelle für den Dienst mit einer Messenger erstellen. Auf diese Weise definiert der Dienst ein Handler, das auf verschiedene Typen von Message-Objekten reagiert.

Diese Handler ist die Grundlage für ein Messenger, das dann ein IBinder mit dem Client teilen kann, damit der Client mithilfe von Message-Objekten Befehle an den Dienst senden kann. Außerdem kann der Client eine eigene Messenger definieren, damit der Dienst Nachrichten zurücksenden kann.

Dies ist die einfachste Methode, um Interprocess Communication (IPC) durchzuführen, da Messenger alle Anfragen in einem einzigen Thread in die Warteschlange stellt. Dadurch müssen Sie Ihren Dienst nicht Thread-sicher gestalten.

AIDL verwenden
Die Android Interface Definition Language (AIDL) zerlegt Objekte in Primitive, die das Betriebssystem verstehen kann, und stellt sie dann prozessübergreifend zur Ausführung von IPC bereit. Die vorherige Technik, bei der Messenger verwendet wird, basiert tatsächlich auf AIDL als zugrunde liegende Struktur.

Wie im vorherigen Abschnitt erwähnt, erstellt der Messenger eine Warteschlange aller Clientanfragen in einem einzelnen Thread, sodass der Dienst 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 Thread-sicher sein und Multithreading unterstützen.

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 IPC verarbeitet, die Sie dann in Ihrem Dienst erweitern können.

Hinweis:Für die meisten Anwendungen ist AIDL nicht die beste Wahl, um einen gebundenen Dienst zu erstellen, da dies Multithreading-Funktionen erfordern und eine kompliziertere Implementierung zur Folge haben kann. Daher wird in diesem Dokument nicht erläutert, wie Sie ihn für Ihren Dienst verwenden können. Wenn Sie sicher sind, dass Sie AIDL direkt verwenden müssen, lesen Sie das AIDL-Dokument.

Binder-Klasse erweitern

Wenn nur die lokale Anwendung Ihren Dienst verwendet und nicht prozessübergreifend arbeiten muss, können Sie Ihre eigene Binder-Klasse implementieren, die Ihrem Client direkten Zugriff auf öffentliche Methoden im Dienst bietet.

Hinweis:Dies funktioniert nur, wenn sich der Client und der Dienst in derselben Anwendung und demselben Prozess befinden. Dies ist häufig der Fall. Dies funktioniert beispielsweise gut für eine Musikanwendung, die eine Aktivität an ihren eigenen Dienst binden muss, der im Hintergrund Musik abspielt.

Und so funktioniert es:

  1. Erstellen Sie in Ihrem Dienst eine Instanz von Binder, die einen der folgenden Schritte ausführt:
    • Enthält öffentliche Methoden, die der Client aufrufen kann.
    • Gibt die aktuelle Service-Instanz mit öffentlichen Methoden zurück, die der Client aufrufen kann.
    • Gibt die Instanz einer anderen Klasse zurück, die vom Dienst mit öffentlichen Methoden gehostet wird, die der Client aufrufen kann.
  2. Gibt diese Instanz von Binder von der Callback-Methode onBind() zurück.
  3. Empfangen Sie im Client die Binder von der Callback-Methode onServiceConnected() und rufen Sie 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 ebenfalls im selben Prozess befinden, da mit dieser Methode kein prozessübergreifendes Marshalling durchgeführt wird.

Im Folgenden sehen Sie beispielsweise 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. Kunden können beispielsweise getRandomNumber() über den Dienst aufrufen.

Diese Aktivität ist an LocalService gebunden und ruft getRandomNumber() auf, wenn auf eine Schaltfläche geklickt wird:

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;
        }
    };
}

Das vorherige Beispiel zeigt, wie sich der Client mithilfe einer Implementierung von ServiceConnection und des onServiceConnected()-Callbacks an den Dienst bindet. Der nächste Abschnitt enthält weitere Informationen zu dieser Bindung an den Dienst.

Hinweis:Im vorherigen Beispiel hebt die Methode onStop() die Bindung des Clients an den Dienst auf. Heben Sie die Bindung von Clients an Dienste zu geeigneten Zeitpunkten auf, wie im Abschnitt Zusätzliche Hinweise beschrieben.

Weiteren Beispielcode findest du in den Klassen LocalService.java und LocalServiceActivities.java unter ApiDemos.

Messenger verwenden

Wenn Ihr Dienst mit Remote-Prozessen kommunizieren muss, können Sie ein Messenger verwenden, um die Schnittstelle für Ihren Dienst bereitzustellen. Mit diesem Verfahren können Sie die Interprozesskommunikation (IPC) ausführen, ohne AIDL verwenden zu müssen.

Die Verwendung von Messenger für Ihre Schnittstelle ist einfacher als die Verwendung von AIDL, da Messenger alle Aufrufe an den Dienst in die 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 Messenger verwenden, kann der Dienst also einen Aufruf nach dem anderen verarbeiten. Wenn es wichtig ist, dass Ihr Dienst Multithreads bietet, verwenden Sie AIDL, um Ihre Schnittstelle zu definieren.

Hier eine Zusammenfassung der Verwendung von Messenger:

  1. Der Dienst implementiert einen Handler, der für jeden Aufruf von einem Client einen Callback empfängt.
  2. Der Dienst verwendet Handler, um ein Messenger-Objekt zu erstellen, das ein Verweis auf das Handler ist.
  3. Der Messenger erstellt ein IBinder-Element, das der Dienst von onBind() an Clients zurückgibt.
  4. Clients verwenden die IBinder, um das Messenger zu instanziieren (das auf die Handler des Dienstes verweist), die der Client zum Senden von Message-Objekten an den Dienst verwendet.
  5. Der Dienst empfängt jede Message in seiner Handler, genauer gesagt in der handleMessage()-Methode.

Auf diese Weise gibt es keine Methoden, die der Client für den Dienst aufrufen kann. Stattdessen sendet der Client Nachrichten (Message-Objekte), die der Dienst in seinem Handler empfängt.

Hier ist ein einfacher 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();
    }
}

Über die Methode handleMessage() im Handler empfängt der Dienst die eingehende Message und entscheidet basierend auf dem what-Mitglied, was zu tun ist.

Ein Client muss lediglich eine Messenger basierend auf dem IBinder erstellen, der vom Dienst zurückgegeben wird, und eine Nachricht mit send() senden. Im Folgenden sehen Sie beispielsweise eine Aktivität, die eine Bindung an den Dienst herstellt und die Nachricht MSG_SAY_HELLO 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)
            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;
        }
    }
}

Dieses Beispiel zeigt nicht, wie der Dienst dem Client antworten kann. Wenn der Dienst antworten soll, müssen Sie auch eine Messenger im Client erstellen. Wenn der Client den Callback onServiceConnected() empfängt, sendet er eine Message an den Dienst, die die Messenger des Clients in den Parameter replyTo der Methode send() einfügt.

In den Beispielen für MessengerService.java (Dienst) und MessengerServiceActivities.java (Client) finden Sie ein Beispiel für die Bereitstellung von bidirektionalem Messaging.

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 zur Interaktion mit dem Dienst zurückgibt.

Die Bindung ist asynchron und bindService() gibt sofort ohne IBinder an den Client zurück. Um das IBinder zu empfangen, muss der Client eine Instanz von ServiceConnection erstellen und an bindService() übergeben. Das ServiceConnection enthält eine Callback-Methode, die vom System aufgerufen wird, um das IBinder zu senden.

Hinweis:Nur Aktivitäten, Dienste und Contentanbieter können eine Bindung an einen Dienst vornehmen. Eine Bindung an einen Dienst von einem Übertragungsempfänger ist nicht möglich.

So binden Sie den Client an einen Dienst:

  1. ServiceConnection implementieren.

    Ihre Implementierung muss zwei Callback-Methoden überschreiben:

    onServiceConnected()
    Dies wird vom System aufgerufen, um das IBinder zu senden, das von der Methode onBind() des Dienstes zurückgegeben wird.
    onServiceDisconnected()
    Das Android-System ruft diesen Aufruf auf, wenn die Verbindung zum Dienst unerwartet unterbrochen wird, z. B. wenn der Dienst abstürzt oder beendet wird. Diese wird nicht aufgerufen, wenn der Client die Bindung aufhebt.
  2. Rufen Sie bindService() auf und übergeben Sie die ServiceConnection-Implementierung.

    Hinweis:Wenn die Methode "false" zurückgibt, hat der Client keine gültige Verbindung zum Dienst. Du solltest jedoch unbindService() in deinem Client aufrufen. Andernfalls verhindert Ihr Client, dass der Dienst heruntergefahren wird, wenn er inaktiv ist.

  3. Wenn das System die Callback-Methode onServiceConnected() aufruft, können Sie mit den von der Schnittstelle definierten Methoden Aufrufe an den Dienst senden.
  4. Um die Verbindung zum Dienst zu trennen, rufen Sie unbindService() an.

    Wenn Ihr Client weiterhin an einen Dienst gebunden ist, während Ihre Anwendung den Client löscht, löst das Löschen der Bindung des Clients aus. Es empfiehlt sich, die Bindung des Clients aufzuheben, sobald die Interaktion mit dem Dienst abgeschlossen ist. Dadurch wird der inaktive Dienst heruntergefahren. Weitere Informationen zu den geeigneten Zeiten zum 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. Daher muss lediglich das zurückgegebene IBinder in die Klasse LocalBinder umgewandelt und die LocalService-Instanz angefordert werden:

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;
    }
};

Mit dieser ServiceConnection kann sich der Client an einen Dienst binden, 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 ein Intent, der den zu bindenden Dienst explizit angibt.

    Achtung:Wenn Sie eine Bindung an eine Service mit einem Intent erstellen, achten Sie darauf, dass Ihre Anwendung sicher ist, indem Sie einen expliziten Intent verwenden. Die Verwendung eines impliziten Intents zum Starten eines Dienstes stellt ein Sicherheitsrisiko dar, 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-Ebene 21) löst das System eine Ausnahme aus, wenn Sie bindService() mit einem impliziten Intent aufrufen.

  • Der zweite Parameter ist das Objekt ServiceConnection.
  • 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 sind BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND oder 0 für keinen.

Zusätzliche Anmerkungen

Im Folgenden finden Sie einige wichtige Hinweise zur Bindung an einen Dienst:

  • DeadObjectException-Ausnahmen werden immer erfasst. Diese werden ausgelöst, wenn die Verbindung unterbrochen wurde. Dies ist die einzige Ausnahme, die von Remote-Methoden ausgelöst wird.
  • Objekte werden prozessübergreifend gezählt.
  • In der Regel kombinieren Sie Bindung und Unbindung in den übereinstimmenden Einführungs- und Abrissmomenten im Clientlebenszyklus, wie in den folgenden Beispielen beschrieben:
    • Wenn Sie nur mit dem Dienst interagieren müssen, während Ihre Aktivität sichtbar ist, binden Sie die Bindung während onStart() und heben Sie die Bindung während onStop() auf.
    • Wenn Sie möchten, dass Ihre Aktivität auch dann Antworten erhält, wenn sie im Hintergrund angehalten wird, binden Sie sie während onCreate() ein und heben Sie die Bindung während onDestroy() auf. Beachten Sie, dass dies impliziert, dass Ihre Aktivität den Dienst die gesamte Zeit, während er ausgeführt wird, nutzen muss, auch im Hintergrund. Wenn sich der Dienst also in einem anderen Prozess befindet, erhöhen Sie die Belastung des Prozesses und er wird mit größerer Wahrscheinlichkeit vom System abgebrochen.

    Hinweis:In der Regel binden und heben Sie die Bindung nicht während der onResume()- und onPause()-Callbacks Ihrer Aktivität auf, da diese Callbacks bei jedem Lebenszyklusübergang auftreten. Halten Sie die Verarbeitung bei diesen Übergängen auf ein Minimum.

    Wenn mehrere Aktivitäten in Ihrer Anwendung an denselben Dienst gebunden sind und es einen Übergang zwischen zwei dieser Aktivitäten gibt, wird der Dienst möglicherweise gelöscht und neu erstellt, da die Bindung der aktuellen Aktivität (während der Pause) vor der Bindung der nächsten Aktivität neu erstellt wird (während des Lebenslaufs). Diese Aktivitätsänderung zur Koordinierung ihrer Lebenszyklen wird unter Aktivitätslebenszyklus beschrieben.

Weiteren Beispielcode 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 ist, wird er vom Android-System gelöscht (es sei denn, er wurde mit startService() gestartet). Sie müssen also den Lebenszyklus Ihres Dienstes nicht verwalten, wenn es sich nur um einen gebundenen Dienst handelt. Das Android-System verwaltet es für Sie, je nachdem, ob es 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 so lange ausgeführt, bis der Dienst mit stopSelf() beendet wird oder eine andere Komponente stopService() aufruft, unabhängig davon, ob er an Clients gebunden ist.

Wenn Ihr Dienst gestartet wird und Bindungen akzeptiert, können Sie beim Aufrufen der Methode onUnbind() durch das System optional true zurückgeben, wenn Sie bei der nächsten Bindung durch einen Client einen Aufruf an onRebind() erhalten möchten. onRebind() gibt „void“ zurück, aber der Client empfängt IBinder weiterhin in seinem onServiceConnected()-Callback. Die folgende Abbildung veranschaulicht die Logik für diese Art von Lebenszyklus.

Abbildung 1: Der Lebenszyklus eines Dienstes, der gestartet wird und auch Bindung zulässt.

Weitere Informationen zum Lebenszyklus eines gestarteten Dienstes finden Sie in der Übersicht der Dienste.