Android 10 では、安定版(stable)の Android インターフェース定義言語(AIDL)のサポートが追加されています。これは AIDL インターフェースによって提供される、アプリケーション プログラム インターフェース(API)やアプリケーション バイナリ インターフェース(ABI)を追跡する新しい方法です。安定版 AIDL と AIDL との主な違いは次のとおりです。
- インターフェースは、ビルドシステムで
aidl_interfaces
を使用して定義されています。 - インターフェースには、構造化データのみを含めることができます。目的のタイプを表す Parcelable は、AIDL 定義に基づいて自動的に作成され、自動的にマーシャリングとマーシャリング解除が行われます。
- インターフェースは、stable(下位互換性のある)として宣言できます。この場合、API は AIDL インターフェースの横のファイルで追跡およびバージョン管理されます。
AIDL インターフェースの定義
aidl_interface
の定義は次のようになります。
aidl_interface {
name: "my-module-name",
local_include_dir: "tests_1",
srcs: [
"tests_1/some/package/IFoo.aidl",
"tests_1/some/package/Thing.aidl",
],
api_dir: "api/test-piece-1",
versions: ["1"],
}
name
: モジュールの名前この場合、my-module-name-java
とmy-module-name-cpp
という、対応する言語の 2 つのスタブ ライブラリが作成されます。C++ ライブラリが作成されないようにするには、gen_cpp
を使用します。これにより、API の確認や更新に使用できるビルドシステムの追加のアクションも作成されます。local_include_dir
: パッケージの開始ディレクトリへのパス。srcs
: ターゲット言語にコンパイルされる安定版 AIDL ソースファイルのリスト。api_dir
: インターフェースの以前のバージョンの API 定義がダンプされるディレクトリへのパス。インターフェースに加えられた変更により API を壊さないよう確かめるために使用されます(以下で説明するプロセスをご覧ください)。versions
:api_dir
の下で固定されている以前のバージョンのインターフェース。これは省略可能です。
AIDL ファイルの書き込み
安定版 AIDL のインターフェースは、従来のインターフェースと似ていますが、構造化されていない Parcelable は使用できません(安定していないため)。安定版 AIDL の主な違いは、Parcelable の定義方法です。以前は、Parcelable は前方宣言されていました。安定版 AIDL では、Parcelable のフィールドと変数は明示的に定義されています。
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
デフォルトでは boolean
、char
、float
、double
、byte
、int
、long
、String
が現在サポートされていますが、必須ではありません。
スタブ ライブラリの使用
モジュールにスタブ ライブラリを依存関係として追加した後で、それらをファイルに含めることができます。ビルドシステムのスタブ ライブラリの例を次に示します(Android.mk
は従来のモジュールの定義にも使用できます)。
cc_... {
name: ...,
shared_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
static_libs: ["my-module-name-java"],
...
}
C++ の例:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
Java の例:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
バージョニング インターフェース
foo という名前のモジュールを宣言すると、モジュールの API の管理に使用できるターゲットもビルドシステムに作成されます。ビルド時に、foo-freeze-api により、インターフェースの次のバージョンで使用できる新しい API 定義が api_dir
の下に追加されます。
インターフェースの安定性を維持するために、以下を追加できます。
- メソッドの終わりまでのメソッド(または明示的に定義された新しいシリアル化を追加したメソッド)
- パーセルの終わりまでの要素(要素ごとにデフォルトの追加が必要)
その他の操作は許可されません。
新しいメタ インターフェース メソッド
Android 10 では、安定版 AIDL 用のメタ インターフェース メソッドがいくつか追加されています。
リモート オブジェクトのインターフェース バージョンの照会
クライアントは、リモート オブジェクトが実装しているインターフェースのバージョンを照会し、返ってきたバージョンをクライアントが使用しているインターフェースのバージョンと比較できます。
C++ の例:
sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
Java の例:
IFoo foo = ... // the remote object
int my_ver = IFoo.VERSION;
int remote_ver = foo.getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
Java 言語の場合、リモート側で getInterfaceVersion()
を次のように実装する必要があります。
class MyFoo extends IFoo.Stubs {
@Override
public final int getInterfaceVersion() { return IFoo.VERSION; }
}
これは、生成されたクラス(IFoo
、IFoo.Stubs
など)がクライアントとサーバーの間で共有されるためです(たとえば、クラスはブート クラスパスに含めることができます)。クラスが共有されている場合、古いバージョンのインターフェースでビルドされていても、サーバーは最新バージョンのクラスにもリンクされます。このメタ インターフェースが共有クラスに実装されている場合、常に最新のバージョンが返されます。ただし、上記のメソッドを実装すると、インターフェースのバージョン番号がサーバーのコードに埋め込まれます(IFoo.VERSION
は参照時にインラインされる static final int
であるため)。したがってこのメソッドは、サーバーがビルドされた正確なバージョンを返すことができます。
古いインターフェースの使用
クライアントが新しいバージョンの AIDL インターフェースで更新され、サーバーが古い AIDL インターフェースを使用している可能性があります。そのような場合、クライアントは古いインターフェースに存在しない新しいメソッドを呼び出すことはできません。安定版 AIDL の前は、このような存在しないメソッドの呼び出しは静に無視されたため、クライアントはメソッドが呼び出されたかどうかさえわかりませんでした。
安定版 AIDL を使用すると、クライアントはより多くの制御を行えます。クライアント側では、デフォルトの実装を AIDL インターフェースに設定できます。メソッドがリモート側で実装されていない場合のみ、デフォルトの実装のメソッドが呼び出されます(古いバージョンのインターフェースでビルドされているため)。
C++ の例:
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(std::unique_ptr<IFoo>(MyDefault));
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
Java の例:
IFoo.Stubs.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
AIDL インターフェースでは、すべてのメソッドのデフォルトの実装を指定する必要はありません。リモート側で実装されることが保証されているメソッドは、デフォルトの impl
クラスでオーバーライドする必要はありません(それらのメソッドが AIDL インターフェースの記述内にあれば、リモート側で確実にビルドされるため)。
既存の AIDL の構造化または安定版 AIDL への変換
既存の AIDL インターフェースとそれを使用するコードがある場合は、次の手順でインターフェースを安定版 AIDL インターフェースに変換します。
インターフェースのすべての依存関係を特定します。インターフェースが依存しているすべてのパッケージについて、パッケージが安定版 AIDL で定義されているかどうかを判断します。定義されていない場合は、パッケージを変換する必要があります。
インターフェース内のすべての Parcelable を stable Parcelable に変換します(インターフェース ファイル自体は変更されません)。AIDL ファイルで直接構造を表現することで、変換を実行します。これらの新しいタイプを使用するには、管理クラスを書き直す必要があります。これは、
aidl_interface
パッケージの作成前に行うことができます(下記参照)。前述のとおり、モジュールの名前、依存関係、その他の必要な情報が含まれている
aidl_interface
パッケージを作成します。 構造化だけでなく安定化のためには、api_dir
パスも指定します。