AIDL arka ucu, taslak kod oluşturma için bir hedeftir. AIDL dosyalarını her zaman belirli bir dil ve belirli bir çalışma zamanında kullanırsınız. Bağlama bağlı olarak farklı AIDL arka uçları kullanmanız gerekir.
Aşağıdaki tabloda API yüzeyinin kararlılığı, system.img
libbinder.so
ikili programından bağımsız olarak iletilebilecek şekilde bu API yüzeyinde kod derleme yeteneğini ifade eder.
AIDL'nin aşağıdaki arka uçları vardır:
Arka uç | Dil | API yüzeyi | Derleme sistemleri |
---|---|---|---|
Java | Java | SDK/SystemApi (stabil*) | tümü |
NDK | C++ | libbinder_ndk (kararlı*) | aidl_arayüz |
ABM | C++ | libbinder (kararsız) | tümü |
Rust | Rust | libbinder_rs (stable*) | aidl_arayüz |
- Bu API yüzeyleri kararlıdır ancak hizmet yönetimi için olanlar gibi API'lerin çoğu şirket içi platform kullanımı için ayrılmıştır ve uygulamalar tarafından kullanılamaz. AIDL'nin uygulamalarda nasıl kullanılacağı hakkında daha fazla bilgi için geliştirici belgelerine bakın.
- Rust arka uç, Android 12'de kullanıma sunulmuştur. NDK arka ucu ise Android 10'dan beri kullanılabilir.
- Paslı kasa, sabit ve taşınabilir olmasını sağlayan
libbinder_ndk
üzerine inşa edilmiştir. APEX'ler, sistem tarafındaki diğer kullanıcılar gibi bağlayıcı paketini kullanır. Rust kısmı bir APEX'e paketlenir ve içinde gönderilir. Bu, sistem bölümündekilibbinder_ndk.so
değerine bağlıdır.
Derleme sistemleri
Arka uça bağlı olarak, AIDL'yi stub koduna derlemenin iki yolu vardır. Derleme sistemleri hakkında daha fazla bilgi için Soong Modülü Referansı başlıklı makaleyi inceleyin.
Temel derleme sistemi
Herhangi bir cc_
veya java_
Android.bp modülünde (veya Android.mk
eşdeğerlerinde) .aidl
dosyaları kaynak dosya olarak belirtilebilir. Bu durumda, AIDL'nin Java/CPP arka uçları (NDK arka ucu değil) kullanılır ve ilgili AIDL dosyalarını kullanacak sınıflar modüle otomatik olarak eklenir. Derleme sistemine ilgili modüldeki AIDL dosyalarının kök yolunu bildiren local_include_dirs
gibi seçenekler, bu modüllerde bir aidl:
grubu altında belirtilebilir. Rust arka ucunun yalnızca Rust ile kullanılabileceğini unutmayın. rust_
modülleri, AIDL dosyalarının kaynak dosya olarak belirtilmemesi nedeniyle farklı şekilde ele alınır.
Bunun yerine, aidl_interface
modülü bağlanabileceğiniz <aidl_interface name>-rust
adlı bir rustlib
üretir. Daha fazla bilgi için Rust AIDL örneğine bakın.
aidl_interface
Bu derleme sistemiyle kullanılan türler yapılandırılmış olmalıdır. Yapılandırılmış olmaları için parseller doğrudan alanları içermeli ve doğrudan hedef dillerde tanımlanan türlerin bildirimleri olmamalıdır. Yapılandırılmış AIDL'nin kararlı AIDL ile nasıl uyumlu olduğu hakkında bilgi edinmek için Yapılandırılmış ve kararlı AIDL başlıklı makaleyi inceleyin.
Türler
aidl
derleyicisini türler için referans bir uygulama olarak düşünebilirsiniz.
Bir arayüz oluşturduğunuzda, ortaya çıkan arayüz dosyasını görmek için aidl --lang=<backend> ...
işlevini çağırın. aidl_interface
modülünü kullandığınızda çıkışı out/soong/.intermediates/<path to module>/
'te görüntüleyebilirsiniz.
Java/AIDL Türü | C++ Türü | NDK Türü | Pas Türü |
---|---|---|---|
boole | Bool | bool | bool |
bayt8 | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | tam32_t | i32 |
uzun | int64_t | int64_t | i64 |
kayan | float | kayan | f32 |
çift | çift | çift | f64 |
Dize | android::String16 | std::string | Dize |
android.os.Parcelable | android::Parcelable | Yok | Yok |
Bağlayıcı | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | Giriş: &[T] Çıkış: Vec<T> |
byte[] | std::vektör<uint8_t> | std::vector<int8_t>1 | In: &[u8] Dışarıda: Vec<u8> |
Liste<T> | std::vector<T>2 | std::vector<T>3 | Giriş: &[T]4 Çıkış: Vec<T> |
FileDescriptor | android::base::benzersiz_fd | Yok | binder::parcel::ParcelFileDescriptor |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
arayüz türü (T) | android::sp<T> | std::shared_ptr<T>7 | binder::Güçlü |
ayrıştırılabilir öğe türü (T) | T | T | T |
birleştirme türü (T)5 | T | T | T |
T[N] 6 | std::array<T, N> | std::dizi<T, N> | [T; N] |
1. Android 12 veya sonraki sürümlerde, uyumluluk nedeniyle bayt dizileri int8_t yerine uint8_t kullanır.
2. C++ arka ucu, T
'nin String
, IBinder
, ParcelFileDescriptor
veya parcelable değerlerinden biri olduğu List<T>
değerini destekler. T
, Android 13 veya sonraki sürümlerde diziler hariç herhangi bir primitif olmayan tür (arayüz türleri dahil) olabilir. AOSP, tüm arka uçlarda çalıştıkları için T[]
gibi dizi türlerini kullanmanızı önerir.
3. NDK arka ucu, T
değerinin String
, ParcelFileDescriptor
veya ayrıştırılabilir öğe olduğu durumlarda List<T>
özelliğini destekler. Android 13 veya sonraki sürümlerde T
, diziler dışındaki herhangi bir ilkel olmayan tür olabilir.
4. Türler, giriş (bağımsız değişken) veya çıkış (döndürülen değer) olmalarına bağlı olarak Rust kodu için farklı şekilde iletilir.
5. Birleştirme türleri, Android 12 ve sonraki sürümlerde desteklenir.
6. Android 13 veya sonraki sürümlerde sabit boyutlu diziler desteklenir. Sabit boyutlu diziler birden fazla boyuta sahip olabilir (ör. int[3][4]
).
Java arka ucunda sabit boyutlu diziler dizi türleri olarak temsil edilir.
7. Bağlayıcı SharedRefBase
nesnesini örneklendirmek için SharedRefBase::make\<My\>(... args ...)
işlevini kullanın. Bu işlev, bir std::shared_ptr\<T\>
nesnesi oluşturur. Bu nesne, bağlayıcının başka bir sürece ait olması durumunda da dahili olarak yönetilir. Nesnenin başka yöntemlerle oluşturulması çift sahipliğe neden olur.
8. Java/AIDL byte[]
türüne de bakın.
Yön (giriş/çıkış/çıkış)
İşlevlere ait bağımsız değişken türlerini belirtirken bunları in
, out
veya inout
olarak belirtebilirsiniz. Bu, bir IPC çağrısı için bilgilerin hangi yönde iletileceğini kontrol eder. in
, varsayılan yöndür ve verilerin arayandan aranana iletildiğini gösterir. out
, verilerin aranan kullanıcıdan arayan kullanıcıya aktarıldığı anlamına gelir. inout
, bu ikisinin birleşimidir. Ancak Android Ekibi, bağımsız değişken belirteci inout
kullanmamanızı önerir.
inout
'ü sürümlü bir arayüzle ve eski bir arananla kullanıyorsanız yalnızca arayanda bulunan ek alanlar varsayılan değerlerine sıfırlanır. Rust'ta normal bir inout
türü &mut Vec<T>
alır ve liste inout
türü &mut Vec<T>
alır.
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF8/UTF16
CPP arka ucu ile dizelerin utf-8 mi yoksa utf-16 mı olacağını seçebilirsiniz. Dizeleri otomatik olarak utf-8'e dönüştürmek için AIDL'de @utf8InCpp String
olarak tanımlayın.
NDK ve Rust arka uçları her zaman utf-8 dizelerini kullanır. utf8InCpp
ek açıklaması hakkında daha fazla bilgi için AIDL'de ek açıklamaları inceleyin.
Boş değer atanabilirliği
@nullable
ile boş olabilen türlere not ekleyebilirsiniz.
nullable
ek açıklaması hakkında daha fazla bilgi için AIDL'deki ek açıklamalar konusuna bakın.
Özel kargolanabilir ürünler
Özel paketlenebilir, hedef arka uçta manuel olarak uygulanan bir paketlenebilir öğedir. Özel parcelable'ları yalnızca değiştirilemeyen mevcut bir özel parcelable için diğer dillere destek eklemeye çalışırken kullanın.
AIDL'nin bildiği özel bir parcelable tanımlamak için AIDL parcelable beyanı aşağıdaki gibidir:
package my.pack.age;
parcelable Foo;
Bu, varsayılan olarak my.pack.age.Foo
'ın Parcelable
arayüzünü uygulayan bir Java sınıfı olduğu bir Java parcelable tanımlar.
AIDL'de paketlenebilir özel bir PBM arka ucu beyanı için cpp_header
kullanın:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
'teki C++ uygulaması şu şekilde görünür:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
AIDL'de özel NDK parsel beyanı için ndk_header
kullanın:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
'teki NDK uygulaması şu şekilde görünür:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
Android 15'te, AIDL'de özel bir Rust dilimlenebilir öğesini beyan etmek için rust_type
kullanın:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
'teki Rust uygulaması şu şekilde görünür:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
Ardından bu parcelable'ı AIDL dosyalarında tür olarak kullanabilirsiniz ancak AIDL tarafından oluşturulmaz. union
'de kullanmak üzere CPP/NDK arka uç özel paketlenebilirleri için <
ve ==
operatörleri sağlayın.
Varsayılan değerler
Yapılandırılmış paketlenebilirler, ilkel türler, String
'ler ve bu türlerin dizileri için alan başına varsayılan değerler tanımlayabilir.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Java arka ucunda varsayılan değerler eksik olduğunda alanlar, ilkel türler için sıfır değer ve ilkel olmayan türler için null
olarak başlatılır.
Diğer arka uçlarda, varsayılan değerler tanımlanmadığında alanlar varsayılan başlatılmış değerlerle başlatılır. Örneğin, C++ arka ucunda String
alanları boş bir dize olarak, List<T>
alanları ise boş bir vector<T>
olarak başlatılır. @nullable
alanları, boş değer alanları olarak başlatılır.
Birlikler
AIDL birlikleri etiketlenir ve özellikleri tüm arka uçlarda benzerdir. Varsayılan olarak ilk alanın varsayılan değeriyle oluşturulurlar ve bunlarla etkileşime geçmenin dile özgü bir yolu vardır.
union Foo {
int intField;
long longField;
String stringField;
MyParcelable parcelableField;
...
}
Java Örneği
Foo u = Foo.intField(42); // construct
if (u.getTag() == Foo.intField) { // tag query
// use u.getIntField() // getter
}
u.setSringField("abc"); // setter
C++ ve NDK Örneği
Foo u; // default constructor
assert (u.getTag() == Foo::intField); // tag query
assert (u.get<Foo::intField>() == 0); // getter
u.set<Foo::stringField>("abc"); // setter
assert (u == Foo::make<Foo::stringField>("abc")); // make<tag>(value)
Pas örneği
Rust'ta birimler enum olarak uygulanır ve açıklayıcı alıcı ve ayarlayıcılara sahip değildir.
let mut u = Foo::Default(); // default constructor
match u { // tag match + get
Foo::IntField(x) => assert!(x == 0);
Foo::LongField(x) => panic!("Default constructed to first field");
Foo::StringField(x) => panic!("Default constructed to first field");
Foo::ParcelableField(x) => panic!("Default constructed to first field");
...
}
u = Foo::StringField("abc".to_string()); // set
Hata işleme
Android OS, hizmetlerin hataları bildirirken kullanılacak yerleşik hata türleri sağlar. Bunlar binder tarafından kullanılır ve binder arayüzü uygulayan tüm hizmetler tarafından kullanılabilir. Kullanımları AIDL tanımında ayrıntılı olarak açıklanmıştır ve kullanıcı tanımlı bir durum veya dönüş türü gerektirmezler.
Hatalı çıkış parametreleri
Bir AIDL işlevi hata bildirdiğinde işlev, çıkış parametrelerini başlatamaz veya değiştiremez. Özellikle çıkış parametreleri, hatanın işlemin işlenmesi sırasında meydana gelmesi yerine ayrıştırma sırasında meydana gelmesi durumunda değiştirilebilir. Genel olarak, bir AIDL işlevinden hata alındığında tüm inout
ve out
parametrelerinin yanı sıra döndürülen değerin (bazı arka uçlarda out
parametresi gibi davranır) belirsiz durumda olduğu kabul edilmelidir.
Kullanılacak hata değerleri
Yerleşik hata değerlerinin çoğu herhangi bir AIDL arayüzünde kullanılabilir ancak bazıları özel bir şekilde ele alınır. Örneğin, hata durumunu tanımlarken EX_UNSUPPORTED_OPERATION
ve EX_ILLEGAL_ARGUMENT
kullanılmasında sakınca yoktur ancak temel altyapı tarafından özel olarak ele alındığı için EX_TRANSACTION_FAILED
kullanılmamalıdır. Bu yerleşik değerler hakkında daha fazla bilgi için arka uca özgü tanımları kontrol edin.
AIDL arayüzü, yerleşik hata türleri tarafından kapsanmayan ek hata değerleri gerektiriyorsa kullanıcı tarafından tanımlanan hizmete özel bir hata değerinin eklenmesine olanak tanıyan hizmete özel özel yerleşik hatayı kullanabilir. Hizmete özgü bu hatalar genellikle AIDL arayüzünde const int
veya int
destekli enum
olarak tanımlanır ve bağlayıcı tarafından ayrıştırılmaz.
Java'da hatalar, android.os.RemoteException
gibi istisnalarla eşlenir. Java, hizmete özgü istisnalar için kullanıcı tanımlı hatayla birlikte android.os.ServiceSpecificException
değerini kullanır.
Android'deki yerel kodda istisnalar kullanılmaz. PBM arka ucu android::binder::Status
kullanır. NDK arka ucu ndk::ScopedAStatus
kullanıyor. AIDL tarafından oluşturulan her yöntem, yöntemin durumunu temsil eden bunlardan birini döndürür. Rust arka ucu, NDK ile aynı istisna kodu değerlerini kullanır ancak bunları kullanıcıya sunmadan önce yerel Rust hatalarına (StatusCode
, ExceptionCode
) dönüştürür. Hizmete özgü hatalar için döndürülen Status
veya ScopedAStatus
, kullanıcı tanımlı hatayla birlikte EX_SERVICE_SPECIFIC
kullanır.
Yerleşik hata türleri aşağıdaki dosyalarda bulunabilir:
Arka uç | Tanım |
---|---|
Java | android/os/Parcel.java |
ABM | binder/Status.h |
NDK | android/binder_status.h |
Rust | android/binder_status.h |
Çeşitli arka uçları kullanma
Bu talimatlar Android platform koduna özeldir. Bu örneklerde tanımlanmış bir tür (my.package.IFoo
) kullanılmaktadır. Rust arka ucunun nasıl kullanılacağıyla ilgili talimatlar için Android Rust Desenleri sayfasındaki Rust AIDL örneğine bakın.
İçe aktarma türleri
Tanımlanmış tür bir arayüz, paketlenebilir veya birleştirme olsun, Java'ya aktarabilirsiniz:
import my.package.IFoo;
Veya PBM arka ucunda:
#include <my/package/IFoo.h>
Alternatif olarak, NDK arka ucunda (ek aidl
ad alanına dikkat edin):
#include <aidl/my/package/IFoo.h>
Rust arka ucunda da:
use my_package::aidl::my::package::IFoo;
Java'da iç içe yerleştirilmiş bir türü içe aktarabilirsiniz, ancak CPP/NDK arka uçlarına kök türünün başlığını eklemeniz gerekir. Örneğin, my/package/IFoo.aidl
içinde tanımlanan iç içe yerleştirilmiş bir Bar
türünü içe aktarırken (IFoo
, dosyanın kök türüdür) CPP arka ucu için <my/package/IFoo.h>
(veya NDK arka ucu için <aidl/my/package/IFoo.h>
) eklemeniz gerekir.
Hizmetleri uygulama
Bir hizmeti uygulamak için yerel stub sınıfından devralmanız gerekir. Bu sınıf, bağlayıcı sürücüsünden gelen komutları okur ve uyguladığınız yöntemleri yürütür. Aşağıdaki gibi bir AIDL dosyanız olduğunu varsayalım:
package my.package;
interface IFoo {
int doFoo();
}
Java'da şu sınıftan genişletmeniz gerekir:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
PBM arka ucunda:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
NDK arka ucunda (ek aidl
ad alanına dikkat edin):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
Rust arka ucunda:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Eşzamansız Rust ile de:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Kaydolma ve hizmet alma
Android platformundaki hizmetler genellikle servicemanager
işlemiyle kaydedilir. Aşağıdaki API'lere ek olarak bazı API'ler hizmeti kontrol eder (yani hizmet kullanılamıyorsa hemen yanıt verir).
Tam ayrıntılar için ilgili servicemanager
arayüzünü kontrol edin. Bu işlemler yalnızca Android platformuna göre derleme yaparken yapılabilir.
Java'da:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
PBM arka ucunda:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
NDK arka ucunda (ek aidl
ad alanına dikkat edin):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
Rust arka ucunda:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
Tek iş parçacıklı bir çalışma zamanında, eşzamansız Rust arka ucunda:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks will run on this thread.
std::future::pending().await
}
Diğer seçenekler arasındaki önemli farklardan biri, eşzamansız Rust ve tek iş parçacıklı bir çalışma zamanı kullanırken join_thread_pool
araması yapmamamızdır. Bunun nedeni, Tokio'ya oluşturulan görevleri yürütebileceği bir iş parçacığı vermeniz gerektiğidir. Bu örnekte ana ileti dizisi bu amaca hizmet eder. tokio::spawn
kullanılarak oluşturulan tüm görevler ana iş parçacığında yürütülür.
Çok iş parçacıklı bir çalışma zamanında, eşzamansız Rust arka ucunda:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
Çok iş parçacıklı Tokio çalışma zamanında, oluşturulan görevler ana iş parçacığında yürütülmez. Bu nedenle, ana iş parçacığının boşta kalmaması için ana iş parçacığında join_thread_pool
çağrısı yapmak daha mantıklıdır. Arayüzü, arayüz bağlamından çıkmak için block_in_place
içine sarmanız gerekir.
Ölümle ilgili bağlantı
Bir bağlayıcıyı barındıran hizmetin kullanımdan kaldırılması durumunda bildirim alma isteğinde bulunabilirsiniz. Bu, geri çağırma proxy'lerinin sızdırılmasını önlemeye veya hata gidermeye yardımcı olabilir. Bu çağrıları bağlayıcı proxy nesnelerinde yapın.
- Java'da
android.os.IBinder::linkToDeath
kullanın. - PBM arka ucunda
android::IBinder::linkToDeath
kullanın. - NDK arka ucunda
AIBinder_linkToDeath
kullanın. - Rust arka ucunda bir
DeathRecipient
nesnesi oluşturun ve ardındanmy_binder.link_to_death(&mut my_death_recipient)
işlevini çağırın. Geri çağırma işlevinin sahibiDeathRecipient
olduğundan, bildirim almak istediğiniz sürece bu nesneyi canlı tutmanız gerektiğini unutmayın.
Arayan bilgileri
Bir çekirdek bağlayıcı çağrısı alındığında arayan bilgileri çeşitli API'lerde kullanılabilir. PID (veya İşlem Kimliği), işlemi gönderen işlemin Linux işlem kimliğini ifade eder. UID (veya User-ID), Linux kullanıcı kimliğini ifade eder. Tek yönlü arama alındığında arayan PID'si 0 olur. Bu işlevler, bir bağlayıcı işlem bağlamının dışındayken geçerli işlemin PID ve UID değerini döndürür.
Java arka ucunda:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
PBM arka ucunda:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
NDK arka ucunda:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Rust arka ucunda, arayüzü uygularken varsayılan ayarlara izin vermek yerine aşağıdakileri belirtin:
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Hizmetler için hata raporları ve hata ayıklama API'si
Hata raporları çalıştırıldığında (örneğin, adb bugreport
ile), çeşitli sorunlarda hata ayıklama işlemine yardımcı olmak için sistem genelinden bilgi toplanır.
AIDL hizmetleri için hata raporları, hizmet yöneticisine kayıtlı tüm hizmetlerde dumpsys
ikili programını kullanarak bilgilerin hata raporuna dökümünü sağlar. dumpsys SERVICE [ARGS]
içeren bir hizmetten bilgi almak için komut satırında dumpsys
'ü de kullanabilirsiniz. C++ ve Java arka uçlarında, addService
için ek bağımsız değişkenler kullanarak hizmetlerin döküldüğü sırayı kontrol edebilirsiniz. Hata ayıklama sırasında bir hizmetin PID'sini almak için dumpsys --pid SERVICE
öğesini de kullanabilirsiniz.
Hizmetinize özel çıkış eklemek için sunucu nesnenizdeki dump
yöntemini, bir AIDL dosyasında tanımlanan diğer IPC yöntemlerini uygular gibi geçersiz kılabilirsiniz. Bunu yaparken, döküm işlemini uygulama izni android.permission.DUMP
ile veya belirli UID'lerle kısıtlamanız gerekir.
Java arka ucunda:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
PBM arka ucunda:
status_t dump(int, const android::android::Vector<android::String16>&) override;
NDK arka ucunda:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
Rust arka ucunda, arayüzü uygularken varsayılan ayarlara izin vermek yerine aşağıdakileri belirtin:
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Zayıf işaretçiler kullanma
Bir bağlayıcı nesnesine zayıf referans tutabilirsiniz.
Java, WeakReference
öğesini destekler, ancak yerel katmanda zayıf bağlayıcı referanslarını desteklemez.
PBM arka ucunda zayıf tür wp<IFoo>
'tür.
NDK arka ucunda ScopedAIBinder_Weak
kullanın:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
Rust arka ucunda WpIBinder
veya Weak<IFoo>
kullanırsınız:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
Arayüz tanımlayıcısı dinamik olarak alma
Arayüz tanımlayıcısı, bir arayüzün türünü tanımlar. Bu, hata ayıklama sırasında veya bilinmeyen bir bağlayıcınız olduğunda faydalıdır.
Java'da arayüz tanımlayıcısını aşağıdaki gibi bir kodla alabilirsiniz:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
PBM arka ucunda:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
NDK ve Rust arka uçları bu özelliği desteklemez.
Arayüz tanımlayıcısını statik olarak alma
Bazen (ör. @VintfStability
hizmetlerini kaydederken) arayüz tanımlayıcısının statik olarak ne olduğunu bilmeniz gerekir. Java'da, aşağıdaki gibi bir kod ekleyerek
açıklayıcıyı alabilirsiniz:
import my.package.IFoo;
... IFoo.DESCRIPTOR
PBM arka ucunda:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
NDK arka ucunda (ek aidl
ad alanına dikkat edin):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
Rust arka ucunda:
aidl::my::package::BnFoo::get_descriptor()
Enum aralığı
Yerleşik arka uçlarda, bir enum'un alabileceği olası değerleri iteratif olarak inceleyebilirsiniz. Kod boyutu nedeniyle bu özellik Java'da desteklenmez.
AIDL'de tanımlanan bir enum MyEnum
için iterasyon aşağıdaki gibi sağlanır.
PBM arka ucunda:
::android::enum_range<MyEnum>()
NDK arka ucunda:
::ndk::enum_range<MyEnum>()
Rust arka ucunda:
MyEnum::enum_values()
İleti dizisi yönetimi
Bir işlemdeki her libbinder
örneği bir iş parçacığı havuzu bulundurur. Çoğu kullanım durumu için bu, tüm arka uçlar arasında paylaşılan tam olarak bir iş parçacığı havuzu olmalıdır.
Tek istisna, tedarikçi kodu /dev/vndbinder
ile konuşmak için libbinder
'ın başka bir kopyasını yükleyebileceği durumdur. Bu ayrı bir bağlayıcı düğümünde olduğundan iş parçacığı havuzu paylaşılmaz.
Java arka ucu için iş parçacığı havuzunun boyutu (zaten başlatıldığı için) yalnızca artabilir:
BinderInternal.setMaxThreads(<new larger value>);
CPP arka ucu için aşağıdaki işlemler kullanılabilir:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
Benzer şekilde, NDK arka ucunda:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
Rust arka ucunda:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
Asenkron Rust arka ucunda iki iş parçacığı havuzuna ihtiyacınız vardır: binder ve Tokio.
Bu nedenle, eşzamansız Rust kullanan uygulamaların özellikle join_thread_pool
kullanımı söz konusu olduğunda özel noktalara dikkat etmesi gerekir. Bu konuda daha fazla bilgi edinmek için hizmet kaydetme bölümüne göz atın.
Ayrılmış adlar
C++, Java ve Rust, bazı adları anahtar kelime olarak veya dile özgü kullanım için ayırır. AIDL, dil kurallarına dayalı kısıtlamalar uygulamasa da ayrılmış bir adla eşleşen alan veya tür adları kullanmak C++ veya Java için derleme hatasına neden olabilir. Rust için alan veya tür, r#
ön eki kullanılarak erişilebilen "ham tanımlayıcı" söz dizimi kullanılarak yeniden adlandırılır.
Ergonomik olmayan bağlamaları veya açık derleme hatasını önlemek için mümkün olduğunda AIDL tanımlarınızda ayrılmış adlar kullanmaktan kaçınmanızı öneririz.
AIDL tanımlarınızda zaten ayrılmış adlarınız varsa protokol uyumlu kalırken alanları güvenle yeniden adlandırabilirsiniz. Geliştirmeye devam etmek için kodunuzu güncellemeniz gerekebilir ancak önceden oluşturulmuş programlar birlikte çalışmaya devam edecektir.
Kullanılmaması gereken adlar: