AIDL arka ucu, sahte kod oluşturma hedefidir. Belirli bir dildeki AIDL dosyalarını her zaman belirli bir çalışma zamanıyla kullanın. Bağlama bağlı olarak farklı AIDL arka uçları kullanmanız gerekir.
Aşağıdaki tabloda, API yüzeyinin kararlılığı, kodun system.img
libbinder.so
ikilisinden bağımsız olarak yayınlanabileceği şekilde bu API yüzeyine karşı derlenebilme özelliğini ifade eder.
AIDL'nin aşağıdaki arka uçları vardır:
Arka uç | Dil | API yüzeyi | Derleme sistemleri |
---|---|---|---|
Java | Java | SDK veya SystemApi (kararlı*) |
Tümü |
NDK | C++ | libbinder_ndk (kararlı*) |
aidl_interface |
ABM | C++ | libbinder (kararsız) |
Tümü |
Rust | Rust | libbinder_rs (kararlı*) |
aidl_interface |
- Bu API yüzeyleri kararlıdır ancak hizmet yönetimi gibi birçok API, platformun dahili kullanımı için ayrılmıştır ve uygulamalarda kullanılamaz. Uygulamalarda AIDL'nin nasıl kullanılacağı hakkında daha fazla bilgi için Android Arayüz Tanımlama Dili (AIDL) başlıklı makaleyi inceleyin.
- Rust arka ucu Android 12'de kullanıma sunuldu. NDK arka ucu ise Android 10'dan beri kullanılabilir.
- Rust paketi,
libbinder_ndk
üzerine kurulmuştur. Bu sayede paket, kararlı ve taşınabilir olur. APEX'ler, sistem tarafında bağlayıcı kasayı standart şekilde kullanır. Rust bölümü bir APEX'e paketlenir ve bu APEX içinde gönderilir. Bu bölüm, sistem bölümündekilibbinder_ndk.so
bağlıdır.
Derleme sistemleri
Arka uca bağlı olarak, AIDL'yi saplama koduna derlemenin iki yolu vardır. Derleme sistemleri hakkında daha fazla bilgi için Soong Modules Reference (Soong Modülleri Referansı) başlıklı makaleyi inceleyin.
Temel derleme sistemi
Herhangi bir cc_
veya java_
Android.bp module
(ya da bunların Android.mk
eşdeğerlerinde) kaynak dosyalar olarak AIDL (.aidl
) dosyalarını belirtebilirsiniz. Bu durumda, AIDL'nin Java veya CPP arka uçları kullanılır (NDK arka ucu değil) ve ilgili AIDL dosyalarını kullanacak sınıflar modüle otomatik olarak eklenir. Bu modüllerde aidl:
grubu altında local_include_dirs
gibi seçenekler belirtebilirsiniz (bu seçenek, derleme sistemine söz konusu modüldeki AIDL dosyalarının kök yolunu bildirir).
Rust arka ucu yalnızca Rust ile kullanılabilir. rust_
modülleri farklı şekilde işlenir. Bu modüllerde AIDL dosyaları kaynak dosyalar olarak belirtilmez. Bunun yerine, aidl_interface
modülü, aidl_interface_name-rust
adlı bir rustlib
oluşturur. Bu rustlib
, bağlantı oluşturmak için kullanılabilir. Ayrıntılar için Rust AIDL
örneği başlıklı makaleyi inceleyin.
aidl_interface
aidl_interface
derleme sistemiyle kullanılan türler yapılandırılmış olmalıdır. Yapılandırılabilmesi için Parcelable'lar doğrudan alan içermeli ve hedef dillerde doğrudan tanımlanan türlerin bildirimleri olmamalıdır. Yapılandırılmış AIDL'nin kararlı AIDL ile nasıl uyum sağladığı hakkında bilgi edinmek için Yapılandırılmış ve kararlı AIDL başlıklı makaleyi inceleyin.
Türler
Türler için referans uygulama olarak aidl
derleyicisini kullanabilirsiniz.
Bir arayüz oluşturduğunuzda, sonuçtaki arayüz dosyasını görmek için aidl --lang=<backend> ...
öğesini çağırın. aidl_interface
modülünü kullandığınızda çıkışı out/soong/.intermediates/<path to module>/
içinde görüntüleyebilirsiniz.
Java veya AIDL türü | C++ türü | NDK türü | Pas türü |
---|---|---|---|
boolean |
bool |
bool |
bool |
byte 8 |
int8_t |
int8_t |
i8 |
char |
char16_t |
char16_t |
u16 |
int |
int32_t |
int32_t |
i32 |
long |
int64_t |
int64_t |
i64 |
float |
float |
float |
f32 |
double |
double |
double |
f64 |
String |
android::String16 |
std::string |
Giriş: &str Çıkış: String |
android.os.Parcelable |
android::Parcelable |
Yok | Yok |
IBinder |
android::IBinder |
ndk::SpAIBinder |
binder::SpIBinder |
T[] |
std::vector<T> |
std::vector<T> |
Giriş: &[T] Çıkış: Vec<T> |
byte[] |
std::vector |
std::vector 1 |
Giriş: &[u8] Çıkış: Vec<u8> |
List<T> |
std::vector<T> 2 |
std::vector<T> 3 |
Giriş: In: &[T] 4Çıkış: Vec<T> |
FileDescriptor |
android::base::unique_fd |
Yok | Yok |
ParcelFileDescriptor |
android::os::ParcelFileDescriptor |
ndk::ScopedFileDescriptor |
binder::parcel::ParcelFileDescriptor |
Arayüz türü (T ) |
android::sp<T> |
std::shared_ptr<T> 7 |
binder::Strong |
Parcelable türü (T ) |
T |
T |
T |
Birleştirme türü (T )5 |
T |
T |
T |
T[N] 6 |
std::array<T, N> |
std::array<T, N> |
[T; N] |
1. Android 12 veya sonraki sürümlerde, uyumluluk nedeniyle bayt dizilerinde int8_t
yerine uint8_t
kullanılır.
2. C++ arka ucu, List<T>
değerini destekler. Burada T
, String
, IBinder
, ParcelFileDescriptor
veya parcelable değerlerinden biridir. Android 13 veya sonraki sürümlerde T
, diziler hariç olmak üzere arayüz türleri de dahil olmak üzere ilkel olmayan herhangi bir tür olabilir. AOSP, tüm arka uçlarda çalıştıkları için T[]
gibi dizi türlerinin kullanılmasını önerir.
3. NDK arka ucu, List<T>
ifadesini destekler. Burada T
, String
, ParcelFileDescriptor
veya parcelable'dır. Android 13 veya sonraki sürümlerde T
, diziler hariç ilkel olmayan herhangi bir tür olabilir.
4. Türler, giriş (bir bağımsız değişken) veya çıkış (döndürülen bir değer) olup olmamasına bağlı olarak Rust kodu için farklı şekilde iletilir.
5. Birleşim 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 (örneğin, int[3][4]
). Java arka ucunda, sabit boyutlu diziler dizi türleri olarak gösterilir.
7. Bir bağlayıcı SharedRefBase
nesnesi oluşturmak için SharedRefBase::make\<My\>(... args ...)
kullanın. Bu işlev, bağlayıcı başka bir işleme aitse dahili olarak da yönetilen bir std::shared_ptr\<T\>
nesnesi oluşturur. Nesnenin başka şekillerde oluşturulması, sahipliğin iki kez alınmasına neden olur.
8. Java veya AIDL türü byte[]
konusuna da bakın.
Yön (in, out ve inout)
İşlevlere iletilecek bağımsız değişkenlerin türlerini belirtirken bunları in
, out
veya inout
olarak belirtebilirsiniz. Bu, IPC çağrısı için bilgilerin iletildiği yönü kontrol eder.
in
bağımsız değişken belirleyici, verilerin arayan kişiden çağrılan kişiye iletildiğini gösterir.in
belirteci varsayılan yöndür ancak veri türleriout
de olabilirse yönü belirtmeniz gerekir.out
bağımsız değişken belirleyici, verilerin çağrılan işlevden çağıran işleve aktarıldığı anlamına gelir.inout
bağımsız değişken belirleyici, bu ikisinin birleşimidir. Ancak,inout
bağımsız değişken belirleyicisini kullanmaktan kaçınmanızı öneririz.inout
sürümünü, sürümü belirlenmiş bir arayüz ve daha eski bir callee ile kullanırsanız yalnızca arayan tarafta bulunan ek alanlar varsayılan değerlerine sıfırlanır. Rust'ta normal birinout
türü&mut T
, listeinout
türü ise&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);
}
UTF-8 ve UTF-16
CPP arka ucuyla, dizelerin UTF-8 veya UTF-16 olup olmayacağını seçebilirsiniz.
Dizeleri, UTF-8'e otomatik olarak dönüştürmek için AIDL'de @utf8InCpp String
olarak bildirin. NDK ve Rust arka uçları her zaman UTF-8 dizeleri kullanır. utf8InCpp
ek açıklaması hakkında daha fazla bilgi için utf8InCpp konusuna bakın.
Boş değer atanabilirliği
Boş değer olabilen türleri @nullable
ile açıklama olarak ekleyebilirsiniz.
nullable
ek açıklaması hakkında daha fazla bilgi için nullable konusuna bakın.
Özel Parcelable'lar
Özel parcelable, hedef arka uçta manuel olarak uygulanan bir parcelable'dır. Özel parcelable'ları yalnızca değiştirilemeyen mevcut bir özel parcelable'a diğer dillerde destek eklemeye çalıştığınızda kullanın.
Aşağıda bir AIDL parcelable bildirimi örneği verilmiştir:
package my.pack.age;
parcelable Foo;
Bu, varsayılan olarak my.pack.age.Foo
öğesinin Parcelable
arayüzünü uygulayan bir Java sınıfı olduğu bir Java parcelable'ı bildirir.
AIDL'de özel bir CPP arka uç paketlenebilir öğesinin bildirimi için cpp_header
kullanın:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
içindeki 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 bir NDK parcelable'ı bildirmek için ndk_header
kullanın:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
içindeki 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 parcelable'ı 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
içindeki 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);
Daha sonra bu paketlenebilir öğeyi AIDL dosyalarında tür olarak kullanabilirsiniz ancak AIDL tarafından oluşturulmaz. <
ve ==
operatörlerini, union
'de kullanmak üzere CPP ve NDK arka uç özel paketlenebilirleri için sağlayın.
Varsayılan değerler
Yapılandırılmış parcelable'lar, ilkel türler, String
alanları ve bu türlerin dizileri için alan başına varsayılan değerler bildirebilir.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Java arka ucunda, varsayılan değerler eksik olduğunda alanlar, temel türler için sıfır değerleri, temel olmayan türler için ise 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ş dize olarak, List<T>
alanları ise boş vector<T>
olarak başlatılır. @nullable
alanları, boş değerli alanlar olarak başlatılır.
Sendikalar
AIDL birleşimleri etiketlenir ve özellikleri tüm arka uçlarda benzerdir. İlk alanın varsayılan değerine göre oluşturulur ve bunlarla etkileşim kurmanın 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.setStringField("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 birleşimler enum olarak uygulanır ve açıkça tanımlanmış getter'lar ve setter'lar yoktur.
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 işletim sistemi, hizmetlerin hataları bildirirken kullanması için yerleşik hata türleri sağlar. Bunlar bağlayıcılar tarafından kullanılır ve bağlayıcı arayüzü uygulayan tüm hizmetler tarafından kullanılabilir. Kullanımları AIDL tanımında iyi bir şekilde belgelenmiştir ve kullanıcı tanımlı durum veya dönüş türü gerektirmezler.
Hatalı çıkış parametreleri
Bir AIDL işlevi hata bildirdiğinde işlev başlatılamayabilir veya çıkış parametreleri değiştirilemeyebilir. Özellikle, hata işlemin kendisi işlenirken değil, paket açma sırasında oluşursa çıkış parametreleri değiştirilebilir. Genel olarak, bir AIDL işlevinden hata alındığında tüm inout
ve out
parametrelerinin yanı sıra dönüş değeri (bazı arka uçlarda out
parametresi gibi davranır) belirsiz bir durumda olduğu kabul edilmelidir.
Hangi hata değerleri kullanılacak?
Yerleşik hata değerlerinin çoğu tüm AIDL arayüzlerinde kullanılabilir ancak bazıları özel olarak ele alınır. Örneğin, EX_UNSUPPORTED_OPERATION
ve EX_ILLEGAL_ARGUMENT
hata durumunu açıklarken kullanılabilir ancak EX_TRANSACTION_FAILED
, temel altyapı tarafından özel olarak işlendiği için kullanılmamalıdır. Bu yerleşik değerler hakkında daha fazla bilgi için arka uca özel tanımları inceleyin.
AIDL arayüzü, yerleşik hata türlerinin kapsamadığı ek hata değerleri gerektiriyorsa kullanıcı tarafından tanımlanan hizmete özgü bir hata değerinin dahil edilmesine izin veren özel hizmete özgü 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. Hizmete özel istisnalar için Java, kullanıcı tanımlı hatayla birlikte android.os.ServiceSpecificException
kullanır.
Android'deki yerel kodda istisnalar kullanılmaz. CPP arka ucu android::binder::Status
kullanır. NDK arka ucu ndk::ScopedAStatus
kullanır. AIDL tarafından oluşturulan her yöntem, yöntemin durumunu temsil eden bu değerlerden birini döndürür. Rust arka ucu, NDK ile aynı istisna kodu değerlerini kullanır ancak bunları kullanıcıya iletmeden ö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 olan my.package.IFoo
kullanılmaktadır. Rust arka ucunu kullanmayla ilgili talimatlar için Android Rust kalıpları bölümündeki Rust AIDL örneğine bakın.
İçe aktarma türleri
Tanımlanan türün arayüz, paketlenebilir veya birleşim olup olmadığına bakılmaksızın, Java'da içe aktarabilirsiniz:
import my.package.IFoo;
Veya CPP 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>
Alternatif olarak Rust arka ucunda:
use my_package::aidl::my::package::IFoo;
Java'da iç içe yerleştirilmiş bir türü içe aktarabilirsiniz ancak CPP ve NDK arka uçlarında kök türünün üstbilgisini eklemeniz gerekir. Örneğin, my/package/IFoo.aidl
içinde tanımlanan iç içe yerleştirilmiş bir tür Bar
içe aktarılı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.
Arayüz uygulama
Bir arayüzü uygulamak için yerel sap sınıfından devralmanız gerekir. Bir arayüzün uygulanması, hizmet yöneticisine veya android.app.ActivityManager
ile kaydedildiğinde genellikle hizmet, bir hizmetin istemcisi tarafından kaydedildiğinde ise geri çağırma olarak adlandırılır. Ancak, arayüz uygulamalarını tanımlamak için kullanıma bağlı olarak çeşitli adlar kullanılır. Sap sınıfı, bağlayıcı sürücüsünden komutları okur ve uyguladığınız yöntemleri yürütür. Şu şekilde bir AIDL dosyanız olduğunu varsayalım:
package my.package;
interface IFoo {
int doFoo();
}
Java'da, oluşturulan Stub
sınıfını genişletmeniz gerekir:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
CPP 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(())
}
}
Veya eşzamansız Rust ile:
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
süreciyle kaydedilir. Aşağıdaki API'lere ek olarak bazı API'ler hizmeti kontrol eder (yani hizmet kullanılamıyorsa hemen geri döner).
Kesin ayrıntılar için ilgili servicemanager
arayüzünü kontrol edin. Bu işlemleri yalnızca Android platformuna karşı derleme yaparken gerçekleştirebilirsiniz.
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"));
CPP 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ıyla 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 run on this thread.
std::future::pending().await
}
Diğer seçeneklerden önemli bir farkı, eşzamansız Rust ve tek iş parçacıklı bir çalışma zamanı kullanırken çağrı yapmamanızdır.
join_thread_pool
Bunun nedeni, Tokio'ya oluşturulan görevleri yürütebileceği bir iş parçacığı vermeniz gerekmesidir. Aşağıdaki ö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ıyla 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
işlevini çağırmak daha mantıklıdır. Asenkron bağlamdan çıkmak için aramayı block_in_place
içine almanız gerekir.
Ölümle bağlantı
Bir bağlayıcıya ev sahipliği yapan hizmet kapatıldığında bildirim almak isteyebilirsiniz. Bu, geri arama proxy'lerinin sızdırılmasını önlemeye veya hataların kurtarılmasına yardımcı olabilir. Bu çağrıları bağlayıcı proxy nesnelerinde yapın.
- Java'da
android.os.IBinder::linkToDeath
kullanın. - CPP arka ucunda
android::IBinder::linkToDeath
kullanın. - NDK arka ucunda
AIBinder_linkToDeath
kullanın. - Rust arka ucunda bir
DeathRecipient
nesnesi oluşturun, 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 etkin 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. İşlem kimliği (PID), bir işlem gönderen işlemin Linux işlem kimliğini ifade eder. Kullanıcı kimliği (UI), Linux kullanıcı kimliğini ifade eder. Tek yönlü bir arama alındığında arayan PID'si 0 olur. Bu işlevler, bağlayıcı işlem bağlamı dışında mevcut işlemin PID'sini ve UID'sini döndürür.
Java arka ucunda:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
CPP arka ucunda:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
NDK arka ucunda:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Rust arka ucunda arayüzü uygularken aşağıdaki bilgileri belirtin (varsayılan olarak ayarlanmasına izin vermek yerine):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Hata raporları ve hizmetler için hata ayıklama API'si
Hata raporları çalıştırıldığında (ör. adb bugreport
ile), çeşitli sorunların hata ayıklanmasına yardımcı olmak için sistemin her yerinden bilgi toplanır.
AIDL hizmetlerinde hata raporları, bilgilerini hata raporuna aktarmak için hizmet yöneticisine kaydedilen tüm hizmetlerde dumpsys
ikilisini kullanır. dumpsys
içeren bir hizmetten bilgi almak için komut satırında dumpsys
da kullanabilirsiniz.dumpsys SERVICE [ARGS]
C++ ve Java arka uçlarında, addService
için ek bağımsız değişkenler kullanarak hizmetlerin dökümünün alınacağı sırayı kontrol edebilirsiniz. Hata ayıklama sırasında bir hizmetin PID'sini almak için dumpsys --pid SERVICE
aracını da kullanabilirsiniz.
Hizmetinize özel çıkış eklemek için sunucu nesnenizdeki dump
yöntemini, AIDL dosyasında tanımlanan diğer IPC yöntemlerini uyguluyormuş gibi geçersiz kılın. Bunu yaparken döküm işlemini uygulama izniyle android.permission.DUMP
veya belirli UID'lerle sınırlayın.
Java arka ucunda:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
CPP 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 aşağıdaki bilgileri belirtin (varsayılan olarak ayarlanmasına izin vermek yerine):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Zayıf işaretçileri kullanma
Bağlayıcı nesneye zayıf bir referans tutabilirsiniz.
Java, WeakReference
'yı desteklese de yerel katmanda zayıf bağlayıcı referanslarını desteklemez.
CPP arka ucunda zayıf tür wp<IFoo>
'dı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ın:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
Arayüz tanımlayıcısını 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 kullanışlı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();
CPP 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 tanımlayıcıyı alabilirsiniz:
import my.package.IFoo;
... IFoo.DESCRIPTOR
CPP 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ığı
Yerel arka uçlarda, bir numaralandırmanın alabileceği olası değerler üzerinde yineleme yapabilirsiniz. Kod boyutuyla ilgili hususlar nedeniyle bu özellik Java'da desteklenmez.
AIDL'de tanımlanan bir enum MyEnum
için yineleme aşağıdaki şekilde sağlanır.
CPP arka ucunda:
::android::enum_range<MyEnum>()
NDK arka ucunda:
::ndk::enum_range<MyEnum>()
Rust arka ucunda:
MyEnum::enum_values()
İleti dizisi yönetimi
Bir süreçteki her libbinder
örneği bir iş parçacığı havuzu tutar. Çoğu kullanım alanında, bu değer tam olarak bir iş parçacığı havuzu olmalı ve tüm arka uçlar arasında paylaşılmalıdır.
Tek istisna, satıcı kodunun /dev/vndbinder
ile iletişim kurmak için libbinder
öğesinin başka bir kopyasını yüklemesidir. Bu, ayrı bir bağlayıcı düğümde olduğundan iş parçacığı havuzu paylaşılmaz.
Java arka ucunda, iş parçacığı havuzunun boyutu yalnızca artabilir (çünkü iş parçacığı havuzu zaten başlatılmıştır):
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, özellikle join_thread_pool
kullanımı söz konusu olduğunda, eşzamansız Rust kullanan uygulamalarda özel hususlara dikkat edilmesi gerekir. Bu konuyla ilgili daha fazla bilgi için hizmetleri kaydetme bölümüne bakı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ı zorunlu kılmasa da ayrılmış bir adla eşleşen alan veya tür adlarının kullanılması C++ ya da Java için derleme hatasına neden olabilir. Rust'ta alan veya tür, r#
öneki kullanılarak erişilebilen ham tanımlayıcı söz dizimi kullanılarak yeniden adlandırılır.
Ergonomik olmayan bağlamaları veya 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 varsa protokol uyumluluğunu koruyarak alanları güvenli bir şekilde yeniden adlandırabilirsiniz. Geliştirmeye devam etmek için kodunuzu güncellemeniz gerekebilir ancak önceden oluşturulmuş programlar birlikte çalışmaya devam eder.
Kaçınılması gereken adlar: