Android 10, AIDL arabirimleri tarafından sağlanan uygulama programı arabirimini (API)/uygulama ikili arabirimini (ABI) izlemenin yeni bir yolu olan kararlı Android Arabirim Tanımlama Dili (AIDL) için destek ekler. Kararlı AIDL, AIDL'den aşağıdaki temel farklılıklara sahiptir:
- Arayüzler yapı sisteminde
aidl_interfaces
ile tanımlanır. - Arayüzler yalnızca yapılandırılmış verileri içerebilir. İstenen türleri temsil eden parseller, AIDL tanımlarına göre otomatik olarak oluşturulur ve otomatik olarak sıralanır ve sıralanmaz.
- Arayüzler kararlı (geriye dönük uyumlu) olarak bildirilebilir. Bu olduğunda, API'leri AIDL arayüzünün yanındaki bir dosyada izlenir ve sürümlendirilir.
AIDL arayüzü tanımlama
aidl_interface
tanımı şöyle görünür:
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions: ["1", "2"],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
},
}
-
name
: Bir AIDL arabirimini benzersiz olarak tanımlayan AIDL arabirim modülünün adı. -
srcs
: Arabirimi oluşturan AIDL kaynak dosyalarının listesi.com.acme
paketinde tanımlananFoo
AIDL tipinin yolu,<base_path>/com/acme/Foo.aidl
olmalıdır; burada<base_path>
,Android.bp
bulunduğu dizinle ilgili herhangi bir dizin olabilir. Yukarıdaki örnekte,<base_path>
srcs/aidl
. -
local_include_dir
: Paket adının başladığı yol. Yukarıda açıklanan<base_path>
ile uyumludur. -
imports
: Bunun kullandığıaidl_interface
modüllerinin listesi. AIDL arayüzlerinizden biri bir arayüz veya başka biraidl_interface
ayrıştırılabilir bir arayüz kullanıyorsa, adını buraya yazın. Bu, en son sürüme atıfta bulunmak için tek başına ad veya belirli bir sürüme atıfta bulunmak için sürüm son ekine sahip ad (-V1
gibi) olabilir. Sürüm belirtme Android 12'den beri desteklenmektedir - version :
api_dir
altında donmuş olan öncekiversions
, Android 11'den itibaren,versions
aidl_api/ name
altında dondurulur. Bir arayüzün donmuş sürümleri yoksa, bu belirtilmemelidir ve uyumluluk kontrolleri yapılmayacaktır. -
stability
: Bu arabirimin kararlılık vaadi için isteğe bağlı bayrak. Şu anda yalnızca"vintf"
destekler. Bu ayarlanmamışsa, bu derleme bağlamında kararlı bir arabirime karşılık gelir (bu nedenle buraya yüklenen bir arabirim yalnızca birlikte derlenen şeylerle kullanılabilir, örneğin system.img üzerinde). Bu"vintf"
olarak ayarlanırsa, bu bir kararlılık vaadine karşılık gelir: arayüz kullanıldığı sürece sabit tutulmalıdır. -
gen_trace
: İzlemeyi açmak veya kapatmak için isteğe bağlı bayrak. Varsayılanfalse
. -
host_supported
:true
olarak ayarlandığında oluşturulan kitaplıkları ana bilgisayar ortamında kullanılabilir hale getiren isteğe bağlı bayrak. -
unstable
: Bu arabirimin kararlı olması gerekmediğini belirtmek için kullanılan isteğe bağlı bayrak. Butrue
olarak ayarlandığında, derleme sistemi arabirim için API dökümü oluşturmaz veya güncellenmesini gerektirmez. -
backend.<type>.enabled
: Bu bayraklar, AIDL derleyicisinin kod üreteceği arka uçların her birini değiştirir. Şu anda üç arka uç desteklenmektedir:java
,cpp
vendk
. Arka uçların tümü varsayılan olarak etkindir. Belirli bir arka uca ihtiyaç duyulmadığında, açıkça devre dışı bırakılması gerekir. -
backend.<type>.apex_available
: Oluşturulan saplama kitaplığının kullanılabilir olduğu APEX adlarının listesi. -
backend.[cpp|java].gen_log
: İşlem hakkında bilgi toplamak için ek kod oluşturulup oluşturulmayacağını kontrol eden isteğe bağlı bayrak. -
backend.[cpp|java].vndk.enabled
: Bu arabirimi VNDK'nın bir parçası yapmak için isteğe bağlı bayrak. Varsayılanfalse
. -
backend.java.platform_apis
: Java saplama kitaplığının platformdaki özel API'lere karşı oluşturulup oluşturulmadığını kontrol eden isteğe bağlı bayrak.stability
"vintf"
olarak ayarlandığında bu"true"
olarak ayarlanmalıdır. -
backend.java.sdk_version
: Java saplama kitaplığının oluşturulduğu SDK sürümünü belirtmek için isteğe bağlı bayrak. Varsayılan"system_current"
.backend.java.platform_apis
true olduğunda bu ayarlanmamalıdır. -
backend.java.platform_apis
: Oluşturulan kitaplıkların SDK yerine platform API'sine karşı oluşturulması gerektiğindetrue
olarak ayarlanması gereken isteğe bağlı bayrak.
versions
ve etkinleştirilmiş arka uçların her bir kombinasyonu için bir saplama kitaplığı oluşturulur. Belirli bir arka uç için saplama kitaplığının belirli sürümüne nasıl başvurulacağına ilişkin Modül adlandırma kurallarına bakın.
AIDL dosyalarının yazılması
Kararlı AIDL'deki arabirimler, yapılandırılmamış parsellenebilirleri kullanmalarına izin verilmemesi (çünkü bunlar kararlı değildir!) dışında geleneksel arabirimlere benzer. Kararlı AIDL'deki birincil fark, parsellenebilirlerin nasıl tanımlandığıdır. Daha önce parsellenebilirler ileriye dönük beyan ediliyordu; kararlı AIDL'de, parsellenebilir alanlar ve değişkenler açıkça tanımlanır.
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
Şu anda boolean
, char
, float
, double
, byte
, int
, long
ve String
için bir varsayılan desteklenmektedir (ancak gerekli değildir). Android 12'de, kullanıcı tanımlı numaralandırmalar için varsayılanlar da desteklenir. Bir varsayılan belirtilmediğinde, 0 benzeri veya boş bir değer kullanılır. Varsayılan değeri olmayan numaralandırmalar, sıfır numaralandırıcı olmasa bile 0 olarak başlatılır.
Saplama kitaplıklarını kullanma
Modülünüze bağımlılık olarak saplama kitaplıkları ekledikten sonra, bunları dosyalarınıza dahil edebilirsiniz. İşte derleme sistemindeki saplama kitaplıklarının örnekleri ( Android.mk
, eski modül tanımları için de kullanılabilir):
cc_... {
name: ...,
shared_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// can also be shared_libs if desire is to load a library and share
// it among multiple users or if you only need access to constants
static_libs: ["my-module-name-java"],
...
}
C++'da örnek:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
Java'da örnek:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
Sürüm oluşturma arayüzleri
Adı foo olan bir modül bildirmek, yapı sisteminde modülün API'sini yönetmek için kullanabileceğiniz bir hedef de oluşturur. Foo-freeze-api , oluşturulduğunda, Android sürümüne bağlı olarak api_dir
veya aidl_api/ name
altına yeni bir API tanımı ekler ve her ikisi de arayüzün yeni donmuş sürümünü temsil eden bir .hash
dosyası ekler. Bunu oluşturmak, ek versions
yansıtacak şekilde version özelliğini de günceller. Versions özelliği belirtildiğinde, yapı sistemi donmuş versions
arasında ve ayrıca Ağacın Başı (ToT) ile en son donmuş sürüm arasında uyumluluk kontrolleri çalıştırır.
Ayrıca ToT sürümünün API tanımını da yönetmeniz gerekir. Bir API güncellendiğinde, ToT sürümünün API tanımını içeren aidl_api/ name /current
güncellemek için foo-update- api'yi çalıştırın.
Bir arayüzün kararlılığını korumak için sahipler yenilerini ekleyebilir:
- Bir arabirimin sonuna kadar yöntemler (veya açıkça tanımlanmış yeni dizilere sahip yöntemler)
- Parçalanabilir bir öğenin sonuna kadar olan öğeler (her öğe için bir varsayılanın eklenmesini gerektirir)
- sabit değerler
- Android 11'de numaralandırıcılar
- Android 12'de, bir birliğin sonuna kadar alanlar
Başka hiçbir eyleme izin verilmez ve başka hiç kimse bir arabirimi değiştiremez (aksi takdirde, bir sahibin yaptığı değişikliklerle çakışma riski vardır).
Sürümlü arayüzleri kullanma
Arayüz yöntemleri
Çalışma zamanında, eski bir sunucuda yeni yöntemler çağırmaya çalışırken, yeni istemciler arka uca bağlı olarak ya bir hata ya da bir istisna alır.
-
cpp
arka ucu::android::UNKNOWN_TRANSACTION
alır. -
ndk
arka ucuSTATUS_UNKNOWN_TRANSACTION
alır. -
java
arka ucu, API'nin uygulanmadığını söyleyen bir mesajlaandroid.os.RemoteException
alır.
Bunu ele alma stratejileri için sürümleri sorgulama ve varsayılanları kullanma konusuna bakın.
parsellenebilirler
Parçalanabilirlere yeni alanlar eklendiğinde, eski istemciler ve sunucular bunları bırakır. Yeni istemciler ve sunucular eski parsellenebilirleri aldığında, yeni alanlar için varsayılan değerler otomatik olarak doldurulur. Bu, bir parsellenebilirdeki tüm yeni alanlar için varsayılanların belirtilmesi gerektiği anlamına gelir.
İstemciler, sunucunun, alan tanımlı olan sürümü uyguladığını bilmedikçe, sunucuların yeni alanları kullanmasını beklememelidir ( sorgulama sürümlerine bakın).
Numaralandırmalar ve sabitler
Benzer şekilde, istemciler ve sunucular, gelecekte daha fazlası eklenebileceğinden, tanınmayan sabit değerleri ve numaralandırıcıları uygun şekilde reddetmeli veya yoksaymalıdır. Örneğin, bir sunucu bilmediği bir numaralandırıcı aldığında iptal etmemelidir. Ya yok saymalı ya da müşterinin bu uygulamada desteklenmediğini bilmesi için bir şey döndürmelidir.
sendikalar
Alıcı eskiyse ve alanı bilmiyorsa, yeni bir alanla bir birlik göndermeye çalışmak başarısız olur. Uygulama, yeni alan ile birliği asla görmeyecektir. Tek yönlü bir işlemse, başarısızlık yoksayılır; aksi takdirde hata BAD_VALUE
(C++ veya NDK arka ucu için) veya IllegalArgumentException
(Java arka ucu için) olur. İstemci eski bir sunucuya yeni alana bir birleşim seti gönderiyorsa veya eski bir istemci birliği yeni bir sunucudan alıyorsa hata alınır.
Modül adlandırma kuralları
Android 11'de, sürümlerin ve etkinleştirilen arka uçların her bir kombinasyonu için otomatik olarak bir saplama kitaplığı modülü oluşturulur. Bağlama için belirli bir saplama kitaplığı modülüne başvurmak için, aidl_interface
modülünün adını değil, saplama kitaplığı modülünün adını, yani ifacename - version - backend kullanın, burada
-
ifacename
:aidl_interface
modülünün adı -
version
herhangi biri- Dondurulmuş versiyonlar için
V version-number
- Ağacın ucu (henüz dondurulmamış) sürüm için
V latest-frozen-version-number + 1
- Dondurulmuş versiyonlar için
-
backend
biridir:- Java arka ucu için
java
, - C++ arka ucu için
cpp
, - NDK arka ucu için
ndk
veyandk_platform
. Birincisi uygulamalar içindir ve ikincisi platform kullanımı içindir.
- Java arka ucu için
Foo adında bir modül olduğunu ve en son sürümünün 2 olduğunu ve hem NDK'yı hem de C++'ı desteklediğini varsayalım. Bu durumda, AIDL şu modülleri oluşturur:
- Sürüm 1'e göre
-
foo-V1-(java|cpp|ndk|ndk_platform)
-
- Sürüm 2'ye göre (en son kararlı sürüm)
-
foo-V2-(java|cpp|ndk|ndk_platform)
-
- ToT sürümüne göre
-
foo-V3-(java|cpp|ndk|ndk_platform)
-
Android 11 ile karşılaştırıldığında,
- En son kararlı sürüme atıfta bulunulan
foo- backend
,foo- V2 - backend
olur - ToT sürümüne atıfta bulunulan
foo-unstable- backend
,foo- V3 - backend
olur
Çıktı dosyası adları her zaman modül adlarıyla aynıdır.
- Sürüm 1:
foo-V1-(cpp|ndk|ndk_platform).so
- 2. sürüme göre:
foo-V2-(cpp|ndk|ndk_platform).so
- ToT sürümüne dayalıdır:
foo-V3-(cpp|ndk|ndk_platform).so
AIDL derleyicisinin, kararlı bir AIDL arabirimi için unstable
bir sürüm modülü veya sürümsüz bir modül oluşturmadığını unutmayın. Android 12'den itibaren, kararlı bir AIDL arayüzünden oluşturulan modül adı her zaman sürümünü içerir.
Yeni meta arayüz yöntemleri
Android 10, kararlı AIDL için birkaç meta arayüz yöntemi ekler.
Uzak nesnenin arayüz versiyonunu sorgulama
İstemciler, uzak nesnenin uyguladığı arabirimin sürümünü ve karmasını sorgulayabilir ve döndürülen değerleri istemcinin kullandığı arabirimin değerleriyle karşılaştırabilir.
cpp
arka ucu ile örnek:
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
}
std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();
ndk
(ve ndk_platform
) arka ucuyla ilgili örnek:
IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);
java
arka ucu ile örnek:
IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
// the remote side is using an older interface
}
String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();
Java dili için, uzak taraf getInterfaceVersion()
ve getInterfaceHash()
'ı aşağıdaki gibi uygulamalıdır:
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return IFoo.VERSION; }
@Override
public final String getInterfaceHash() { return IFoo.HASH; }
}
Bunun nedeni, oluşturulan sınıfların ( IFoo
, IFoo.Stub
, vb.) istemci ve sunucu arasında paylaşılmasıdır (örneğin, sınıflar önyükleme sınıf yolunda olabilir). Sınıflar paylaşıldığında, sunucu, arayüzün daha eski bir sürümüyle oluşturulmuş olsa bile, sınıfların en yeni sürümüne de bağlanır. Bu meta arabirim paylaşılan sınıfta uygulanırsa, her zaman en yeni sürümü döndürür. Ancak, yöntemi yukarıdaki gibi uygulayarak, arabirimin sürüm numarası sunucunun koduna gömülür (çünkü IFoo.VERSION
, başvurulduğunda satır içi olan static final int
int'dir) ve böylece yöntem, sunucunun tam olarak oluşturulduğu sürümü döndürebilir. ile.
Eski arayüzlerle uğraşmak
İstemcinin AIDL arabiriminin daha yeni sürümüyle güncellenmesi ancak sunucunun eski AIDL arabirimini kullanması mümkündür. Bu gibi durumlarda, eski bir arabirimde bir yöntemin çağrılması, UNKNOWN_TRANSACTION
döndürür.
Kararlı AIDL ile müşteriler daha fazla kontrole sahiptir. İstemci tarafında, bir AIDL arayüzüne varsayılan bir uygulama ayarlayabilirsiniz. Varsayılan uygulamada bir yöntem, yalnızca yöntem uzak tarafta uygulanmadığında çağrılır (çünkü arabirimin daha eski bir sürümüyle oluşturulmuştur). Varsayılanlar genel olarak ayarlandığından, potansiyel olarak paylaşılan bağlamlardan kullanılmamalıdırlar.
Android T (AOSP deneysel) ve sonraki sürümlerde C++ örneği:
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
Java'da örnek:
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
Bir AIDL arabirimindeki tüm yöntemlerin varsayılan uygulamasını sağlamanız gerekmez. Uzak tarafta uygulanması garanti edilen yöntemlerin (yöntemler AIDL arabirim açıklamasındayken oluşturulduğundan emin olduğunuz için) varsayılan impl
sınıfında geçersiz kılınması gerekmez.
Mevcut AIDL'yi yapılandırılmış/kararlı AIDL'ye dönüştürme
Mevcut bir AIDL arayüzünüz ve onu kullanan kodunuz varsa, arayüzü kararlı bir AIDL arayüzüne dönüştürmek için aşağıdaki adımları kullanın.
Arayüzünüzün tüm bağımlılıklarını tanımlayın. Arayüzün bağlı olduğu her paket için, paketin kararlı AIDL'de tanımlanıp tanımlanmadığını belirleyin. Tanımlanmamışsa, paket dönüştürülmelidir.
Arayüzünüzdeki tüm ayrıştırılabilirleri kararlı ayrıştırılabilirlere dönüştürün (arayüz dosyalarının kendileri değişmeden kalabilir). Bunu, yapılarını doğrudan AIDL dosyalarında ifade ederek yapın. Bu yeni türleri kullanmak için yönetim sınıfları yeniden yazılmalıdır. Bu, bir
aidl_interface
paketi oluşturmadan önce yapılabilir (aşağıda).Modülünüzün adını, bağımlılıklarını ve ihtiyacınız olan diğer bilgileri içeren bir
aidl_interface
paketi (yukarıda açıklandığı gibi) oluşturun. Bunu stabilize etmek için (sadece yapılandırılmış değil), aynı zamanda versiyonlanması gerekir. Daha fazla bilgi için Sürüm oluşturma arabirimlerine bakın.