एआईडीएल स्टाइल गाइड

यहां बताए गए सबसे सही तरीके, AIDL इंटरफ़ेस को असरदार तरीके से डेवलप करने के लिए एक गाइड के तौर पर काम करते हैं. साथ ही, ये इंटरफ़ेस को ज़्यादा से ज़्यादा लचीला बनाने में मदद करते हैं. खास तौर पर, तब जब AIDL का इस्तेमाल किसी एपीआई को तय करने या एपीआई के साथ इंटरैक्ट करने के लिए किया जाता है.

जब ऐप्लिकेशन को बैकग्राउंड प्रोसेस में एक-दूसरे के साथ इंटरफ़ेस करने या सिस्टम के साथ इंटरफ़ेस करने की ज़रूरत होती है, तब एपीआई को तय करने के लिए AIDL का इस्तेमाल किया जा सकता है. एआईडीएल की मदद से ऐप्लिकेशन में प्रोग्रामिंग इंटरफ़ेस डेवलप करने के बारे में ज़्यादा जानने के लिए, Android इंटरफ़ेस डेफ़िनिशन लैंग्वेज (एआईडीएल) लेख पढ़ें. एआईडीएल के इस्तेमाल के उदाहरणों के लिए, एचएएल के लिए एआईडीएल और स्टेबल एआईडीएल देखें.

वर्शन

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 इंटरफ़ेस में कॉन्स्टेंट के तौर पर तय किया जाता है. ज़्यादा जानकारी के लिए, AIDL बैकएंड के गड़बड़ी हैंडलिंग सेक्शन पर जाएं.

5. आउटपुट पैरामीटर के तौर पर इस्तेमाल किए गए ऐरे को नुकसान पहुंचाने वाला माना गया

[-Wout-array] ऐरे आउटपुट पैरामीटर वाले तरीके, जैसे कि void foo(out String[] ret) आम तौर पर सही नहीं होते, क्योंकि आउटपुट ऐरे का साइज़, क्लाइंट को Java में तय करना होता है. इसलिए, सर्वर ऐरे आउटपुट का साइज़ नहीं चुन सकता. ऐसा इसलिए होता है, क्योंकि Java में ऐरे इस तरह से काम करते हैं कि उन्हें फिर से असाइन नहीं किया जा सकता. इसके बजाय, String[] foo() जैसे एपीआई का इस्तेमाल करें.

6. इनपुट और आउटपुट पैरामीटर का इस्तेमाल न करें

[-Winout-parameter] इससे क्लाइंट भ्रमित हो सकते हैं, क्योंकि in पैरामीटर भी out पैरामीटर की तरह दिखते हैं.

7. @nullable नॉन-ऐरे पैरामीटर के लिए, out और inout का इस्तेमाल न करें

[-Wout-nullable] Java बैकएंड, @nullable एनोटेशन को हैंडल नहीं करता है, जबकि अन्य बैकएंड ऐसा करते हैं. इसलिए, out/inout @nullable T की वजह से अलग-अलग बैकएंड में अलग-अलग समस्याएं हो सकती हैं. उदाहरण के लिए, Java के अलावा अन्य बैकएंड, out @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 के साथ उपलब्ध हैं. आम तौर पर, ये पुराने और मौजूदा पार्सल होते हैं. इन्हें स्ट्रक्चर नहीं किया जा सकता.

कॉन्स्टेंट और enum

1. बिटफ़ील्ड में कॉन्स्टेंट फ़ील्ड का इस्तेमाल किया जाना चाहिए

बिटफ़ील्ड को कॉन्स्टेंट फ़ील्ड का इस्तेमाल करना चाहिए. उदाहरण के लिए, इंटरफ़ेस में const int FOO = 3;.

2. एनम, क्लोज़्ड सेट होने चाहिए.

एनम, क्लोज़्ड सेट होने चाहिए. ध्यान दें: सिर्फ़ इंटरफ़ेस का मालिक, enum एलिमेंट जोड़ सकता है. अगर वेंडर या 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] Avoid redundant or repetitive prefixes and suffixes in constants and enumerators.

सुझाया नहीं गया: एक से ज़्यादा बार इस्तेमाल किए गए प्रीफ़िक्स का इस्तेमाल करना

enum MyStatus {
    STATUS_GOOD,
    STATUS_BAD // BAD
}

सुझाया गया: सीधे तौर पर enum का नाम देना

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;