Android インターフェース定義言語(AIDL)

Android インターフェース定義言語(AIDL)は、 IDL: プログラミング インタフェースを定義して、 クライアントとサービスは、API を使用して相互に通信するために プロセス間通信(IPC)です。

Android では、1 つのプロセスが通常は メモリを消費します話すには、オブジェクトをプリミティブに分解して、 境界を越えてオブジェクトを認識して整理できます。このコードを マーシャリングは面倒な記述なので、Android が AIDL を使って処理します。

注: AIDL は、クライアントが外部 IP アドレスから さまざまなアプリケーションが IPC のサービスにアクセスしており、マルチスレッド処理を あります。ネットワーク全体で IPC を同時に実行する必要がない場合は、 インターフェースを作成するには、 Binder。 IPC を実行したいが、マルチスレッドを処理する必要はない場合は、 Messenger を使用してインターフェースを実装します。 いずれにしても、バインドされたサービスを理解してから、 AIDL の実装です。

AIDL インターフェースの設計を開始する前に、AIDL インターフェースの呼び出しは 直接関数呼び出しを行えます。呼び出しが行われているスレッドについて想定しない 発生します。処理は、呼び出しがスレッド内のスレッドから行われたか ローカル プロセスまたはリモート プロセス:

  • ローカル プロセスからの呼び出しは、呼び出しを行ったスレッドと同じスレッドで実行されます。条件 メイン UI スレッドの場合、そのスレッドは引き続き AIDL インターフェースで実行されます。もし 別のスレッド、つまりサービス内でコードを実行するスレッドです。したがって、ローカル接続にのみ サービスにアクセスしているスレッドの数に応じて、サービスで実行するスレッドを完全に制御できます。しかし、 その場合は AIDL をまったく使用しないでください。代わりに インターフェースに Binder
  • リモート プロセスからの呼び出しは、プラットフォームが内部で維持しているスレッドプールからディスパッチされる できます。複数の呼び出しを伴う不明なスレッドからの着信呼び出しに備える 同時に発生します。つまり、AIDL インターフェースの実装は、 完全にスレッドセーフです。同じリモート オブジェクト上の 1 つのスレッドからの呼び出し 受信側で順番に到着します。
  • oneway キーワードは、リモート呼び出しの動作を変更します。リモート呼び出しを使用すると、リモート呼び出しは ブロックされません。トランザクション データを送信し、すぐに戻ります。 インターフェースの実装は最終的にこれを、通常のリモート呼び出しとして Binder スレッドプールからの通常の呼び出しとして受け取ります。oneway をローカル通話で使用する場合、 影響はなく、呼び出しは引き続き同期的です。

AIDL インターフェースの定義

Java を使用して .aidl ファイルで AIDL インターフェースを定義する 保存して、ソースコード内の src/ ディレクトリ内に保存します。 サービスをホストするアプリケーションと、サービスにバインドするその他のアプリケーション。

.aidl ファイルを含む各アプリをビルドすると、Android SDK Tools .aidl ファイルに基づいて IBinder インターフェースを生成し、 プロジェクトの gen/ ディレクトリに配置します。サービスは IBinder を実装する必要があります。 インターフェースを使用します。その後、クライアント アプリケーションはサービスにバインドして、 IBinder を呼び出して IPC を実行します。

AIDL を使用して制限付きサービスを作成する手順は次のとおりです。 以降のセクションで説明します

  1. .aidl ファイルを作成する

    このファイルでは、メソッド シグネチャを使用してプログラミング インターフェースを定義します。

  2. インターフェースを実装する

    Android SDK Tools は、Java プログラミング言語で記述されたインターフェースを、 .aidl ファイル。このインターフェースには、Stub という名前の内部抽象クラスがあり、 Binder を作成し、AIDL インターフェースのメソッドを実装します。この関数を Stub クラスを使用してメソッドを実装します。

  3. クライアントにインターフェースを公開する

    Service を実装して onBind() をオーバーライドして、Stub の実装を返します。 クラスです。

注意: 変更後に AIDL インターフェースに変更を加えると、 他のアプリの互換性を損なわないよう、最初のリリースで下位互換性を維持する必要がある モニタリングします。これは、.aidl ファイルを他のアプリにコピーする必要があるためです。 サービスのインターフェースにアクセスできるようにするには、元のインターフェースのサポートを維持する必要があります。 行うことができます。

.aidl ファイルを作成する

AIDL では、インターフェースを宣言するための簡単な構文を使用します。この構文を使用すると、インターフェースの パラメータを受け取って値を返します。パラメータと戻り値の型は自由ですが、他のタイプや AIDL 生成インターフェース。

.aidl ファイルは、Java プログラミング言語を使用して作成する必要があります。各.aidl ファイルは単一のインターフェースを定義する必要があり、インターフェースの宣言とメソッドのみが必要 できます。

デフォルトでは、AIDL は次のデータ型をサポートしています。

  • Java プログラミング言語のすべてのプリミティブ型(intlongcharboolean など)
  • プリミティブ型の配列(int[] など)
  • String
  • CharSequence
  • List

    List 内のすべての要素は、サポートされているデータ型のいずれかである必要があります。 宣言する他の AIDL 生成インターフェースまたは Parcelable のいずれか 1 つを指定できます。 必要に応じて、List をパラメータ化された型クラスとして使用できます。次に例を示します。 List<String>。 相手側が受け取る実際の具象クラスは常に ArrayList ですが、 メソッドが生成され、List インターフェースが使用されます。

  • Map

    Map 内のすべての要素は、サポートされているデータ型のいずれかである必要があります。 宣言する他の AIDL 生成インターフェースまたは Parcelable のいずれか 1 つを指定できます。パラメータ化された型マップ、 たとえば Map<String,Integer> はサポートされていません。もう一方の 常に HashMap になります。 ただし、メソッドは Map インターフェースを使用するように生成されます。次を検討する: Map の代わりに Bundle

上記以外のタイプごとに import ステートメントを含める必要があります。 インターフェースと同じパッケージで定義されている場合でも同じです。

サービス インターフェースを定義するときは、次の点に注意してください。

  • メソッドはゼロ以上のパラメータを受け取り、値または void を返すことができます。
  • すべての非プリミティブ パラメータには、データの向きを示す方向タグが必要です。 inout、または inout(下記の例を参照)。

    プリミティブ、StringIBinder、AIDL で生成されるもの インターフェースはデフォルトで in です。それ以外の場合は、指定できません。

    注意: 方向を本当に正しいものに絞り込んでください。 パラメータのマーシャリングにはコストがかかるため、

  • .aidl ファイルに含まれるすべてのコードコメントが、 収益: IBinder インターフェース(インポートとパッケージの前のコメントは除く) ステートメント。
  • 文字列定数と int 定数は、AIDL インターフェースで定義できます(const int VERSION = 1; など)。
  • メソッド呼び出しは transact() code。通常はインターフェースのメソッド インデックスに基づいています。これは、 バージョニングが困難になります。 メソッド void method() = 10; にトランザクション コードを手動で割り当てることができます。
  • null 値許容の引数と戻り値の型には、@nullable を使用してアノテーションを付ける必要があります。

.aidl ファイルの例を次に示します。

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

.aidl ファイルをプロジェクトの src/ ディレクトリに保存します。ユーザーが アプリケーションをビルドすると、SDK ツールによって IBinder インターフェース ファイルが gen/ ディレクトリに配置します。生成されたファイルの名前は .aidl ファイルの名前と一致しますが、 .java 拡張子を使用します。たとえば、IRemoteService.aidlIRemoteService.java になります。

Android Studio を使用する場合、増分ビルドによってほぼ即座にバインダー クラスが生成されます。 Android Studio を使用しない場合、Gradle ツールは次回実行時にバインダー クラスを生成します アプリケーションを構築します。gradle assembleDebug でプロジェクトをビルドする またはgradle assembleReleaseファイルの作成が終わったら、.aidlすぐに 生成されたクラスにコードをリンクできるようにします。

インターフェースを実装する

アプリをビルドすると、Android SDK Tools によって .java インターフェース ファイルが生成されます。 .aidl ファイルにちなんだ名前が付けられます。生成されたインターフェースには、Stub という名前のサブクラスが含まれます。 これは、親インターフェースの抽象実装(YourInterface.Stub など)であり、.aidl ファイルのすべてのメソッドを宣言します。

注: Stub も いくつかのヘルパー メソッドを定義します。特に asInterface()IBinder(通常はクライアントの onServiceConnected() コールバック メソッドに渡されるもの)を受け取ります。 スタブ インターフェースのインスタンスを返します。このキャストを行う方法について詳しくは、「IPC の呼び出し」をご覧ください。 メソッド

.aidl から生成されたインターフェースを実装するには、生成された Binder を拡張します。 インターフェース(YourInterface.Stub など)を作成し、 .aidl ファイルから継承されます。

以下は、IRemoteService というインターフェースの実装例です。これは、上記の 匿名インスタンスを使用した IRemoteService.aidl の例:

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

これで、binderStub クラス(Binder)のインスタンスになりました。 Service の IPC インターフェースを定義します。次のステップでは、このインスタンスを クライアントがサービスとやり取りできるようにします。

AIDL インターフェースを実装する際は、次のルールに注意してください。

  • 着信がメインスレッドで実行されるとは限らないため、 マルチスレッド処理について学び、サービスがスレッドセーフになるよう適切に構築する必要があります。
  • デフォルトでは、IPC 呼び出しは同期的です。このサービスに 2 ~ 3 回以上かかることが ミリ秒未満しか必要ないため、アクティビティのメインスレッドから呼び出さないでください。 アプリがハングし、Android に「Application is Not Responding」と表示されることがある クリックします。クライアントの別のスレッドから呼び出す。
  • リファレンス ドキュメントに記載されている例外タイプのみ Parcel.writeException() 呼び出し元に返されます

インターフェースをクライアントに公開する

サービスのインターフェースを実装したら、 クライアントをバインドできるようにします。インターフェースを公開する サービスに対して、Service を拡張して onBind() を実装することで、 生成された Stub(前のセクションで説明したとおり)。例 IRemoteService サンプル インターフェースをクライアントに公開するサービス。

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

これで、アクティビティなどのクライアントが bindService() を呼び出してこのサービスに接続すると、クライアントの onServiceConnected() コールバックは サービスの onBind() によって返される binder インスタンス メソッドを呼び出します。

また、クライアントはインターフェース クラスにアクセスできる必要があります。そのため、クライアントとサービスが 別のアプリケーションを使用する場合は、クライアントのアプリケーションに .aidl ファイルのコピーが必要です。 その src/ ディレクトリ。これにより、android.os.Binder が生成されます。 クライアントが AIDL メソッドにアクセスできるようにします。

クライアントが IBinder を受信したとき onServiceConnected() コールバック内では、 YourServiceInterface.Stub.asInterface(service): 返された パラメータを 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;
    }
};

その他のサンプルコードについては、 <ph type="x-smartling-placeholder"></ph> RemoteService.java クラス <ph type="x-smartling-placeholder"></ph> ApiDemos

IPC 経由でオブジェクトを渡す

Android 10(API レベル 29 以降)では、 Parcelable オブジェクトを直接 AIDL です。AIDL インターフェース引数やその他の Parcelable としてサポートされている型も、 できません。これにより、マーシャリング コードを手動で記述したり、カスタムのソースコードを クラスです。ただし、これによって基本的な構造体も作成されます。カスタム アクセサやその他の機能が 代わりに 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;
}

上記のコードサンプルでは、整数フィールド left を含む Java クラスが自動的に生成されます。 toprightbottom。関連するすべてのマーシャリング コードは、 追加せずにオブジェクトを直接使用できます。 説明します。

IPC インターフェースを介して、あるプロセスから別のプロセスにカスタムクラスを送信することもできます。ただし、 クラスのコードが IPC チャネルの相手側からアクセス可能であることを確認する。 クラスが Parcelable インターフェースをサポートしている必要があります。サポート Parcelable は重要です なぜなら、Android システムがオブジェクトをプリミティブに分解し、マーシャリングできるからです。 必要があります。

Parcelable をサポートするカスタムクラスを作成する手順は次のとおりです。 次のとおりです。

  1. クラスに Parcelable インターフェースを実装させます。
  2. writeToParcel を実装します。これは、 オブジェクトの現在の状態を取得し、Parcel に書き込みます。
  3. クラスに CREATOR という静的フィールドを追加します。 Parcelable.Creator インターフェース。
  4. 最後に、以下に示すように、Parcelable クラスを宣言する .aidl ファイルを作成します。 Rect.aidl ファイル。

    カスタム ビルドプロセスを使用する場合は、.aidl ファイルを 構築できます。C 言語のヘッダー ファイルと同様に、この .aidl ファイルはコンパイルされません。

AIDL は、生成されたコード内のこれらのメソッドとフィールドを使用して、マーシャリングとマーシャリング解除を行います。 管理できます。

たとえば、次のような Rect クラスを作成する Rect.aidl ファイルがあるとします。 Parcelable:

package android.graphics;

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

また、以下の図は、Rect クラスが Parcelable プロトコル。

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

Rect クラスのマーシャリングは単純です。もう一つは、 メソッドを Parcel で参照して、ユーザーが書き込める他の種類の値を表示できます。 Parcel にマッピング。

警告: メールの受信はセキュリティ上の影響を 他のプロセスからのデータです。この場合、RectParcel から 4 つの数値を読み取りますが、これらの数値が許容範囲内にあるかを自分で確認する必要があります。 呼び出し元が行おうとするものが何でも構いません。マルウェアからアプリケーションを保護する方法について詳しくは、セキュリティに関するヒントをご覧ください。

Parcelable を含む Bundle 引数を使用するメソッド

メソッドが Bundle オブジェクトを受け入れるかどうか Parcelable を使用するには、クラスローダーを Bundle 読み取りを行う前に Bundle.setClassLoader(ClassLoader) を呼び出す Bundle から。そうしないと、Parcelable がアプリで正しく定義されていても ClassNotFoundException が発生します。

たとえば、次のサンプル .aidl ファイルについて考えてみましょう。

// 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);
}
次の実装に示すように、ClassLoaderRect を読み取る前に Bundle で明示的に設定する:

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 メソッドを呼び出す

AIDL で定義されたリモート インターフェースを呼び出すには、次の手順を実施します。 呼び出すクラス:

  1. プロジェクトの src/ ディレクトリに .aidl ファイルを含めます。
  2. IBinder インターフェースのインスタンスを宣言します。このインターフェースは AIDL です。
  3. ServiceConnection を実装します。
  4. Context.bindService() にお電話ください。 ServiceConnection の実装を渡します。
  5. onServiceConnected() の実装で、次のように設定します。 IBinder を受け取る service という名前のインスタンスに作成されます。発信 YourInterfaceName.Stub.asInterface((IBinder)service)~ 返されたパラメータを YourInterface 型にキャストします。
  6. インターフェースで定義したメソッドを呼び出します。常にトラップ DeadObjectException 例外。次の場合にスローされます。 接続が切断されます。また、SecurityException 例外をトラップします。この例外は、IPC メソッド呼び出しに関与する 2 つのプロセスに競合する AIDL 定義がある場合にスローされます。
  7. 接続を解除するには、インターフェースのインスタンスを指定して Context.unbindService() を呼び出します。

IPC サービスを呼び出すときは、次の点に注意してください。

  • オブジェクトはプロセス間で有効な参照です。
  • 匿名のオブジェクトを送信したり、 使用できます。

サービスへのバインディングについて詳しくは、バインドされたサービスの概要をご覧ください。

以下に、AIDL で作成されたサービスを呼び出すサンプルコードをいくつか示します。 リモート サービス サンプルから抜粋したコードです。

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