Android Interface Definition Language (AIDL)

Die Android Interface Definition Language (AIDL) ähnelt anderen IDLs: können Sie die Programmierschnittstelle definieren, vereinbarten sich die Kundschaft und der Service, um mithilfe Interprocess Communication (IPC)

Unter Android kann ein Prozess normalerweise nicht auf die eines anderen Prozesses. Um zu sprechen, müssen sie ihre Objekte in Primitive zerlegen, das Betriebssystem die Objekte über diese Begrenzungen hinweg verstehen und platzieren kann. Der Code für ist das Marshalling mühsam, daher übernimmt Android dies mit AIDL für Sie.

Hinweis:AIDL ist nur erforderlich, wenn Sie Clients aus verschiedene Anwendungen auf Ihren IPC-Dienst zugreifen und Sie Multithreading in Ihrem . Wenn Sie IPCs nicht gleichzeitig Anwendungen zu erstellen, erstellen Sie Ihre Schnittstelle, indem Sie eine Binder Wenn Sie IPC ausführen möchten, aber kein Multithreading verarbeiten müssen, Implementieren Sie Ihre Schnittstelle mithilfe eines Messenger. Unabhängig davon sollten Sie sich mit gebundenen Diensten vertraut machen, bevor Sie Implementierung eines AIDL.

Bevor Sie mit dem Entwerfen Ihrer AIDL-Schnittstelle beginnen, beachten Sie, dass Aufrufe einer AIDL-Schnittstelle für direkten Funktionsaufrufen. Treffen Sie keine Annahmen über den Thread, in dem der Aufruf erfolgt. Was passiert, hängt davon ab, ob der Anruf aus einem Thread im einem lokalen Prozess oder einem Remote-Prozess:

  • Aufrufe aus dem lokalen Prozess werden in dem Thread ausgeführt, der den Aufruf durchführt. Wenn Dies ist Ihr UI-Hauptthread. Dieser Thread wird weiterhin in der AIDL-Schnittstelle ausgeführt. Falls ja in dem der Code in dem Dienst ausgeführt wird. Wenn also nur lokale Threads auf den Dienst zugreifen, können Sie vollständig steuern, welche Threads darin ausgeführt werden. Aber Verwenden Sie in diesem Fall AIDL überhaupt nicht. erstellen Sie stattdessen die Schnittstelle durch Implementierung eines Binder
  • Aufrufe von einem Remoteprozess werden aus einem Thread-Pool weitergeleitet, den die Plattform enthält Ihren eigenen Prozess. Auf eingehende Anrufe von unbekannten Threads mit mehreren Anrufen vorbereitet sein gleichzeitig passiert. Mit anderen Worten, die Implementierung einer AIDL-Schnittstelle muss Thread-sicher. Aufrufe von einem Thread für dasselbe Remote-Objekt in der richtigen Reihenfolge beim Empfänger eintreffen.
  • Mit dem Schlüsselwort oneway wird das Verhalten von Remoteaufrufen geändert. Bei einem Remote-Aufruf nicht blockieren. Es sendet die Transaktionsdaten und kehrt sofort zurück. Die Implementierung der Schnittstelle empfängt dies schließlich als regulären Aufruf aus dem Threadpool Binder als normaler Remoteaufruf. Wenn oneway mit einem lokalen Anruf verwendet wird, ohne Auswirkungen und der Aufruf ist dennoch synchron.

AIDL-Schnittstelle definieren

AIDL-Schnittstelle mithilfe der Java-Datei in einer .aidl-Datei definieren Syntax einer Programmiersprache und speichern Sie sie dann im Quellcode von beiden im Verzeichnis src/. die Anwendung, die den Dienst hostet, und jede andere Anwendung, die an den Dienst gebunden wird.

Wenn Sie jede Anwendung erstellen, die die Datei .aidl enthält, IBinder-Schnittstelle basierend auf der Datei .aidl generieren und speichern unter Verzeichnis gen/ des Projekts. Der Dienst muss die IBinder implementieren. an die jeweilige Benutzeroberfläche. Die Client-Anwendungen können sich dann an den Dienst binden und Methoden aus den IBinder, um IPC durchzuführen.

Führen Sie die folgenden Schritte aus, um einen begrenzten Dienst mit AIDL zu erstellen. finden Sie in den folgenden Abschnitten:

  1. .aidl-Datei erstellen

    Diese Datei definiert die Programmierschnittstelle mit Methodensignaturen.

  2. Schnittstelle implementieren

    Die Android SDK-Tools generieren eine Schnittstelle in der Programmiersprache Java basierend auf Ihrem .aidl-Datei. Diese Schnittstelle hat eine innere abstrakte Klasse namens Stub, die Binder und implementiert Methoden aus Ihrer AIDL-Schnittstelle. Sie müssen die Stub und implementiere die Methoden.

  3. Schnittstelle für Clients verfügbar machen

    Implementieren Sie ein Service und überschreiben Sie onBind(), um die Implementierung der Stub zurückzugeben. .

Achtung:Alle Änderungen, die Sie nach dem Ihr erster Release muss abwärtskompatibel bleiben, damit andere Anwendungen nicht beeinträchtigt werden die Ihren Dienst nutzen. Das liegt daran, dass die Datei „.aidl“ in andere Anwendungen kopiert werden muss damit sie auf die Oberfläche Ihres Service zugreifen können, müssen Sie den Support für die ursprüngliche .

AIDL-Datei erstellen

AIDL verwendet eine einfache Syntax, mit der Sie eine Schnittstelle mit einer oder mehreren Methoden deklarieren können, Parameter und Rückgabewerte. Die Parameter und Rückgabewerte können einen beliebigen Typ haben, auch AIDL-generierte Schnittstellen.

Sie müssen die Datei .aidl mit der Programmiersprache Java erstellen. Jeweils .aidl -Datei muss eine einzelne Schnittstelle definieren und erfordert nur die Interface-Deklaration und -Methode. Signaturen.

Standardmäßig unterstützt AIDL die folgenden Datentypen:

  • Alle primitiven Typen in der Programmiersprache Java (z. B. int, long, char, boolean usw.)
  • Arrays primitiver Typen wie int[]
  • String
  • CharSequence
  • List

    Alle Elemente in der List müssen einen der unterstützten Datentypen in diesem Liste oder eine der anderen AIDL-generierten Schnittstellen oder Pakete, die Sie deklarieren. A List kann optional als parametrisierte Typklasse verwendet werden, z. B. List<String>. Die tatsächliche konkrete Klasse, die die andere Seite empfängt, ist immer ein ArrayList, obwohl der wird generiert, um die List-Schnittstelle zu verwenden.

  • Map

    Alle Elemente in der Map müssen einen der unterstützten Datentypen in diesem Liste oder eine der anderen AIDL-generierten Schnittstellen oder Pakete, die Sie deklarieren. Parametrisierte Typzuordnungen, zum Beispiel in der Form Map<String,Integer> werden nicht unterstützt. Die konkrete Klasse, die die andere Seite ist immer ein HashMap, obwohl die Methode zur Verwendung der Map-Schnittstelle generiert wurde. Erwägen Sie die Verwendung Bundle als Alternative zu Map.

Für jeden zusätzlichen Typ, der zuvor nicht aufgeführt wurde, müssen Sie eine import-Anweisung verwenden. auch wenn sie im selben Paket wie Ihre Schnittstelle definiert sind.

Beachten Sie beim Definieren der Dienstschnittstelle Folgendes:

  • Methoden können null oder mehr Parameter annehmen und einen Wert oder ein void-Element zurückgeben.
  • Für alle nicht-primitiven Parameter ist ein Richtungs-Tag erforderlich, das angibt, in welche Richtung die Daten gehen sollen: in, out oder inout (siehe Beispiel unten).

    Primitives, String, IBinder und AIDL-generiert Schnittstellen sind standardmäßig in. Andernfalls ist dies nicht möglich.

    Achtung:Beschränken Sie die Richtung auf das, was wirklich ist. erforderlich, da das Mars-lling-Parameter- system teuer ist.

  • Alle Codekommentare in der Datei .aidl sind in der generierten IBinder Schnittstelle mit Ausnahme von Kommentaren vor dem Import und dem Paket Aussagen.
  • String- und Ganzzahl-Konstanten können in der AIDL-Schnittstelle wie const int VERSION = 1; definiert werden.
  • Methodenaufrufe werden von einem transact() , der normalerweise auf einem Methodenindex in der Schnittstelle basiert. Da diese die Versionsverwaltung erschwert, können Sie kann den Transaktionscode manuell einer Methode zuweisen: void method() = 10;.
  • Argumente und Rückgabetypen, für die Nullwerte zulässig sind, müssen mit @nullable annotiert werden.

Hier ist eine .aidl-Beispieldatei:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements.

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

Speichern Sie die Datei .aidl im Verzeichnis src/ Ihres Projekts. Wenn Sie App erstellen, generieren die SDK-Tools die IBinder-Schnittstellendatei in Ihrem Verzeichnis gen/ des Projekts. Der Name der generierten Datei stimmt mit dem Namen der Datei .aidl überein, aber mit der Erweiterung .java. Beispielsweise führt IRemoteService.aidl zu IRemoteService.java.

Wenn Sie Android Studio verwenden, generiert der inkrementelle Build fast sofort die Binder-Klasse. Wenn Sie Android Studio nicht verwenden, generiert das Gradle-Tool die Binder-Klasse das nächste Mal, wenn Sie um Ihre Anwendung zu erstellen. Projekt mit gradle assembleDebug erstellen oder gradle assembleRelease, sobald Sie mit dem Schreiben der .aidl-Datei fertig sind, damit Ihr Code mit der generierten Klasse verknüpft werden kann.

Schnittstelle implementieren

Wenn du deine App erstellst, generieren die Android SDK-Tools eine .java-Schnittstellendatei. benannt nach der Datei .aidl. Die generierte Schnittstelle enthält eine Unterklasse mit dem Namen Stub. eine abstrakte Implementierung der übergeordneten Schnittstelle, wie z. B. YourInterface.Stub, und deklariert alle Methoden aus der Datei .aidl.

Hinweis: Stub müssen auch definiert mehrere Hilfsmethoden, insbesondere asInterface(), die ein IBinder-Element verwenden, das normalerweise an die onServiceConnected()-Callback-Methode eines Clients übergeben wird, und gibt eine Instanz der Stub-Schnittstelle zurück. Weitere Informationen zur Übertragung finden Sie im Abschnitt IPC anrufen .

Erweitern Sie den generierten Binder, um die aus .aidl generierte Schnittstelle zu implementieren. wie YourInterface.Stub erstellen und die Methoden von der Datei .aidl übernommen.

Hier sehen Sie eine Beispielimplementierung einer Schnittstelle mit dem Namen IRemoteService, die durch die vorhergehende Beispiel für IRemoteService.aidl mit einer anonymen Instanz:

Kotlin

private val binder = object : IRemoteService.Stub() {

    override fun getPid(): Int =
            Process.myPid()

    override fun basicTypes(
            anInt: Int,
            aLong: Long,
            aBoolean: Boolean,
            aFloat: Float,
            aDouble: Double,
            aString: String
    ) {
        // Does nothing.
    }
}

Java

private final IRemoteService.Stub binder = new IRemoteService.Stub() {
    public int getPid(){
        return Process.myPid();
    }
    public void basicTypes(int anInt, long aLong, boolean aBoolean,
        float aFloat, double aDouble, String aString) {
        // Does nothing.
    }
};

binder ist jetzt eine Instanz der Klasse Stub (ein Binder). definiert die IPC-Schnittstelle für den Dienst. Im nächsten Schritt wird diese Instanz damit sie mit dem Dienst interagieren können.

Beachten Sie bei der Implementierung der AIDL-Schnittstelle einige Regeln:

  • Eingehende Anrufe werden nicht zwangsläufig im Hauptthread ausgeführt. Sie müssen sich also überlegen, von Anfang an über Multithreading informieren und Ihren Dienst so erstellen, dass er threadsicher ist.
  • IPC-Aufrufe sind standardmäßig synchron. Wenn Sie wissen, dass der Service Millisekunden bis zum Ausführen einer Anfrage erforderlich sind, rufen Sie diese nicht aus dem Hauptthread der Aktivität auf. Dadurch kann die App hängen bleiben, sodass Android die Meldung „App reagiert nicht“ anzeigt Dialogfeld. Rufen Sie sie aus einem separaten Thread im Client auf.
  • Nur die Ausnahmetypen, die in der Referenzdokumentation für Parcel.writeException() an den Anrufer zurückgesendet werden.

Schnittstelle für Clients verfügbar machen

Nachdem Sie die Schnittstelle für Ihren Dienst implementiert haben, müssen Sie sie für damit sie sich daran binden können. So machen Sie die Benutzeroberfläche verfügbar: für Ihren Dienst: Erweitern Sie Service und implementieren Sie onBind(), um eine Instanz Ihrer Klasse zurückzugeben, die Den generierten Stub, wie im vorherigen Abschnitt erläutert. Hier ein Beispiel: , der Clients die Beispielschnittstelle IRemoteService zur Verfügung stellt.

Kotlin

class RemoteService : Service() {

    override fun onCreate() {
        super.onCreate()
    }

    override fun onBind(intent: Intent): IBinder {
        // Return the interface.
        return binder
    }


    private val binder = object : IRemoteService.Stub() {
        override fun getPid(): Int {
            return Process.myPid()
        }

        override fun basicTypes(
                anInt: Int,
                aLong: Long,
                aBoolean: Boolean,
                aFloat: Float,
                aDouble: Double,
                aString: String
        ) {
            // Does nothing.
        }
    }
}

Java

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface.
        return binder;
    }

    private final IRemoteService.Stub binder = new IRemoteService.Stub() {
        public int getPid(){
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
            float aFloat, double aDouble, String aString) {
            // Does nothing.
        }
    };
}

Wenn jetzt ein Client, z. B. eine Aktivität, bindService() aufruft, um eine Verbindung zu diesem Dienst herzustellen, empfängt der onServiceConnected()-Callback des Clients die binder-Instanz, die von onBind() des Dienstes zurückgegeben wird .

Der Client muss auch Zugriff auf die Interface-Klasse haben. Wenn also der Kunde und der Service separate Anwendungen erstellt haben, muss die Clientanwendung über eine Kopie der Datei .aidl verfügen im Verzeichnis src/, das die Datei android.os.Binder generiert und dem Client Zugriff auf die AIDL-Methoden gewähren.

Wenn der Client die IBinder empfängt im onServiceConnected()-Callback angegeben werden, YourServiceInterface.Stub.asInterface(service), um die zurückgegebene auf den Typ YourServiceInterface:

Kotlin

var iRemoteService: IRemoteService? = null

val mConnection = object : ServiceConnection {

    // Called when the connection with the service is established.
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // Following the preceding example for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service.
        iRemoteService = IRemoteService.Stub.asInterface(service)
    }

    // Called when the connection with the service disconnects unexpectedly.
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "Service has unexpectedly disconnected")
        iRemoteService = null
    }
}

Java

IRemoteService iRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established.
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Following the preceding example for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service.
        iRemoteService = IRemoteService.Stub.asInterface(service);
    }

    // Called when the connection with the service disconnects unexpectedly.
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        iRemoteService = null;
    }
};

Weiteren Beispielcode finden Sie in der <ph type="x-smartling-placeholder"></ph> RemoteService.java Kurs in <ph type="x-smartling-placeholder"></ph> ApiDemos zu finden.

Objekte über IPC übergeben

In Android 10 (API-Level 29 oder höher) kannst du Parcelable Objekte direkt in AIDL Typen, die als AIDL-Schnittstellenargumente und andere Attribute unterstützt werden, unterstützt. Dadurch entfällt der zusätzliche Aufwand zum manuellen Schreiben von Marshaling-Code und . Dadurch wird jedoch auch eine Grundstruktur erstellt. Wenn benutzerdefinierte Zugriffsfunktionen oder andere Funktionen implementieren Sie stattdessen Parcelable.

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect {
    int left;
    int top;
    int right;
    int bottom;
}

Mit dem vorherigen Codebeispiel wird automatisch eine Java-Klasse mit den ganzzahligen Feldern left, top, right und bottom. Alle relevanten Marshalling-Codes automatisch implementiert und das Objekt kann direkt verwendet werden, ohne Implementierung.

Sie können eine benutzerdefinierte Klasse auch über eine IPC-Schnittstelle von einem Prozess an einen anderen senden. Sie können jedoch stellen Sie sicher, dass der Code für Ihre Klasse auf der anderen Seite des IPC-Kanals verfügbar ist. Ihre Klasse muss die Parcelable-Schnittstelle unterstützen. Unterstützend Parcelable ist wichtig da das Android-System damit Objekte in Primitive zerlegt, die sich dann zusammenfügen lassen. über verschiedene Prozesse hinweg.

So erstellen Sie eine benutzerdefinierte Klasse, die Parcelable unterstützt: Folgendes:

  1. Sorgen Sie dafür, dass Ihre Klasse die Parcelable-Schnittstelle implementiert.
  2. Implementieren Sie writeToParcel, aktuellen Status des Objekts und schreibt ihn in ein Parcel-Objekt.
  3. Fügen Sie der Klasse ein statisches Feld namens CREATOR hinzu, das ein Objekt ist, das der Parcelable.Creator-Schnittstelle.
  4. Erstellen Sie abschließend eine .aidl-Datei, die Ihre parcelable-Klasse deklariert, wie im Folgenden gezeigt: Rect.aidl-Datei.

    Wenn Sie einen benutzerdefinierten Build-Prozess verwenden, fügen Sie die Datei .aidl nicht zu Ihrem erstellen. Ähnlich wie eine Headerdatei in der Sprache C wird diese Datei .aidl nicht kompiliert.

AIDL verwendet diese Methoden und Felder im generierten Code, um ein Marshalling- und Unmarshalling durchzuführen für Ihre Objekte.

Hier sehen Sie beispielsweise eine Rect.aidl-Datei, um eine Rect-Klasse zu erstellen, die parcelable:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

Und hier ist ein Beispiel dafür, wie die Rect-Klasse die Parcelable-Protokoll.

Kotlin

import android.os.Parcel
import android.os.Parcelable

class Rect() : Parcelable {
    var left: Int = 0
    var top: Int = 0
    var right: Int = 0
    var bottom: Int = 0

    companion object CREATOR : Parcelable.Creator<Rect> {
        override fun createFromParcel(parcel: Parcel): Rect {
            return Rect(parcel)
        }

        override fun newArray(size: Int): Array<Rect?> {
            return Array(size) { null }
        }
    }

    private constructor(inParcel: Parcel) : this() {
        readFromParcel(inParcel)
    }

    override fun writeToParcel(outParcel: Parcel, flags: Int) {
        outParcel.writeInt(left)
        outParcel.writeInt(top)
        outParcel.writeInt(right)
        outParcel.writeInt(bottom)
    }

    private fun readFromParcel(inParcel: Parcel) {
        left = inParcel.readInt()
        top = inParcel.readInt()
        right = inParcel.readInt()
        bottom = inParcel.readInt()
    }

    override fun describeContents(): Int {
        return 0
    }
}

Java

import android.os.Parcel;
import android.os.Parcelable;

public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }

        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };

    public Rect() {
    }

    private Rect(Parcel in) {
        readFromParcel(in);
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }

    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }

    public int describeContents() {
        return 0;
    }
}

Das Marshalling in der Rect-Klasse ist unkompliziert. Schauen Sie sich die andere auf Parcel, um die anderen Arten von Werten zu sehen, die Sie schreiben können zu einem Parcel.

Warnung:Beachten Sie die Sicherheitsaspekte des Empfangs Daten aus anderen Prozessen. In diesem Fall liest Rect vier Zahlen aus dem Parcel. Es liegt an Ihnen, dafür zu sorgen, dass diese Werte innerhalb des zulässigen für alles, was der Aufrufer zu tun versucht. Weitere Informationen zum Schutz Ihrer Anwendung vor Malware finden Sie in den Sicherheitstipps.

Methoden mit Bundle-Argumenten, die Parcelables enthalten

Wenn in einer Methode ein Bundle-Objekt akzeptiert wird, das die folgenden Informationen enthält: Parcelables müssen Sie den Classloader von Bundle festlegen, indem Sie Bundle.setClassLoader(ClassLoader) aufrufen, bevor versucht wird, von Bundle. Andernfalls tritt ClassNotFoundException auf, obwohl das Paket in Ihrer Anwendung richtig definiert ist.

Sehen Sie sich beispielsweise die folgende .aidl-Beispieldatei an:

// IRectInsideBundle.aidl
package com.example.android;

/** Example service interface */
interface IRectInsideBundle {
    /** Rect parcelable is stored in the bundle with key "rect". */
    void saveRect(in Bundle bundle);
}
Wie die folgende Implementierung zeigt, ist ClassLoader explizit in Bundle festgelegt ist, bevor Rect gelesen wird:

Kotlin

private val binder = object : IRectInsideBundle.Stub() {
    override fun saveRect(bundle: Bundle) {
      bundle.classLoader = classLoader
      val rect = bundle.getParcelable<Rect>("rect")
      process(rect) // Do more with the parcelable.
    }
}

Java

private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() {
    public void saveRect(Bundle bundle){
        bundle.setClassLoader(getClass().getClassLoader());
        Rect rect = bundle.getParcelable("rect");
        process(rect); // Do more with the parcelable.
    }
};

IPC-Methode aufrufen

Führen Sie die folgenden Schritte aus, um eine mit AIDL definierte Remote-Schnittstelle aufzurufen: Ihrem Anrufkurs:

  1. Fügen Sie die Datei .aidl in das Projektverzeichnis src/ ein.
  2. Deklarieren Sie eine Instanz der Schnittstelle IBinder, die basierend auf dem AIDL
  3. Implementieren Sie ServiceConnection.
  4. Rufen Sie uns unter Context.bindService() an. und Ihre ServiceConnection-Implementierung übergeben.
  5. Bei der Implementierung von onServiceConnected() erhalten Sie ein IBinder Instanz namens service. Anruf YourInterfaceName.Stub.asInterface((IBinder)service) bis den zurückgegebenen Parameter in den Typ YourInterface um.
  6. Rufen Sie die Methoden auf, die Sie in Ihrer Schnittstelle definiert haben. Immer erfassen DeadObjectException-Ausnahmen, die ausgelöst werden, wenn bricht die Verbindung ab. Erfassen Sie außerdem SecurityException-Ausnahmen, die ausgelöst werden, wenn die beiden am IPC-Methodenaufruf beteiligten Prozesse widersprüchliche AIDL-Definitionen haben.
  7. Rufen Sie zum Trennen der Verbindung Context.unbindService() mit der Instanz Ihrer Schnittstelle auf.

Beachten Sie beim Aufrufen eines IPC-Dienstes die folgenden Punkte:

  • Objekte werden prozessübergreifend gezählt.
  • Sie können anonyme Objekte als Methodenargumente.

Weitere Informationen zum Binden an einen Dienst finden Sie in der Übersicht zu gebundenen Diensten.

Hier ist Beispielcode, der den Aufruf eines von AIDL erstellten Dienstes veranschaulicht. „Remote Service“-Beispiel im Projekt „ApiDemos“.

Kotlin

private const val BUMP_MSG = 1

class Binding : Activity() {

    /** The primary interface you call on the service.  */
    private var mService: IRemoteService? = null

    /** Another interface you use on the service.  */
    internal var secondaryService: ISecondary? = null

    private lateinit var killButton: Button
    private lateinit var callbackText: TextView
    private lateinit var handler: InternalHandler

    private var isBound: 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 is
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service)
            killButton.isEnabled = true
            callbackText.text = "Attached."

            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService?.registerCallback(mCallback)
            } catch (e: RemoteException) {
                // In this case, the service crashes before we can
                // do anything with it. We can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(
                    this@Binding,
                    R.string.remote_service_connected,
                    Toast.LENGTH_SHORT
            ).show()
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service is
            // unexpectedly disconnected&mdash;that is, its process crashed.
            mService = null
            killButton.isEnabled = false
            callbackText.text = "Disconnected."

            // As part of the sample, tell the user what happened.
            Toast.makeText(
                    this@Binding,
                    R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT
            ).show()
        }
    }

    /**
     * Class for interacting with the secondary interface of the service.
     */
    private val secondaryConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            secondaryService = ISecondary.Stub.asInterface(service)
            killButton.isEnabled = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            secondaryService = null
            killButton.isEnabled = false
        }
    }

    private val mBindListener = View.OnClickListener {
        // Establish a couple connections with the service, binding
        // by interface names. This lets other applications be
        // installed that replace the remote service by implementing
        // the same interface.
        val intent = Intent(this@Binding, RemoteService::class.java)
        intent.action = IRemoteService::class.java.name
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        intent.action = ISecondary::class.java.name
        bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE)
        isBound = true
        callbackText.text = "Binding."
    }

    private val unbindListener = View.OnClickListener {
        if (isBound) {
            // If we have received the service, and hence registered with
            // it, then now is the time to unregister.
            try {
                mService?.unregisterCallback(mCallback)
            } catch (e: RemoteException) {
                // There is nothing special we need to do if the service
                // crashes.
            }

            // Detach our existing connection.
            unbindService(mConnection)
            unbindService(secondaryConnection)
            killButton.isEnabled = false
            isBound = false
            callbackText.text = "Unbinding."
        }
    }

    private val killListener = View.OnClickListener {
        // To kill the process hosting the service, we need to know its
        // PID.  Conveniently, the service has a call that returns
        // that information.
        try {
            secondaryService?.pid?.also { pid ->
                // Note that, though this API lets us request to
                // kill any process based on its PID, the kernel
                // still imposes standard restrictions on which PIDs you
                // can actually kill. Typically this means only
                // the process running your application and any additional
                // processes created by that app, as shown here. Packages
                // sharing a common UID are also able to kill each
                // other's processes.
                Process.killProcess(pid)
                callbackText.text = "Killed service process."
            }
        } catch (ex: RemoteException) {
            // Recover gracefully from the process hosting the
            // server dying.
            // For purposes of this sample, put up a notification.
            Toast.makeText(this@Binding, R.string.remote_call_failed, Toast.LENGTH_SHORT).show()
        }
    }

    // ----------------------------------------------------------------------
    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------

    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private val mCallback = object : IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here is
         * NOT running in our main thread like most other things. So,
         * to update the UI, we need to use a Handler to hop over there.
         */
        override fun valueChanged(value: Int) {
            handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0))
        }
    }

    /**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to interact with it before doing anything.
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.remote_service_binding)

        // Watch for button taps.
        var button: Button = findViewById(R.id.bind)
        button.setOnClickListener(mBindListener)
        button = findViewById(R.id.unbind)
        button.setOnClickListener(unbindListener)
        killButton = findViewById(R.id.kill)
        killButton.setOnClickListener(killListener)
        killButton.isEnabled = false

        callbackText = findViewById(R.id.callback)
        callbackText.text = "Not attached."
        handler = InternalHandler(callbackText)
    }

    private class InternalHandler(
            textView: TextView,
            private val weakTextView: WeakReference<TextView> = WeakReference(textView)
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                BUMP_MSG -> weakTextView.get()?.text = "Received from service: ${msg.arg1}"
                else -> super.handleMessage(msg)
            }
        }
    }
}

Java

public static class Binding extends Activity {
    /** The primary interface we are calling on the service. */
    IRemoteService mService = null;
    /** Another interface we use on the service. */
    ISecondary secondaryService = null;

    Button killButton;
    TextView callbackText;

    private InternalHandler handler;
    private boolean isBound;

    /**
     * Standard initialization of this activity. Set up the UI, then wait
     * for the user to interact with it before doing anything.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.remote_service_binding);

        // Watch for button taps.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(unbindListener);
        killButton = (Button)findViewById(R.id.kill);
        killButton.setOnClickListener(killListener);
        killButton.setEnabled(false);

        callbackText = (TextView)findViewById(R.id.callback);
        callbackText.setText("Not attached.");
        handler = new InternalHandler(callbackText);
    }

    /**
     * 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 is
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service);
            killButton.setEnabled(true);
            callbackText.setText("Attached.");

            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
                // In this case the service crashes before we can even
                // do anything with it. We can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service is
            // unexpectedly disconnected&mdash;that is, its process crashed.
            mService = null;
            killButton.setEnabled(false);
            callbackText.setText("Disconnected.");

            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * Class for interacting with the secondary interface of the service.
     */
    private ServiceConnection secondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            secondaryService = ISecondary.Stub.asInterface(service);
            killButton.setEnabled(true);
        }

        public void onServiceDisconnected(ComponentName className) {
            secondaryService = null;
            killButton.setEnabled(false);
        }
    };

    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            // Establish a couple connections with the service, binding
            // by interface names. This lets other applications be
            // installed that replace the remote service by implementing
            // the same interface.
            Intent intent = new Intent(Binding.this, RemoteService.class);
            intent.setAction(IRemoteService.class.getName());
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            intent.setAction(ISecondary.class.getName());
            bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE);
            isBound = true;
            callbackText.setText("Binding.");
        }
    };

    private OnClickListener unbindListener = new OnClickListener() {
        public void onClick(View v) {
            if (isBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        mService.unregisterCallback(mCallback);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // crashes.
                    }
                }

                // Detach our existing connection.
                unbindService(mConnection);
                unbindService(secondaryConnection);
                killButton.setEnabled(false);
                isBound = false;
                callbackText.setText("Unbinding.");
            }
        }
    };

    private OnClickListener killListener = new OnClickListener() {
        public void onClick(View v) {
            // To kill the process hosting our service, we need to know its
            // PID.  Conveniently, our service has a call that returns
            // that information.
            if (secondaryService != null) {
                try {
                    int pid = secondaryService.getPid();
                    // Note that, though this API lets us request to
                    // kill any process based on its PID, the kernel
                    // still imposes standard restrictions on which PIDs you
                    // can actually kill.  Typically this means only
                    // the process running your application and any additional
                    // processes created by that app as shown here. Packages
                    // sharing a common UID are also able to kill each
                    // other's processes.
                    Process.killProcess(pid);
                    callbackText.setText("Killed service process.");
                } catch (RemoteException ex) {
                    // Recover gracefully from the process hosting the
                    // server dying.
                    // For purposes of this sample, put up a notification.
                    Toast.makeText(Binding.this,
                            R.string.remote_call_failed,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

    // ----------------------------------------------------------------------
    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------

    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here is
         * NOT running in our main thread like most other things. So,
         * to update the UI, we need to use a Handler to hop over there.
         */
        public void valueChanged(int value) {
            handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0));
        }
    };

    private static final int BUMP_MSG = 1;

    private static class InternalHandler extends Handler {
        private final WeakReference<TextView> weakTextView;

        InternalHandler(TextView textView) {
            weakTextView = new WeakReference<>(textView);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    TextView textView = weakTextView.get();
                    if (textView != null) {
                        textView.setText("Received from service: " + msg.arg1);
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
}