AIDL, एनोटेशन के साथ काम करता है. ये एनोटेशन, AIDL कंपाइलर को एनोटेट किए गए एलिमेंट के बारे में अतिरिक्त जानकारी देते हैं. इससे जनरेट किए गए स्टब कोड पर भी असर पड़ता है.
इसका सिंटैक्स, Java के सिंटैक्स से मिलता-जुलता है:
@AnnotationName(argument1=value, argument2=value) AidlEntity
यहां, AnnotationName एनोटेशन का नाम है और AidlEntity, interface Foo, void method() या int arg जैसी एआईडीएल इकाई है. एनोटेशन, उसके बाद आने वाली इकाई से जुड़ा होता है.
कुछ एनोटेशन में, ब्रैकेट के अंदर आर्ग्युमेंट सेट किए जा सकते हैं. जैसा कि पिछले उदाहरण में दिखाया गया है. जिन एनोटेशन में कोई तर्क नहीं होता उनके लिए कोष्ठक की ज़रूरत नहीं होती. उदाहरण के लिए:
@AnnotationName AidlEntity
ये एनोटेशन, Java एनोटेशन से अलग होते हैं. हालांकि, ये एक जैसे दिखते हैं. सभी एनोटेशन पहले से तय होते हैं. साथ ही, इन्हें अटैच करने के लिए कुछ पाबंदियां होती हैं. कुछ एनोटेशन सिर्फ़ किसी खास बैकएंड पर असर डालते हैं और अन्य बैकएंड में काम नहीं करते.
यहां AIDL के पहले से तय किए गए एनोटेशन की सूची दी गई है:
| टिप्पणियां | Android वर्शन में जोड़ा गया |
|---|---|
nullable | 7 |
utf8InCpp | 7 |
VintfStability | 11 |
UnsupportedAppUsage | 10 |
Hide | 11 |
Backing | 11 |
NdkOnlyStableParcelable | 14 |
JavaOnlyStableParcelable | 11 |
JavaDerive | 12 |
JavaPassthrough | 12 |
FixedSize | 12 |
Descriptor | 12 |
शून्य हो सकता है
nullable यह एलान करता है कि एनोटेट की गई इकाई की वैल्यू शून्य हो सकती है.
इस एनोटेशन को सिर्फ़ इन चीज़ों से अटैच किया जा सकता है: मेथड के रिटर्न टाइप, मेथड के पैरामीटर, और पार्सल किए जा सकने वाले फ़ील्ड:
interface IFoo {
// method return types
@nullable Data method();
// method parameters
void method2(in @nullable Data d);
}
parcelable Data {
// parcelable fields
@nullable Data d;
}
प्रिमिटिव टाइप में एनोटेशन अटैच नहीं किए जा सकते. यहां एक गड़बड़ी दी गई है:
void method(in @nullable int a); // int is a primitive type
यह एनोटेशन, Java बैकएंड के लिए कोई कार्रवाई नहीं करता. Java में, सभी नॉनप्रिमिटिव टाइप को रेफ़रंस के तौर पर पास किया जाता है. ऐसा null हो सकता है.
सीपीपी बैकएंड में, Android 11 या इससे पहले के वर्शन में @nullable T को std::unique_ptr<T> के तौर पर मैप किया जाता है. वहीं, Android 12 या इसके बाद के वर्शन में इसे std::optional<T> के तौर पर मैप किया जाता है.
एनडीके बैकएंड में, @nullable T को std::optional<T> पर मैप किया जाता है.
Rust बैकएंड में, @nullable T को Option<T> पर मैप किया जाता है.
सूची जैसे टाइप L के लिए, T[] या List<T>, @nullable L, std::optional<std::vector<std::optional<T>>> पर मैप करता है. Android 11 या इससे पहले के वर्शन के लिए, सीपीपी बैकएंड के मामले में std::unique_ptr<std::vector<std::unique_ptr<T>>> पर मैप करता है.
इस मैपिंग के लिए एक अपवाद है. अगर T, IBinder या AIDL इंटरफ़ेस है, तो Java, CPP, और NDK बैकएंड के लिए @nullable कोई कार्रवाई नहीं करता. उदाहरण के लिए, CPP बैकएंड में @nullable IBinder और IBinder, दोनों को android::sp<IBinder> पर मैप किया जाता है. यह पहले से ही नल हो सकता है, क्योंकि यह एक स्ट्रॉन्ग पॉइंटर है. CPP और NDK रीड, अब भी शून्यता को लागू करते हैं, लेकिन टाइप android::sp<IBinder> या ndk::SpAIBinder ही रहता है.
Rust बैकएंड में, Default (IBinder, AIDL इंटरफ़ेस, ParcelFileDescriptor, और अनस्ट्रक्चर्ड पार्सलेबल) लागू न करने वाले टाइप, उनके कॉन्टेक्स्ट के आधार पर Option<T> पर मैप होते हैं:
- मेथड के रिटर्न टाइप,
inपैरामीटर, औरinoutपैरामीटर: ये टाइप सिर्फ़ तब नल हो सकते हैं, जब इन्हें@nullableके साथ एनोटेट किया गया हो. यहOption<T>(याinपैरामीटर के लिएOption<&T>औरinoutपैरामीटर के लिए&mut Option<T>) पर मैप करता है.@nullableके बिना, वे सीधे तौर परT(या&Tऔर&mut T) से मैप होते हैं. - पार्सल किए जा सकने वाले फ़ील्ड और
outपैरामीटर: जनरेट किए गए Rust पार्सल किए जा सकने वाले फ़ील्ड,Defaultऔरoutपैरामीटर डिफ़ॉल्ट रूप से शुरू होते हैं. इसलिए, ये टाइप हमेशाOption<T>पर मैप होते हैं. साथ ही, इन कॉन्टेक्स्ट में तय साइज़ वाले ऐरे,[Option<T>; N]पर मैप होते हैं. वहीं,outपैरामीटर में मौजूद डाइनैमिक ऐरे,Vec<Option<T>>पर मैप होते हैं. ऐसा@nullableके बिना भी होता है.@nullableके साथ एनोटेट न किए जाने पर, पार्सल करते समय रनटाइम के दौरान, शून्य न होने की स्थिति लागू की जाती है. अगर वैल्यूNoneहै, तोStatusCode::UNEXPECTED_NULLदिखाता है.
Android 13 से, @nullable(heap=true) का इस्तेमाल पार्सल किए जा सकने वाले फ़ील्ड के लिए किया जा सकता है, ताकि रिकर्सिव टाइप को मॉडल किया जा सके. @nullable(heap=true) का इस्तेमाल, मेथड पैरामीटर या रिटर्न टाइप के साथ नहीं किया जा सकता. इस एनोटेशन का इस्तेमाल करने पर, फ़ील्ड को CPP और NDK बैकएंड में हीप-एलॉकेट किए गए रेफ़रंस std::unique_ptr<T> पर मैप किया जाता है. @nullable(heap=true), Java बैकएंड में कोई कार्रवाई नहीं करता है.
utf8InCpp
utf8InCpp यह एलान करता है कि CPP बैकएंड के लिए, String को UTF8 फ़ॉर्मैट में दिखाया गया है. जैसा कि इसके नाम से पता चलता है, यह एनोटेशन अन्य बैकएंड के लिए कोई कार्रवाई नहीं करता है.
खास तौर पर, String Java बैकएंड में हमेशा UTF16 और NDK बैकएंड में UTF8 होता है.
इस एनोटेशन को किसी भी ऐसी जगह पर अटैच किया जा सकता है जहां String टाइप का इस्तेमाल किया जा सकता है. जैसे, रिटर्न वैल्यू, पैरामीटर, कॉन्स्टेंट के एलान, और पार्सल किए जा सकने वाले फ़ील्ड.
CPP बैकएंड के लिए, AIDL में @utf8InCpp String, std::string पर मैप करता है. यहां, एनोटेशन के बिना String, android::String16 पर मैप करता है, जहां UTF16 का इस्तेमाल किया जाता है.
VintfStability
VintfStability यह एलान करता है कि उपयोगकर्ता की ओर से तय किए गए टाइप (इंटरफ़ेस, पार्सल करने लायक, और enum) का इस्तेमाल सिस्टम और वेंडर के डोमेन में किया जा सकता है. सिस्टम और वेंडर के बीच इंटरऑपरेबिलिटी के बारे में ज़्यादा जानने के लिए, एचएएल के लिए एआईडीएल लेख पढ़ें.
एनोटेशन, टाइप के सिग्नेचर को नहीं बदलता. हालांकि, जब इसे सेट किया जाता है, तो टाइप के इंस्टेंस को स्टेबल के तौर पर मार्क किया जाता है, ताकि यह वेंडर और सिस्टम प्रोसेस में काम कर सके.
एनोटेशन को सिर्फ़ उपयोगकर्ता की ओर से तय किए गए टाइप के एलान से जोड़ा जा सकता है. जैसा कि यहां दिखाया गया है:
@VintfStability
interface IFoo {
....
}
@VintfStability
parcelable Data {
....
}
@VintfStability
enum Type {
....
}
जब किसी टाइप को VintfStability से एनोटेट किया जाता है, तो उस टाइप में रेफ़र किए गए किसी भी अन्य टाइप को भी इसी तरह एनोटेट किया जाना चाहिए. यहां दिए गए उदाहरण में, Data और IBar, दोनों को VintfStability से एनोटेट किया जाना चाहिए:
@VintfStability
interface IFoo {
void doSomething(in IBar b); // references IBar
void doAnother(in Data d); // references Data
}
@VintfStability // required
interface IBar {...}
@VintfStability // required
parcelable Data {...}
इसके अलावा, VintfStability से एनोटेट किए गए टाइप को तय करने वाली AIDL फ़ाइलों को सिर्फ़ aidl_interface Soong मॉड्यूल टाइप का इस्तेमाल करके बनाया जा सकता है. साथ ही, stability प्रॉपर्टी को vintf पर सेट किया जा सकता है:
aidl_interface {
name: "my_interface",
srcs: [...],
stability: "vintf",
}
UnsupportedAppUsage
UnsupportedAppUsage एनोटेशन से पता चलता है कि एनोटेट किया गया AIDL टाइप, गैर-एसडीके इंटरफ़ेस का हिस्सा है. यह लेगसी ऐप्लिकेशन के लिए उपलब्ध है.
छुपे हुए एपीआई के बारे में ज़्यादा जानने के लिए, गैर-एसडीके इंटरफ़ेस पर पाबंदियां देखें.
UnsupportedAppUsage एनोटेशन से, जनरेट किए गए कोड के व्यवहार पर कोई असर नहीं पड़ता. यह एनोटेशन, जनरेट की गई Java क्लास को सिर्फ़ उसी नाम के Java एनोटेशन के साथ एनोटेट करता है:
// in AIDL
@UnsupportedAppUsage
interface IFoo {...}
// in Java
@android.compat.annotation.UnsupportedAppUsage
public interface IFoo {...}
यह गैर-Java बैकएंड के लिए कोई कार्रवाई नहीं करता है.
बैकअप एनोटेशन
Backing एनोटेशन, AIDL enum टाइप के स्टोरेज टाइप के बारे में बताता है:
@Backing(type="int")
enum Color { RED, BLUE, }
सीपीपी बैकएंड में, यह int32_t टाइप की C++ enum क्लास दिखाता है:
enum class Color : int32_t {
RED = 0,
BLUE = 1,
}
अगर एनोटेशन शामिल नहीं किया जाता है, तो type को byte माना जाता है. यह सीपीपी बैकएंड के लिए int8_t पर मैप करता है.
type आर्ग्युमेंट को सिर्फ़ इन इंटिग्रल टाइप पर सेट किया जा सकता है:
byte(8-बिट चौड़ा)int(32-बिट चौड़ा)long(64-बिट वाइड)
NdkOnlyStableParcelable
NdkOnlyStableParcelable पार्सल किए जा सकने वाले किसी एलान (डेफ़िनिशन नहीं) को स्टेबल के तौर पर मार्क करता है, ताकि इसे स्टेबल AIDL टाइप से रेफ़रंस किया जा सके. यह JavaOnlyStableParcelable की तरह है, लेकिन NdkOnlyStableParcelable, पार्सल किए जा सकने वाले एलान को Java के बजाय NDK बैकएंड के लिए स्टेबल के तौर पर मार्क करता है.
इस पार्सलेबल का इस्तेमाल करने के लिए:
- आपको
ndk_headerएट्रिब्यूट की वैल्यू देनी होगी. - आपके पास एक ऐसी NDK लाइब्रेरी होनी चाहिए जिसमें पार्सल किए जा सकने वाले ऑब्जेक्ट के बारे में बताया गया हो. साथ ही, लाइब्रेरी को लाइब्रेरी में कंपाइल किया जाना चाहिए. उदाहरण के लिए,
cc_*मॉड्यूल पर कोर बिल्ड सिस्टम में,static_libsयाshared_libsका इस्तेमाल करें.aidl_interfaceके लिए,Android.bpमेंadditional_shared_librariesमें जाकर लाइब्रेरी जोड़ें.
JavaOnlyStableParcelable
JavaOnlyStableParcelable पार्सल किए जा सकने वाले किसी एलान (डेफ़िनिशन नहीं) को स्टेबल के तौर पर मार्क करता है, ताकि इसे अन्य स्टेबल AIDL टाइप से रेफ़रंस किया जा सके.
स्टेबल एआईडीएल के लिए, यह ज़रूरी है कि उपयोगकर्ता की ओर से तय किए गए सभी टाइप स्टेबल हों. पार्सल किए जा सकने वाले ऑब्जेक्ट के लिए, यह ज़रूरी है कि उसके फ़ील्ड के बारे में AIDL सोर्स फ़ाइल में साफ़ तौर पर बताया गया हो:
parcelable Data { // Data is a structured parcelable.
int x;
int y;
}
parcelable AnotherData { // AnotherData is also a structured parcelable
Data d; // OK, because Data is a structured parcelable
}
अगर पार्सल किए जा सकने वाले ऑब्जेक्ट का स्ट्रक्चर नहीं बनाया गया है या सिर्फ़ उसे पार्सल किए जा सकने वाले ऑब्जेक्ट के तौर पर एलान किया गया है, तो उसे रेफ़रंस नहीं किया जा सकता:
parcelable Data; // Data is NOT a structured parcelable
parcelable AnotherData {
Data d; // Error
}
JavaOnlyStableParcelable की मदद से, जांच को तब बदला जा सकता है, जब Android SDK के हिस्से के तौर पर, पार्सल किए जा सकने वाले ऑब्जेक्ट को सुरक्षित तरीके से उपलब्ध कराया गया हो:
@JavaOnlyStableParcelable
parcelable Data;
parcelable AnotherData {
Data d; // OK
}
JavaDerive
JavaDerive Java बैकएंड में पार्सल किए जा सकने वाले टाइप के लिए, अपने-आप ये तरीके जनरेट करता है:
@JavaDerive(equals = true, toString = true)
parcelable Data {
int number;
String str;
}
एनोटेशन को यह कंट्रोल करने के लिए अतिरिक्त पैरामीटर की ज़रूरत होती है कि क्या जनरेट करना है. इन पैरामीटर का इस्तेमाल किया जा सकता है:
equals=true,equalsऔरhashCodeतरीके जनरेट करता है.toString=true,toStringतरीके को जनरेट करता है. यह टाइप और फ़ील्ड का नाम प्रिंट करता है. उदाहरण के लिए,Data{number: 42, str: foo}.
JavaDefault (अब सेवा में नहीं है)
JavaDefault को Android 13 में जोड़ा गया था. यह कंट्रोल करता है कि setDefaultImpl के लिए, डिफ़ॉल्ट तौर पर लागू किए गए वर्शन के साथ काम करने की सुविधा जनरेट की गई है या नहीं. जगह बचाने के लिए, अब यह सुविधा डिफ़ॉल्ट रूप से जनरेट नहीं होती.
JavaPassthrough
JavaPassthrough से जनरेट किए गए Java API को किसी भी Java एनोटेशन के साथ एनोटेट किया जा सकता है.
एआईडीएल में ये एनोटेशन:
@JavaPassthrough(annotation="@android.annotation.Alice")
@JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")
जनरेट किए गए Java कोड में, यह इस तरह दिखेगा:
@android.annotation.Alice
@com.android.Alice(arg=com.android.Alice.Value.A)
annotation पैरामीटर की वैल्यू सीधे तौर पर भेजी जाती है. AIDL कंपाइलर, पैरामीटर की वैल्यू की जांच नहीं करता. अगर Java-लेवल पर सिंटैक्स से जुड़ी कोई गड़बड़ी होती है, तो उसे AIDL कंपाइलर नहीं, बल्कि Java कंपाइलर पकड़ता है.
इस एनोटेशन को किसी भी AIDL इकाई से जोड़ा जा सकता है. यह एनोटेशन, Java के अलावा अन्य बैकएंड के लिए कोई कार्रवाई नहीं करता है.
RustDerive
RustDerive, जनरेट किए गए Rust टाइप के लिए अपने-आप ट्रेट लागू करता है.
एनोटेशन को यह कंट्रोल करने के लिए अतिरिक्त पैरामीटर की ज़रूरत होती है कि क्या जनरेट करना है. इन पैरामीटर का इस्तेमाल किया जा सकता है:
Copy=trueClone=trueOrd=truePartialOrd=trueEq=truePartialEq=trueHash=true
इन विशेषताओं के बारे में जानने के लिए, Rust का दस्तावेज़ देखें.
FixedSize
FixedSize किसी स्ट्रक्चर्ड पार्सलेबल को तय साइज़ के तौर पर मार्क करता है. मार्क करने के बाद, पार्सल किए जा सकने वाले ऑब्जेक्ट में नए फ़ील्ड नहीं जोड़े जा सकते. पार्सल किए जा सकने वाले ऑब्जेक्ट के सभी फ़ील्ड का साइज़ तय होना चाहिए. इनमें प्रिमिटिव टाइप, एनम, तय साइज़ वाले ऐरे, और FixedSize के तौर पर मार्क किए गए अन्य पार्सल किए जा सकने वाले ऑब्जेक्ट शामिल हैं.
FixedSize ऑब्जेक्ट के साइज़ और अलाइनमेंट, ndk बैकएंड में स्थिर होते हैं.
| प्रकार | साइज़ (बाइट में) | अलाइनमेंट (बाइट में) |
|---|---|---|
boolean |
1 |
1 |
byte |
1 |
1 |
char |
2 |
2 |
int |
4 |
4 |
long |
8 |
8 |
float |
4 |
4 |
double |
8 |
8 |
parcelable |
सभी फ़ील्ड का कुल साइज़ | सभी फ़ील्ड का सबसे बड़ा अलाइनमेंट |
union |
सभी फ़ील्ड का सबसे बड़ा साइज़ | सभी फ़ील्ड का सबसे बड़ा अलाइनमेंट |
enum |
बैकअप के टाइप का साइज़ | बैकअप के टाइप का अलाइनमेंट |
T[N] (तय किए गए साइज़ की ऐरे) |
T * N का साइज़ |
T का अलाइनमेंट |
String, IBinder, FileDescriptor, ParcelFileDescriptor |
N/A |
N/A |
ब्यौरा
Descriptor किसी इंटरफ़ेस के इंटरफ़ेस डिस्क्रिप्टर को ज़बरदस्ती तय करता है:
package android.foo;
@Descriptor(value="android.bar.IWorld")
interface IHello {...}
इस इंटरफ़ेस का डिस्क्रिप्टर android.bar.IWorld है. अगर Descriptor एनोटेशन मौजूद नहीं है, तो डिस्क्रिप्टर android.foo.IHello होगा.
यह सुविधा, पहले से पब्लिश किए गए इंटरफ़ेस का नाम बदलने के लिए काम की है. नाम बदले गए इंटरफ़ेस के डिस्क्रिप्टर को, नाम बदलने से पहले वाले इंटरफ़ेस के डिस्क्रिप्टर के जैसा ही रखने से, दोनों इंटरफ़ेस एक-दूसरे से कम्यूनिकेट कर पाते हैं.
@hide in comments
AIDL कंपाइलर, टिप्पणियों में मौजूद @hide को पहचानता है और इसे Java आउटपुट में पास करता है, ताकि metalava इसे पिकअप कर सके. इस टिप्पणी से यह पक्का करने में मदद मिलती है कि Android बिल्ड सिस्टम यह पहचान ले कि AIDL एपीआई, एसडीके एपीआई नहीं हैं.
टिप्पणियों में @deprecated टैग
एआईडीएल कंपाइलर, टिप्पणियों में @deprecated को ऐसे टैग के तौर पर पहचानता है जिसका इस्तेमाल अब नहीं किया जाना चाहिए:
interface IFoo {
/** @deprecated use bar() instead */
void foo();
void bar();
}
हर बैकएंड, बंद की गई इकाइयों को बैकएंड के हिसाब से एनोटेशन या एट्रिब्यूट के साथ मार्क करता है, ताकि क्लाइंट कोड को यह सूचना मिल जाए कि वह बंद की गई इकाइयों को रेफ़र कर रहा है. उदाहरण के लिए, @Deprecated एनोटेशन और @deprecated टैग, Java से जनरेट किए गए कोड से जुड़े होते हैं.