यहां बताए गए सबसे सही तरीके, एआईडीएल इंटरफ़ेस को असरदार तरीके से और इंटरफ़ेस के लचीलेपन पर ध्यान देकर बनाने के लिए गाइड के तौर पर काम करते हैं. खास तौर पर, जब एआईडीएल का इस्तेमाल किसी एपीआई को तय करने या एपीआई के प्लैटफ़ॉर्म के साथ इंटरैक्ट करने के लिए किया जाता है.
जब ऐप्लिकेशन को बैकग्राउंड प्रोसेस में एक-दूसरे के साथ इंटरफ़ेस करना हो या सिस्टम के साथ इंटरफ़ेस करना हो, तब एपीआई तय करने के लिए AIDL का इस्तेमाल किया जा सकता है. AIDL की मदद से ऐप्लिकेशन में प्रोग्रामिंग इंटरफ़ेस बनाने के बारे में ज़्यादा जानने के लिए, Android इंटरफ़ेस डेफ़िनिशन लैंग्वेज (AIDL) देखें. AIDL के इस्तेमाल के उदाहरणों के लिए, HALs के लिए AIDL और Stable AIDL देखें.
वर्शन
किसी AIDL API का हर पुराने वर्शन के साथ काम करने वाला स्नैपशॉट, किसी वर्शन से जुड़ा होता है.
स्नैपशॉट लेने के लिए, m <module-name>-freeze-api
चलाएं. जब भी एपीआई का कोई क्लाइंट या सर्वर रिलीज़ किया जाता है (उदाहरण के लिए, मेनलाइन ट्रेन में), तो आपको उसका स्नैपशॉट लेना होगा और नया वर्शन बनाना होगा. सिस्टम-टू-वेंडर एपीआई के लिए, यह हर साल प्लैटफ़ॉर्म में होने वाले बदलावों के साथ होना चाहिए.
अनुमति वाले बदलावों के बारे में ज़्यादा जानकारी के लिए, वर्शनिंग इंटरफ़ेस देखें.
एपीआई के डिज़ाइन से जुड़े दिशा-निर्देश
सामान्य सेटिंग
1. सब कुछ रिकॉर्ड करना
- हर तरीके के सेमेंटेक्स, आर्ग्युमेंट, पहले से मौजूद अपवादों के इस्तेमाल, सेवा के हिसाब से अपवादों, और रिटर्न वैल्यू के लिए दस्तावेज़ बनाएं.
- हर इंटरफ़ेस के सिमेंटिक के लिए दस्तावेज़ बनाएं.
- एनम और कॉन्स्टेंट के सेमेटिक मतलब को दस्तावेज़ में शामिल करें.
- ऐसे सभी कामों को दस्तावेज़ में शामिल करें जो लागू करने वाले व्यक्ति को समझ न आएं.
- जहां ज़रूरी हो वहां उदाहरण दें.
2. केसिंग
टाइप के लिए अपर कैमल केसिंग और तरीकों, फ़ील्ड, और आर्ग्युमेंट के लिए लोअर कैमल केसिंग का इस्तेमाल करें. उदाहरण के लिए, पार्सल किए जा सकने वाले टाइप के लिए MyParcelable
और आर्ग्युमेंट के लिए anArgument
. संक्षिप्त शब्दों के लिए, संक्षिप्त शब्द को एक शब्द मानें (NFC
-> Nfc
).
[-Wconst-name] Enum की वैल्यू और कॉन्स्टेंट, ENUM_VALUE
और
CONSTANT_NAME
होने चाहिए
इंटरफ़ेस
1. इन्हें
[-Winterface-name] इंटरफ़ेस का नाम I
से शुरू होना चाहिए, जैसे कि IFoo
.
2. आईडी पर आधारित "ऑब्जेक्ट" वाले बड़े इंटरफ़ेस का इस्तेमाल न करें
किसी खास एपीआई से जुड़े कई कॉल होने पर, सब-इंटरफ़ेस का इस्तेमाल करें. इससे ये फ़ायदे मिलते हैं:
- क्लाइंट या सर्वर कोड को आसानी से समझने में मदद करता है
- इससे ऑब्जेक्ट के लाइफ़साइकल को आसान बनाना
- बाइंडर में मौजूद जानकारी को बदला नहीं जा सकता.
इसका सुझाव नहीं दिया जाता: आईडी पर आधारित ऑब्जेक्ट वाला एक बड़ा इंटरफ़ेस
interface IManager {
int getFooId();
void beginFoo(int id); // clients in other processes can guess an ID
void opFoo(int id);
void recycleFoo(int id); // ownership not handled by type
}
इस तरह लिखें: अलग-अलग इंटरफ़ेस
interface IManager {
IFoo getFoo();
}
interface IFoo {
void begin(); // clients in other processes can't guess a binder
void op();
}
3. एकतरफ़ा और दोतरफ़ा तरीकों को एक साथ इस्तेमाल न करें
[-Wmixed-oneway] एकतरफ़ा तरीके को नॉन-वन-वे तरीकों के साथ न मिलाएं, क्योंकि इससे क्लाइंट और सर्वर के लिए, थ्रेडिंग मॉडल को समझना मुश्किल हो जाता है. खास तौर पर, किसी इंटरफ़ेस के क्लाइंट कोड को पढ़ते समय, आपको हर तरीके के लिए यह देखना होगा कि वह तरीका ब्लॉक करेगा या नहीं.
4. स्टेटस कोड दिखाने से बचना
मेथड में, रिटर्न वैल्यू के तौर पर स्टेटस कोड का इस्तेमाल नहीं किया जाना चाहिए. ऐसा इसलिए, क्योंकि सभी AIDL मेथड में, रिटर्न स्टेटस का एक कोड शामिल होता है. ServiceSpecificException
या
EX_SERVICE_SPECIFIC
देखें. आम तौर पर, इन वैल्यू को AIDL इंटरफ़ेस में स्थिरांक के तौर पर तय किया जाता है. ज़्यादा जानकारी के लिए, एआईडीएल बैकएंड के गड़बड़ी मैनेजमेंट सेक्शन पर जाएं.
5. आउटपुट पैरामीटर के तौर पर ऐरे को नुकसान पहुंचाने वाला माना जाता है
[-Wout-array] आम तौर पर, void foo(out String[] ret)
जैसे अरे आउटपुट पैरामीटर वाले तरीके खराब होते हैं, क्योंकि क्लाइंट को Java में अरे आउटपुट का साइज़ तय करना होता है और उसे ऐलोकेट करना होता है. इसलिए, सर्वर को अरे आउटपुट का साइज़ नहीं चुना जा सकता. यह गड़बड़ी इसलिए होती है, क्योंकि Java में ऐरे के काम करने का तरीका अलग होता है. इन ऐरे को फिर से असाइन नहीं किया जा सकता. इसके बजाय, String[] foo()
जैसे एपीआई का इस्तेमाल करें.
6. इनपुट और आउटपुट पैरामीटर इस्तेमाल करने से बचें
[-Winout-parameter] इससे क्लाइंट को भ्रम हो सकता है, क्योंकि in
पैरामीटर भी out
पैरामीटर जैसे दिखते हैं.
7. @nullable वाले ऐसे पैरामीटर इस्तेमाल न करें जो आउट और इनआउट पैरामीटर हों और ऐरे न हों
[-Wout-nullable] Java बैकएंड, @nullable
एनोटेशन को मैनेज नहीं करता, जबकि अन्य बैकएंड करते हैं. इसलिए, out/inout @nullable T
की वजह से सभी बैकएंड में अलग-अलग तरह का व्यवहार हो सकता है. उदाहरण के लिए, नॉन-Java बैकएंड, आउट @nullable
पैरामीटर को शून्य पर सेट कर सकते हैं (C++ में, इसे std::nullopt
के तौर पर सेट करना). हालांकि, Java क्लाइंट इसे शून्य के तौर पर नहीं पढ़ सकता.
स्ट्रक्चर्ड पार्सल
1. कब इस्तेमाल करें
स्ट्रक्चर्ड पार्सलबल का इस्तेमाल तब करें, जब आपको एक से ज़्यादा तरह का डेटा भेजना हो.
इसके अलावा, जब आपके पास एक ही डेटा टाइप हो, लेकिन आपको आने वाले समय में उसे बढ़ाना हो. उदाहरण के लिए, String username
का इस्तेमाल न करें. ऐसे पार्सल का इस्तेमाल करें जिसे बड़ा किया जा सकता हो. जैसे:
parcelable User {
String username;
}
आने वाले समय में, इसे इस तरह बढ़ाया जा सकता है:
parcelable User {
String username;
int id;
}
2. डिफ़ॉल्ट वैल्यू साफ़ तौर पर दें
[-Wexplicit-default, -Wenum-explicit-default] फ़ील्ड के लिए साफ़ तौर पर डिफ़ॉल्ट वैल्यू दें.
ऐसे पार्सल जिनका डेटा स्ट्रक्चर्ड नहीं है
1. कब इस्तेमाल करें
नॉन-स्ट्रक्चर्ड पार्सल करने लायक ऑब्जेक्ट, Java में @JavaOnlyStableParcelable
और NDK बैकएंड में @NdkOnlyStableParcelable
के साथ उपलब्ध हैं. आम तौर पर, ये पुराने और मौजूदा पार्सल करने लायक आइटम होते हैं, जिन्हें स्ट्रक्चर नहीं किया जा सकता.
कॉन्स्टेंट और एनम
1. बिटफ़ील्ड में कॉन्स्टेंट फ़ील्ड का इस्तेमाल किया जाना चाहिए
बिटफ़ील्ड में, कॉन्स्टेंट फ़ील्ड का इस्तेमाल किया जाना चाहिए. उदाहरण के लिए, इंटरफ़ेस में const int FOO = 3;
.
2. एनोम के लिए, क्लोज़्ड सेट इस्तेमाल किए जाने चाहिए.
एनोटेशन, क्लोज़्ड सेट होने चाहिए. ध्यान दें: सिर्फ़ इंटरफ़ेस का मालिक ही, सूची के एलिमेंट जोड़ सकता है. अगर वेंडर या OEM को इन फ़ील्ड को बड़ा करना है, तो इसके लिए किसी अन्य तरीके की ज़रूरत होगी. जब भी हो सके, तो वेंडर की अपस्ट्रीम करने की सुविधा को प्राथमिकता दी जानी चाहिए. हालांकि, कुछ मामलों में वेंडर की कस्टम वैल्यू को अनुमति दी जा सकती है. हालांकि, वेंडर के पास इसकी वैल्यू को बदलने का कोई तरीका होना चाहिए. शायद यह तरीका AIDL में ही मौजूद हो. वेंडर की वैल्यू एक-दूसरे से मेल नहीं खानी चाहिए और इन्हें तीसरे पक्ष के ऐप्लिकेशन को नहीं दिखाया जाना चाहिए.
3. "NUM_ELEMENTS" जैसी वैल्यू का इस्तेमाल न करें
वैल्यू की सूची के वर्शन होते हैं. इसलिए, ऐसी वैल्यू का इस्तेमाल नहीं किया जाना चाहिए जिनसे यह पता चलता हो कि कितनी वैल्यू मौजूद हैं. C++ में, enum_range<>
का इस्तेमाल करके इस समस्या को हल किया जा सकता है. Rust के लिए, enum_values()
का इस्तेमाल करें. Java में, अभी तक कोई समाधान नहीं है.
इसका सुझाव नहीं दिया जाता: नंबर वाली वैल्यू का इस्तेमाल करना
@Backing(type="int")
enum FruitType {
APPLE = 0,
BANANA = 1,
MANGO = 2,
NUM_TYPES, // BAD
}
4. ज़रूरत से ज़्यादा प्रीफ़िक्स और सफ़िक्स इस्तेमाल न करें
[-Wredundant-name] कॉन्स्टेंट और एनोमेरेटर में, ग़ैर-ज़रूरी या बार-बार इस्तेमाल होने वाले प्रीफ़िक्स और सफ़िक्स का इस्तेमाल न करें.
इसका सुझाव नहीं दिया जाता: ग़ैर-ज़रूरी प्रीफ़िक्स का इस्तेमाल करना
enum MyStatus {
STATUS_GOOD,
STATUS_BAD // BAD
}
सुझाया गया: सीधे तौर पर एनम को नाम देना
enum MyStatus {
GOOD,
BAD
}
FileDescriptor
[-Wfile-descriptor] FileDescriptor
को आर्ग्युमेंट के तौर पर इस्तेमाल करने या किसी AIDL इंटरफ़ेस के तरीके की रिटर्न वैल्यू के तौर पर इस्तेमाल करने का सुझाव नहीं दिया जाता. खास तौर पर, जब एआईडीएल को Java में लागू किया जाता है, तो इससे फ़ाइल डिस्क्रिप्टर लीक हो सकता है. हालांकि, ऐसा तब ही होगा, जब इसे ध्यान से मैनेज न किया जाए. आम तौर पर, अगर आपने कोई FileDescriptor
स्वीकार किया है, तो उसका इस्तेमाल न होने पर, आपको उसे मैन्युअल तरीके से बंद करना होगा.
नेटिव बैकएंड के लिए, आप सुरक्षित हैं, क्योंकि FileDescriptor
, unique_fd
पर मैप होता है, जो अपने-आप बंद हो जाता है. हालांकि, बैकएंड की भाषा चाहे जो भी हो, FileDescriptor
का इस्तेमाल बिलकुल न करें. ऐसा करने से, आने वाले समय में बैकएंड की भाषा बदलने में समस्या आ सकती है.
इसके बजाय, ParcelFileDescriptor
का इस्तेमाल करें, जो अपने-आप बंद हो जाता है.
वैरिएबल यूनिट
पक्का करें कि वैरिएबल की इकाइयां नाम में शामिल हों, ताकि उनकी इकाइयां अच्छी तरह से तय की जा सकें और दस्तावेज़ों का रेफ़रंस दिए बिना उन्हें समझा जा सके
उदाहरण
long duration; // Bad
long durationNsec; // Good
long durationNanos; // Also good
double energy; // Bad
double energyMilliJoules; // Good
int frequency; // Bad
int frequencyHz; // Good
टाइमस्टैंप में उनका रेफ़रंस होना चाहिए
टाइमस्टैंप (वास्तव में, सभी यूनिट!) में उनकी यूनिट और रेफ़रंस पॉइंट साफ़ तौर पर दिखने चाहिए.
उदाहरण
/**
* Time since device boot in milliseconds
*/
long timestampMs;
/**
* UTC time received from the NTP server in units of milliseconds
* since January 1, 1970
*/
long utcTimeMs;