एआईडीएल बैकएंड, स्टब कोड जनरेशन के लिए टारगेट है. एआईडीएल फ़ाइलों का इस्तेमाल करते समय, उनका इस्तेमाल हमेशा किसी खास रनटाइम के साथ किसी खास भाषा में करें. इसके आधार पर के लिए अलग-अलग एआईडीएल बैकएंड इस्तेमाल करने चाहिए.
इस टेबल में, एपीआई सरफ़ेस की स्थिरता के बारे में बताया गया है
इस एपीआई सरफ़ेस के हिसाब से कोड को इस तरह कंपाइल किया जाएगा कि कोड
system.img
libbinder.so
बाइनरी से अलग डिलीवर किया गया.
एआईडीएल में ये बैकएंड होते हैं:
बैकएंड | भाषा | एपीआई प्लैटफ़ॉर्म | सिस्टम बनाएं |
---|---|---|---|
Java | Java | SDK/SystemApi (स्टेबल*) | सभी |
एनडीके | C++ | libbinder_ndk (स्टेबल*) | aidl_इंटरफ़ेस |
सीपीपी | C++ | लिबिंदर (अस्टेबल) | सभी |
रस्ट | रस्ट | libbinder_rs (स्टेबल*) | aidl_इंटरफ़ेस |
- एपीआई के ये प्लैटफ़ॉर्म स्टेबल हैं. हालांकि, इनमें से कई एपीआई हैं. जैसे, सेवा के लिए इस्तेमाल होने वाले एपीआई मैनेज नहीं किए जा सकते. ये संगठन के अंदर इस्तेमाल करने के लिए रिज़र्व हैं. साथ ही, दिखाई देता है. ऐप्लिकेशन में एआईडीएल का इस्तेमाल करने के तरीके के बारे में ज़्यादा जानने के लिए, यहां जाएं डेवलपर दस्तावेज़.
- Rust बैकएंड को Android 12 में पेश किया गया था; यह NDK बैकएंड, Android 10 के बाद से उपलब्ध है.
- रस्ट क्रेट
libbinder_ndk
के ऊपर बनाया गया है, जिससे यह स्थायी और पोर्टेबल. APEXes, बाइंडर क्रेट का इस्तेमाल किसी दूसरे व्यक्ति की तरह ही करते हैं अन्य चीज़ों पर भी ध्यान दें. Rust के हिस्से को APEX में बंडल करके शिप किया जाता है अंदर रखा हुआ था. यह सिस्टम पार्टीशन पर मौजूदlibbinder_ndk.so
पर निर्भर करता है.
सिस्टम बनाएं
बैकएंड के आधार पर, स्टब में एआईडीएल को कंपाइल करने के दो तरीके हैं कोड. बिल्ड सिस्टम के बारे में ज़्यादा जानकारी के लिए, Soong मॉड्यूल के बारे में जानकारी.
कोर बिल्ड सिस्टम
किसी भी cc_
या java_
Android.bp मॉड्यूल में (या उनके Android.mk
जैसे मॉड्यूल में),
.aidl
फ़ाइलों को सोर्स फ़ाइलों के तौर पर रखा जा सकता है. इस मामले में, Java/CPP
एआईडीएल के बैकएंड का इस्तेमाल किया जाता है (एनडीके बैकएंड नहीं) और क्लास
संबंधित AIDL फ़ाइलें, मॉड्यूल में अपने-आप जुड़ जाती हैं. विकल्प
जैसे कि local_include_dirs
, जो बिल्ड सिस्टम को इसका रूट पाथ बताता है
उस मॉड्यूल की एआईडीएल फ़ाइलों के बारे में, इन मॉड्यूल में aidl:
के तहत बताया जा सकता है
ग्रुप. ध्यान दें कि Rust बैकएंड का इस्तेमाल सिर्फ़ Rust के साथ किया जा सकता है. rust_
मॉड्यूल हैं
उस एआईडीएल फ़ाइलों में अलग तरीके से हैंडल की जाती है, लेकिन इन्हें सोर्स फ़ाइलों के तौर पर नहीं चुना जाता.
इसके बजाय, aidl_interface
मॉड्यूल एक rustlib
बनाता है, जिसे
<aidl_interface name>-rust
, जिनके साथ लिंक किया जा सकता है. ज़्यादा जानकारी के लिए, यह देखें
रस्ट एआईडीएल का उदाहरण.
aidl_इंटरफ़ेस
इस बिल्ड सिस्टम के साथ इस्तेमाल किए जाने वाले टाइप को स्ट्रक्चर्ड होना चाहिए. स्ट्रक्चर करने के लिए, पार्सल करने लायक में फ़ील्ड सीधे तौर पर होने चाहिए और उनमें टाइप की जानकारी नहीं होनी चाहिए जिन्हें सीधे लक्षित भाषाओं में परिभाषित किया जा सकता है. जानें कि स्ट्रक्चर्ड एआईडीएल स्थिर एआईडीएल, स्ट्रक्चर्ड बनाम स्टेबल एआईडीएल देखें.
प्रकार
टाइप के लिए, aidl
कंपाइलर को रेफ़रंस के तौर पर लागू किया जा सकता है.
इंटरफ़ेस बनाते समय aidl --lang=<backend> ...
शुरू करें. ऐसा करके, यह देखा जा सकता है कि
मिलने वाली इंटरफ़ेस फ़ाइल का इस्तेमाल करें. aidl_interface
मॉड्यूल का इस्तेमाल करने पर, आपको जानकारी दिखेगी
out/soong/.intermediates/<path to module>/
में आउटपुट.
Java/AIDL टाइप | C++ टाइप | एनडीके का टाइप | रस्ट टाइप |
---|---|---|---|
बूलियन | बूल | बूल | बूल |
बाइट | int8_t | int8_t | आई8 |
वर्ण | वर्ण16_t | वर्ण16_t | यू16 |
आईएनटी | पूर्णांक32_t | पूर्णांक32_t | आई32 |
लंबा | int64_t | int64_t | आई64 |
फ़्लोट | फ़्लोट | फ़्लोट | F32 |
डबल | डबल | डबल | एफ़64 |
स्ट्रिंग | android::स्ट्रिंग16 | std::स्ट्रिंग | स्ट्रिंग |
android.os.Parcelable | android::पार्सेबल | लागू नहीं | लागू नहीं |
आईबाइंडर | Android::IBinder | एनडीके::एसपीएआईबाइंडर | बाइंडर::SpIBinder |
टी[] | std::वेक्टर<T> | std::वेक्टर<T> | इसमें: &[T] आउट: Vec<T> |
बाइट[] | std::वेक्टर<uint8_t> | std::vector<int8_t>1 | इसमें: &[u8] आउट: Vec<u8> |
सूची<T> | std::वेक्टर<T>2 | std::वेक्टर<T>3 | इसमें: &[T]4 आउट: Vec<T> |
फ़ाइल वर्णनकर्ता | android::base::unique_fd | लागू नहीं | बाइंडर::पार्सल::ParcelFileDescriptor |
पार्सलफ़ाइलडिस्क्रिप्टर | android::os::ParcelFileDescriptor | एनडीके::ScopedFileDescriptor | बाइंडर::पार्सल::ParcelFileDescriptor |
इंटरफ़ेस प्रकार (T) | android::sp<T> | std::shared_ptr<T>7 | बाइंडर::मज़बूत |
पार्स किया जा सकने वाला टाइप (T) | T | T | T |
यूनियन टाइप (T)5 | T | T | T |
टी[N] 6 | std::अरे<T, N> | std::अरे<T, N> | [T; उ |
1. Android 12 या उसके बाद के वर्शन में, बाइट सरणियों का इस्तेमाल साथ में काम करने की वजहों के लिए, int8_t के बजाय uint8_t का इस्तेमाल करें.
2. C++ बैकएंड List<T>
के साथ काम करता है, जहां T
, String
में से एक है,
IBinder
, ParcelFileDescriptor
या पार्सल किया जा सकने वाला. Android में
13 या उससे ज़्यादा, T
कोई भी ऐसा हो सकता है जो पुराना न हो
(इसमें इंटरफ़ेस टाइप शामिल हैं). इसमें सरणियां शामिल नहीं हैं. AOSP का सुझाव है कि आप
T[]
जैसे कलेक्शन टाइप का इस्तेमाल करता है, क्योंकि ये सभी बैकएंड में काम करते हैं.
3. एनडीके बैकएंड List<T>
के साथ काम करता है, जहां T
, String
में से एक है,
ParcelFileDescriptor
या पार्सल किया जा सकने वाला. Android 13 में
या उससे बाद के वर्शन के लिए, तो T
कोई भी नॉन-प्रीमिटिव टाइप हो सकता है. हालांकि, इसमें रेंज शामिल नहीं हैं.
4. Rust कोड के लिए, टाइप अलग-अलग तरीके से पास किए जाते हैं. यह इस बात पर निर्भर करता है कि वे इनपुट (एक आर्ग्युमेंट) या एक आउटपुट (लौटी गई वैल्यू) है.
5. यूनियन टाइप, Android 12 और उच्च.
6. Android 13 या उसके बाद वाले वर्शन में, तय साइज़ के अरे
समर्थित हैं. तय साइज़ वाले कलेक्शन में कई डाइमेंशन हो सकते हैं (जैसे, int[3][4]
).
Java बैकएंड में, तय साइज़ की अरे को अरे टाइप के तौर पर दिखाया जाता है.
7. बाइंडर SharedRefBase
ऑब्जेक्ट को इंस्टैंशिएट करने के लिए, इसका इस्तेमाल करें
SharedRefBase::make\<My\>(... args ...)
. इस फ़ंक्शन की मदद से
std::shared_ptr\<T\>
ऑब्जेक्ट
इसे कंपनी के अंदर भी मैनेज किया जाता है. अगर बाइंडर का मालिकाना हक किसी अन्य कंपनी के पास है, तो
प्रोसेस. ऑब्जेक्ट को दूसरे तरीकों से बनाने से, दो बार मालिकाना हक होता है.
दिशा (इन/आउट/इनआउट)
फ़ंक्शन के लिए आर्ग्युमेंट के टाइप तय करते समय,
उन्हें in
, out
या inout
के तौर पर सबमिट करें. इससे यह कंट्रोल होता है कि जानकारी किस दिशा में होगी
को पास किया गया. in
डिफ़ॉल्ट दिशा है और यह बताता है कि डेटा यह है
कॉल करने वाले के पास से कॉल करने वाले व्यक्ति को भेजा जाता है. out
का मतलब है कि डेटा
कॉल करने वाले (कॉलर) को कॉल करने वाला. inout
इन दोनों का कॉम्बिनेशन है. हालांकि,
Android टीम का सुझाव है कि आप आर्ग्युमेंट की खास जानकारी देने वाले inout
का इस्तेमाल न करें.
अगर आप वर्शन वाले इंटरफ़ेस और पुराने कॉली के साथ inout
का इस्तेमाल करते हैं, तो
अतिरिक्त फ़ील्ड जो केवल कॉलर में मौजूद होते हैं, अपने डिफ़ॉल्ट पर रीसेट हो जाते हैं
वैल्यू. Rust के हिसाब से, सामान्य inout
टाइप को &mut Vec<T>
मिलता है और
सूची inout
टाइप को &mut Vec<T>
मिलता है.
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
सीपीपी बैकएंड से, यह चुना जा सकता है कि स्ट्रिंग utf-8 है या utf-16. बताएं
एआईडीएल में @utf8InCpp String
जैसी स्ट्रिंग होती हैं, ताकि उन्हें utf-8 में अपने-आप बदला जा सके.
NDK और Rust बैकएंड हमेशा utf-8 स्ट्रिंग का इस्तेमाल करते हैं. इसके बारे में ज़्यादा जानकारी पाने के लिए,
utf8InCpp
एनोटेशन के लिए, एआईडीएल में एनोटेशन देखें.
शून्य होने की क्षमता
आपके पास ऐसे टाइप के बारे में बताने का विकल्प होता है जो @nullable
की मदद से Java बैकएंड में शून्य हो सकते हैं
सीपीपी और एनडीके बैकएंड में शून्य वैल्यू दिखाने के लिए. Rust बैकएंड में
@nullable
टाइप को Option<T>
के तौर पर दिखाया जाता है. नेटिव सर्वर, शून्य वैल्यू को अस्वीकार करते हैं
डिफ़ॉल्ट रूप से. इसके अपवाद सिर्फ़ interface
और IBinder
टाइप हैं,
जो एनडीके रीड और सीपीपी/एनडीके राइट के लिए हमेशा शून्य हो सकता है. Reader Revenue Manager को सेट अप करने के बारे में
nullable
के बारे में जानकारी यहां देखें
एआईडीएल में एनोटेशन.
पसंद के मुताबिक पार्सल किए जा सकने वाले प्रॉडक्ट
पसंद के मुताबिक पार्स किया जा सकने वाला ऐसा पार्सल किया जा सकता है जिसे टारगेट में मैन्युअल तरीके से लागू किया जाता है बैकएंड. पसंद के मुताबिक पार्स किए जा सकने वाले पार्स किए जा सकने वाले एलिमेंट का इस्तेमाल सिर्फ़ तब करें, जब आपको किसी मौजूदा कस्टम पार्सल की भाषाएं, जिन्हें बदला नहीं जा सकता.
एआईडीएल को कस्टम पार्स किए जा सकने वाले किसी इवेंट के बारे में बताने के लिए, एआईडीएल को इसके बारे में जानकारी होनी चाहिए पार्स किया जा सकने वाला डिक्लेरेशन ऐसा दिखता है:
package my.pack.age;
parcelable Foo;
डिफ़ॉल्ट रूप से, यह एक Java पार्सल होने का एलान करता है, जहां my.pack.age.Foo
एक Java है
Parcelable
इंटरफ़ेस को लागू करने वाली क्लास.
एआईडीएल में कस्टम सीपीपी बैकएंड पार्सल करने की जानकारी देने के लिए, cpp_header
का इस्तेमाल करें:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
में C++ लागू करने पर यह दिखाई देगा:
#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);
};
एआईडीएल में पसंद के मुताबिक एनडीके पार्सल करने का एलान करने के लिए, ndk_header
का इस्तेमाल करें:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
में एनडीके लागू करने का तरीका ऐसा दिखता है:
#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 (एओएसपी एक्सपेरिमेंट के तौर पर) में, कस्टम Rust का एलान करने के लिए
एआईडीएल में पार्सल किया जा सकता है, rust_type
का इस्तेमाल करें:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
में Rust लागू करें:
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);
इसके बाद, पार्सल किए जा सकने वाले इस डेटा का इस्तेमाल एआईडीएल फ़ाइलों में टाइप के तौर पर किया जा सकता है. हालांकि, ऐसा नहीं होगा
इन्हें एआईडीएल ने जनरेट किया है. सीपीपी/एनडीके बैकएंड के लिए, <
और ==
ऑपरेटर उपलब्ध कराएं
कस्टम पार्सल, ताकि आप उन्हें union
में इस्तेमाल कर सकें.
डिफ़ॉल्ट वैल्यू
स्ट्रक्चर्ड पार्सल, प्रिमिटिव के लिए हर फ़ील्ड में डिफ़ॉल्ट वैल्यू का एलान कर सकता है,
String
और इन टाइप की कैटगरी.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Java बैकएंड में, डिफ़ॉल्ट वैल्यू न होने पर, फ़ील्ड इस तरह शुरू होते हैं
प्रिमिटिव टाइप के लिए शून्य वैल्यू और नॉन-प्रीमिटिव टाइप के लिए null
.
अन्य बैकएंड में, फ़ील्ड को डिफ़ॉल्ट शुरुआती वैल्यू के साथ शुरू किया जाता है. ऐसा तब होता है, जब
डिफ़ॉल्ट वैल्यू के बारे में नहीं बताया गया है. उदाहरण के लिए, C++ बैकएंड में String
फ़ील्ड
एक खाली स्ट्रिंग के तौर पर शुरू किए जाते हैं और List<T>
फ़ील्ड को
vector<T>
खाली है. @nullable
फ़ील्ड को शून्य-वैल्यू वाले फ़ील्ड के तौर पर शुरू किया जाता है.
गड़बड़ी ठीक करना
Android OS, रिपोर्टिंग के समय सेवाओं के लिए पहले से मौजूद गड़बड़ी के टाइप उपलब्ध कराता है गड़बड़ियां हैं. इनका इस्तेमाल बाइंडर में किया जाता है. साथ ही, इन्हें लागू करने वाली किसी भी सेवा में इस्तेमाल किया जा सकता है बाइंडर इंटरफ़ेस होता है. एआईडीएल की परिभाषा में इन टूल के इस्तेमाल के बारे में पुख्ता सबूत मौजूद हैं और उन्हें उपयोगकर्ता की ओर से तय किए गए किसी स्टेटस या रिटर्न टाइप की ज़रूरत नहीं होती है.
गड़बड़ी वाले आउटपुट पैरामीटर
जब कोई AIDL फ़ंक्शन किसी गड़बड़ी की रिपोर्ट करता है, तो हो सकता है कि फ़ंक्शन शुरू न हो या
आउटपुट पैरामीटर में बदलाव कर सकता है. खास तौर पर, आउटपुट पैरामीटर में तब बदलाव किया जा सकता है, जब
प्रोसेस करने के दौरान होने वाली गड़बड़ी के बजाय, पार्सल होने के दौरान गड़बड़ी होती है
उस लेन-देन पर लागू होता है. आम तौर पर, एआईडीएल से गड़बड़ी मिलने पर
फ़ंक्शन है, सभी inout
और out
पैरामीटर और साथ ही रिटर्न मान (जो
कुछ बैकएंड में out
पैरामीटर की तरह काम करता है)
अनिश्चित अवधि तक.
गड़बड़ी वाली किन वैल्यू का इस्तेमाल करना है
पहले से मौजूद कई गड़बड़ी वाली वैल्यू का इस्तेमाल किसी भी एआईडीएल इंटरफ़ेस में किया जा सकता है. हालांकि, कुछ हद तक
एक खास तरीका अपनाया जाता है. उदाहरण के लिए, EX_UNSUPPORTED_OPERATION
और
गड़बड़ी की स्थिति के बारे में बताने के लिए, EX_ILLEGAL_ARGUMENT
का इस्तेमाल किया जा सकता है, लेकिन
EX_TRANSACTION_FAILED
का इस्तेमाल नहीं किया जाना चाहिए, क्योंकि इसे खास
किया जा सकता है. ज़्यादा जानकारी के लिए, बैकएंड से जुड़ी खास परिभाषाओं की जांच करें
इन बिल्ट-इन वैल्यू की जानकारी मिलेगी.
अगर एआईडीएल इंटरफ़ेस को गड़बड़ी की ऐसी अतिरिक्त वैल्यू की ज़रूरत होती है जो इसके तहत नहीं आती हैं
कोई गड़बड़ी होती है, तो वे खास सेवा-विशिष्ट बिल्ट-इन
की गड़बड़ी को दिखाता है, जो उस सेवा-विशिष्ट गड़बड़ी मान को शामिल करने की अनुमति देता है, जो
तय करती है. सेवा से जुड़ी इन गड़बड़ियों को आम तौर पर
AIDL इंटरफ़ेस, const int
या int
पर आधारित enum
के तौर पर काम करता है और इसे पार्स नहीं किया जाता
बाइंडर.
Java में, गड़बड़ियां अपवाद जैसे कि android.os.RemoteException
से मैप होती हैं. इसके लिए
सेवा-विशिष्ट अपवाद, Java android.os.ServiceSpecificException
का उपयोग करता है
साथ ही, आपको गड़बड़ी की जानकारी भी दिखेगी.
Android में स्थानीय कोड, अपवादों का इस्तेमाल नहीं करता. सीपीपी बैकएंड इनका इस्तेमाल करता है
android::binder::Status
. NDK बैकएंड, ndk::ScopedAStatus
का इस्तेमाल करता है. कई
एआईडीएल से जनरेट हुआ तरीका, इनमें से कोई एक वैल्यू दिखाता है, जो
तरीका. Rust बैकएंड, एनडीके की तरह ही अपवाद कोड वैल्यू का इस्तेमाल करता है, लेकिन
उन्हें इससे पहले नेटिव Rust गड़बड़ियों (StatusCode
, ExceptionCode
) में बदल देता है
उपयोगकर्ता को डिलीवर करते हैं. सेवा से जुड़ी गड़बड़ियों की वजह से,
Status
या ScopedAStatus
, EX_SERVICE_SPECIFIC
का इस्तेमाल
उपयोगकर्ता से मिली गड़बड़ी.
इन फ़ाइलों में, बिल्ट-इन गड़बड़ियों के टाइप देखे जा सकते हैं:
बैकएंड | परिभाषा |
---|---|
Java | android/os/Parcel.java |
सीपीपी | binder/Status.h |
एनडीके | android/binder_status.h |
रस्ट | android/binder_status.h |
अलग-अलग बैकएंड का इस्तेमाल करना
ये निर्देश खास तौर पर Android प्लैटफ़ॉर्म कोड के लिए हैं. ये उदाहरण
तय टाइप, my.package.IFoo
. Rust बैकएंड का इस्तेमाल करने के तरीके के बारे में निर्देश पाने के लिए,
रस्ट एआईडीएल का उदाहरण देखें
Android रस्ट पैटर्न के बारे में ज़्यादा जानें
करें.
इंपोर्ट के टाइप
तय किया गया टाइप कोई इंटरफ़ेस, पार्स किया जा सकने वाला या यूनियन हो, तो आपके पास इसे Java में:
import my.package.IFoo;
या सीपीपी बैकएंड में:
#include <my/package/IFoo.h>
या एनडीके बैकएंड में (अतिरिक्त aidl
नेमस्पेस पर देखें):
#include <aidl/my/package/IFoo.h>
या Rust बैकएंड में:
use my_package::aidl::my::package::IFoo;
हालांकि, Java में नेस्ट किए गए टाइप को इंपोर्ट किया जा सकता है, लेकिन सीपीपी/एनडीके बैकएंड में आपको
उसके रूट टाइप के लिए हेडर शामिल करना चाहिए. उदाहरण के लिए, नेस्ट किए गए डेटा टाइप को इंपोर्ट करते समय
my/package/IFoo.aidl
में तय किया गया Bar
(IFoo
फ़ाइल) आपको सीपीपी बैकएंड के लिए <my/package/IFoo.h>
शामिल करना होगा (या
एनडीके बैकएंड के लिए <aidl/my/package/IFoo.h>
).
सेवाएं लागू करना
किसी सेवा को लागू करने के लिए, आपको नेटिव स्टब क्लास से इनहेरिट करना होगा. यह क्लास बाइंडर ड्राइवर के कमांड पढ़ता है और उन तरीकों को एक्ज़ीक्यूट करता है जिनके लिए लागू करें. मान लें कि आपके पास एक ऐसी एआईडीएल फ़ाइल है:
package my.package;
interface IFoo {
int doFoo();
}
Java में, आपको इस क्लास से भी विस्तार करना होगा:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
सीपीपी बैकएंड में:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
एनडीके बैकएंड में (अतिरिक्त aidl
नेमस्पेस पर देखें):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
Rust बैकएंड में:
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(())
}
}
या एक साथ काम नहीं करने वाले रस्ट के साथ:
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(())
}
}
रजिस्टर करें और सेवाएं पाएं
Android प्लैटफ़ॉर्म की सेवाएं आम तौर पर, servicemanager
के साथ रजिस्टर की जाती हैं
प्रोसेस. नीचे दिए गए एपीआई के अलावा, कुछ एपीआई
सेवा (यानी सेवा उपलब्ध न होने पर वह तुरंत वापस लौट जाए).
सटीक जानकारी के लिए, इससे जुड़ा servicemanager
इंटरफ़ेस देखें. ये
कार्रवाइयों को प्लैटफ़ॉर्म Android से कंपाइल करते समय ही किया जा सकता है.
Java में:
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"));
सीपीपी बैकएंड में:
#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"));
एनडीके बैकएंड में (अतिरिक्त aidl
नेमस्पेस पर देखें):
#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 बैकएंड में:
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()
}
एक साथ काम नहीं करने वाली रस्ट बैकएंड में, एक थ्रेड वाले रनटाइम के साथ:
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
}
अन्य विकल्पों से एक महत्वपूर्ण अंतर यह है कि हम कॉल नहीं करते हैं
एक साथ काम नहीं करने वाली रस्ट और सिंगल-थ्रेड रनटाइम का इस्तेमाल करते समय join_thread_pool
. यह है
क्योंकि आपको Tokio को एक ऐसा थ्रेड देना होगा जिसमें वह पैदा किए गए टास्क कर सके. तय सीमा में
मुख्य थ्रेड में यह मकसद पूरा किया जाएगा. इसका इस्तेमाल करके शुरू किए गए टास्क
tokio::spawn
मुख्य थ्रेड पर काम करेगा.
एक से ज़्यादा थ्रेड वाले रनटाइम के साथ, एसिंक्रोनस रस्ट बैकएंड में:
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();
});
}
एक से ज़्यादा थ्रेड वाले Tokio रनटाइम के साथ, जनरेट किए गए टास्क मुख्य साइट पर लागू नहीं होते हैं
थ्रेड. इसलिए, join_thread_pool
को मुख्य
थ्रेड कर सकें, ताकि मुख्य थ्रेड कुछ समय से इस्तेमाल में न रहे. आपको कॉल को रैप करना होगा
block_in_place
का इस्तेमाल करें.
मौत की जानकारी देने वाला लिंक
बाइंडर होस्ट करने वाली किसी सेवा की मौत होने पर, सूचना पाने का अनुरोध किया जा सकता है. इससे कॉलबैक प्रॉक्सी को लीक होने से बचाने या गड़बड़ी ठीक करने में मदद मिल सकती है. ये कॉल बाइंडर प्रॉक्सी ऑब्जेक्ट पर करें.
- Java में,
android.os.IBinder::linkToDeath
का इस्तेमाल करें. - सीपीपी बैकएंड में,
android::IBinder::linkToDeath
का इस्तेमाल करें. - एनडीके बैकएंड में,
AIBinder_linkToDeath
का इस्तेमाल करें. - Rust बैकएंड में,
DeathRecipient
ऑब्जेक्ट बनाएं. इसके बाद, कॉल करेंmy_binder.link_to_death(&mut my_death_recipient)
. ध्यान दें कि कॉलबैक का मालिकDeathRecipient
है. आपको उस ऑब्जेक्ट को लंबे समय तक चालू रखना होगा सूचना पाने का विकल्प चुनें.
कॉल करने वाले (कॉलर) की जानकारी
कर्नेल बाइंडर कॉल प्राप्त करते समय, कॉलर की जानकारी इसमें उपलब्ध होती है कई एपीआई का इस्तेमाल कर रहे हैं. पीआईडी (या प्रोसेस आईडी) का मतलब Linux प्रोसेस आईडी से है जो लेन-देन भेज रही है. यूआईडी (या यूज़र आईडी) का मतलब है Linux यूज़र आईडी. एकतरफ़ा कॉल आने पर, कॉल करने वाला पीआईडी 0 होता है. टास्क कब शुरू होगा बाइंडर ट्रांज़ैक्शन कॉन्टेक्स्ट के बाहर, ये फ़ंक्शन पीआईडी और यूआईडी वापस करते हैं मौजूदा प्रोसेस के तहत आता है.
Java बैकएंड में:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
सीपीपी बैकएंड में:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
एनडीके बैकएंड में:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Rust बैकएंड में, इंटरफ़ेस लागू करते समय, यह जानकारी दें (इसे डिफ़ॉल्ट की अनुमति देने के बजाय):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
सेवाओं के लिए गड़बड़ी की रिपोर्ट और डीबग करने वाला एपीआई
जब गड़बड़ी की रिपोर्ट चलती हैं (जैसे कि adb bugreport
के साथ), तो वे ये जानकारी इकट्ठा करती हैं
अलग-अलग तरह की समस्याओं को डीबग करने में मदद करने के लिए, पूरे सिस्टम से जानकारी इकट्ठा करें.
एआईडीएल सेवाओं के लिए, गड़बड़ी की रिपोर्ट सभी सेवाओं पर बाइनरी dumpsys
का इस्तेमाल करती हैं
को सेवा मैनेजर के साथ, अपनी जानकारी को
गड़बड़ी की रिपोर्ट. जानकारी पाने के लिए, कमांडलाइन पर dumpsys
का भी इस्तेमाल किया जा सकता है
dumpsys SERVICE [ARGS]
की सेवा से. C++ और Java बैकएंड में,
अतिरिक्त आर्ग्युमेंट का इस्तेमाल करके, सेवाओं के क्रम को कंट्रोल कर सकता है
addService
तक. dumpsys --pid SERVICE
का इस्तेमाल करके,
डीबग करने के दौरान सेवा.
अपनी सेवा में कस्टम आउटपुट जोड़ने के लिए, dump
को बदलें
कुछ अन्य तरीकों का इस्तेमाल करें, जैसे कि आप कोई दूसरा आईपीसी तरीका लागू कर रहे हों.
बताई गई किसी AIDL फ़ाइल में मौजूद होती है. ऐसा करते समय, आपको ऐप्लिकेशन में डंप करने पर पाबंदी लगा देनी चाहिए
android.permission.DUMP
या किसी खास यूआईडी पर डंप करने पर पाबंदी लगाएं.
Java बैकएंड में:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
सीपीपी बैकएंड में:
status_t dump(int, const android::android::Vector<android::String16>&) override;
एनडीके बैकएंड में:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
Rust बैकएंड में, इंटरफ़ेस लागू करते समय, यह जानकारी दें (इसे डिफ़ॉल्ट की अनुमति देने के बजाय):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
डायनैमिक तौर पर इंटरफ़ेस डिस्क्रिप्टर पाएं
इंटरफ़ेस डिस्क्रिप्टर से यह पता चलता है कि इंटरफ़ेस किस तरह का है. यह काम का है डीबग करते समय या जब आपके पास कोई अनजान बाइंडर होता है.
Java में, आपको कोड के साथ इंटरफ़ेस डिस्क्रिप्टर मिल सकता है, जैसे:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
सीपीपी बैकएंड में:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
NDK और Rust बैकएंड में, यह सुविधा काम नहीं करती.
स्टैटिक रूप से इंटरफ़ेस डिस्क्रिप्टर पाएं
कभी-कभी, जैसे कि @VintfStability
की सेवाओं को रजिस्टर करते समय, आपको ये काम करने की ज़रूरत पड़ती है
जानें कि इंटरफ़ेस डिस्क्रिप्टर स्टैटिक है. Java में, आप
डिस्क्रिप्टर जोड़ने के लिए कोड जोड़ें, जैसे:
import my.package.IFoo;
... IFoo.DESCRIPTOR
सीपीपी बैकएंड में:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
एनडीके बैकएंड में (अतिरिक्त aidl
नेमस्पेस पर देखें):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
Rust बैकएंड में:
aidl::my::package::BnFoo::get_descriptor()
Enum रेंज
नेटिव बैकएंड में, ईनम से ली जा सकने वाली संभावित वैल्यू को दोहराया जा सकता है चालू करें. कोड के साइज़ की वजह से, यह Java में काम नहीं करता.
एआईडीएल में तय की गई MyEnum
ईनम के लिए, वैल्यू को इस तरह क्रम में लगाया जाता है.
सीपीपी बैकएंड में:
::android::enum_range<MyEnum>()
एनडीके बैकएंड में:
::ndk::enum_range<MyEnum>()
Rust बैकएंड में:
MyEnum::enum_values()
थ्रेड मैनेज करना
प्रोसेस में libbinder
के हर इंस्टेंस में एक थ्रेडपूल होता है. ज़्यादातर के लिए
इस्तेमाल के उदाहरण के लिए, यह सिर्फ़ एक थ्रेडपूल होना चाहिए, जिसे सभी बैकएंड के साथ शेयर किया जाना चाहिए.
हालांकि, यह सिर्फ़ तब अपवाद हो सकता है, जब वेंडर कोड libbinder
की एक और कॉपी लोड कर दे
/dev/vndbinder
से बात करने के लिए. यह एक अलग बाइंडर नोड पर है, इसलिए
थ्रेडपूल शेयर नहीं किया जा सकता.
Java बैकएंड के लिए, थ्रेडपूल का आकार सिर्फ़ बढ़ सकता है (क्योंकि यह पहले ही शुरू कर दिया गया है):
BinderInternal.setMaxThreads(<new larger value>);
सीपीपी बैकएंड के लिए, ये काम किए जा सकते हैं:
// 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();
इसी तरह, एनडीके बैकएंड में:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
Rust बैकएंड में:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
एक साथ काम नहीं करने वाले Rust बैकएंड के साथ, आपको दो थ्रेडपूल की ज़रूरत होगी: बाइंडर और Tokio.
इसका मतलब यह है कि एक साथ काम नहीं करने वाले Rust का इस्तेमाल करने वाले ऐप्लिकेशन पर खास ध्यान देने की ज़रूरत है,
खास तौर पर, जब join_thread_pool
के इस्तेमाल की बात हो. इसके बारे में ज़्यादा जानने के लिए
सेवाओं को रजिस्टर करना न भूलें.
रिज़र्व किए गए नाम
C++, Java और Rust, कीवर्ड के रूप में या किसी भाषा के लिए कुछ नाम सुरक्षित रखते हैं
इस्तेमाल करें. एआईडीएल, भाषा के नियमों के आधार पर पाबंदियां लागू नहीं करता है. हालांकि,
रिज़र्व किए गए नाम से मेल खाने वाले फ़ील्ड या टाइप के नाम को कंपाइलेशन में बदला जा सकता है
C++ या Java का इस्तेमाल करते समय गड़बड़ी हुई. Rust के लिए, फ़ील्ड या टाइप का नाम
"रॉ आइडेंटिफ़ायर" सिंटैक्स, जिसे r#
प्रीफ़िक्स का इस्तेमाल करके ऐक्सेस किया जा सकता है.
हमारा सुझाव है कि आप एआईडीएल की परिभाषाओं में रिज़र्व किए गए नामों का इस्तेमाल न करें जहां तक हो सके, बिना किसी क्रम के बाइंडिंग या पूरी तरह से कंपाइलेशन के काम न करने से बचा जा सके.
अगर एआईडीएल परिभाषाओं में पहले से ही रिज़र्व किए गए नाम हैं, तो प्रोटोकॉल के साथ काम करने के दौरान, फ़ील्ड के नाम बदलें; आपको अपना खाता अपडेट करना पड़ सकता है कोड बनाना जारी रखने के लिए, लेकिन पहले से बनाए गए प्रोग्राम एक-दूसरे से जुड़ी हुई हैं.
इन नामों से बचें: