Android API के लिए दिशा-निर्देश

यह पेज, डेवलपर के लिए एक गाइड है. इससे उन्हें उन सामान्य सिद्धांतों के बारे में पता चलेगा जिन्हें एपीआई काउंसिल, एपीआई की समीक्षाओं में लागू करती है.

एपीआई लिखते समय इन दिशा-निर्देशों का पालन करने के साथ-साथ, डेवलपर को एपीआई लिंट टूल का इस्तेमाल करना चाहिए. यह टूल, इनमें से कई नियमों को ऐसे चेक में कोड करता है जिन्हें वह एपीआई के ख़िलाफ़ चलाता है.

इसे Lint टूल के नियमों के लिए गाइड के तौर पर समझें. साथ ही, इसमें उन नियमों के बारे में सामान्य सलाह भी दी गई है जिन्हें ज़्यादा सटीक तरीके से उस टूल में कोड नहीं किया जा सकता.

एपीआई लिंट टूल

API Lint को Metalava स्टैटिक विश्लेषण टूल में इंटिग्रेट किया गया है. यह CI में पुष्टि के दौरान अपने-आप चलता है. इसे मैन्युअल तरीके से, लोकल प्लैटफ़ॉर्म चेकआउट से m checkapi का इस्तेमाल करके या लोकल AndroidX चेकआउट से ./gradlew :path:to:project:checkApi का इस्तेमाल करके चलाया जा सकता है.

एपीआई के नियम

Android प्लैटफ़ॉर्म और कई Jetpack लाइब्रेरी, दिशा-निर्देशों का यह सेट बनाए जाने से पहले ही मौजूद थीं. साथ ही, इस पेज पर बाद में बताई गई नीतियां, Android के इकोसिस्टम की ज़रूरतों को पूरा करने के लिए लगातार बेहतर होती जा रही हैं.

इस वजह से, हो सकता है कि कुछ मौजूदा एपीआई, दिशा-निर्देशों का पालन न करें. अन्य मामलों में, अगर कोई नया एपीआई, दिशा-निर्देशों का सख्ती से पालन करने के बजाय मौजूदा एपीआई के साथ काम करता है, तो इससे ऐप्लिकेशन डेवलपर को बेहतर उपयोगकर्ता अनुभव मिल सकता है.

अपने विवेक का इस्तेमाल करें. अगर एपीआई से जुड़े ऐसे मुश्किल सवाल हैं जिनका जवाब देना है या ऐसे दिशा-निर्देश हैं जिन्हें अपडेट करना है, तो एपीआई काउंसिल से संपर्क करें.

एपीआई के बारे में बुनियादी जानकारी

यह कैटगरी, Android API के मुख्य पहलुओं से जुड़ी है.

सभी एपीआई लागू किए जाने चाहिए

एपीआई की ऑडियंस (उदाहरण के लिए, सार्वजनिक या @SystemApi) से कोई फ़र्क़ नहीं पड़ता. जब एपीआई को मर्ज किया जाता है या एपीआई के तौर पर दिखाया जाता है, तब सभी एपीआई सरफ़ेस लागू किए जाने चाहिए. एपीआई स्टब को लागू करने के साथ मर्ज न करें.

एपीआई के ऐसे प्लैटफ़ॉर्म जिनमें एपीआई लागू नहीं किया गया है उनमें कई समस्याएं होती हैं:

  • इस बात की कोई गारंटी नहीं है कि पूरी या सही सतह दिख रही है. जब तक क्लाइंट किसी एपीआई की जांच नहीं कर लेते या उसका इस्तेमाल नहीं कर लेते, तब तक यह पुष्टि नहीं की जा सकती कि उनके पास इस सुविधा का इस्तेमाल करने के लिए सही एपीआई हैं.
  • जिन एपीआई को लागू नहीं किया गया है उन्हें डेवलपर प्रीव्यू में टेस्ट नहीं किया जा सकता.
  • जिन एपीआई को लागू नहीं किया गया है उनकी जांच, सीटीएस में नहीं की जा सकती.

सभी एपीआई की जांच की जानी चाहिए

यह प्लैटफ़ॉर्म की सीटीएस से जुड़ी ज़रूरी शर्तों, AndroidX की नीतियों, और इस सिद्धांत के मुताबिक है कि एपीआई लागू किए जाने चाहिए.

एपीआई की जांच करने से यह पक्का किया जा सकता है कि एपीआई का इस्तेमाल किया जा सकता है. साथ ही, हमने इस्तेमाल के अनुमानित उदाहरणों को हल कर लिया है. एपीआई के मौजूद होने की जांच करना काफ़ी नहीं है. एपीआई के काम करने के तरीके की भी जांच की जानी चाहिए.

अगर किसी बदलाव से नया एपीआई जोड़ा जाता है, तो उसी सीएल या Gerrit विषय में उससे जुड़ी जांच शामिल होनी चाहिए.

एपीआई की जांच भी की जा सकती हो. आपके पास इस सवाल का जवाब होना चाहिए, "ऐप्लिकेशन डेवलपर, आपके एपीआई का इस्तेमाल करने वाले कोड की जांच कैसे करेगा?"

सभी एपीआई के बारे में जानकारी दी जानी चाहिए

एपीआई को इस्तेमाल करने के लिए, दस्तावेज़ एक अहम हिस्सा है. एपीआई सर्फ़ेस का सिंटैक्स भले ही आसान लगे, लेकिन नए क्लाइंट को एपीआई के पीछे के सिमैंटिक, व्यवहार या कॉन्टेक्स्ट के बारे में पता नहीं होगा.

जनरेट किए गए सभी एपीआई, दिशा-निर्देशों के मुताबिक होने चाहिए

टूल से जनरेट किए गए एपीआई को, हाथ से लिखे गए कोड के लिए बने एपीआई के दिशा-निर्देशों का पालन करना होगा.

एपीआई जनरेट करने के लिए, इन टूल का इस्तेमाल न करने का सुझाव दिया जाता है:

  • AutoValue: कई तरह से दिशा-निर्देशों का उल्लंघन करता है. उदाहरण के लिए, AutoValue के काम करने के तरीके से, फ़ाइनल वैल्यू क्लास और फ़ाइनल बिल्डर को लागू करने का कोई तरीका नहीं है.

कोड स्टाइल

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

जहां बताया गया है वहां को छोड़कर, कोडिंग के स्टैंडर्ड नियमों का पालन करें

Android की कोडिंग से जुड़ी शर्तों के बारे में बाहरी योगदानकर्ताओं के लिए यहां बताया गया है:

https://source.android.com/source/code-style.html

कुल मिलाकर, हम Java और Kotlin के कोडिंग के स्टैंडर्ड नियमों का पालन करते हैं.

तरीके के नामों में शॉर्ट फ़ॉर्म वाले शब्दों के सभी अक्षरों को कैपिटल लेटर में नहीं लिखा जाना चाहिए

उदाहरण के लिए: तरीके का नाम runCtsTests होना चाहिए, न कि runCTSTests.

नामों के आखिर में Impl नहीं होना चाहिए

इससे लागू करने से जुड़ी जानकारी का पता चलता है. इसलिए, ऐसा न करें.

कक्षाएं

इस सेक्शन में, क्लास, इंटरफ़ेस, और इनहेरिटेंस से जुड़े नियमों के बारे में बताया गया है.

सही बेस क्लास से नई सार्वजनिक क्लास इनहेरिट करना

इनहेरिटेंस से, आपकी सबक्लास में ऐसे एपीआई एलिमेंट दिखते हैं जो शायद सही न हों. उदाहरण के लिए, FrameLayout की नई सार्वजनिक सबक्लास, FrameLayout की तरह दिखती है. इसमें नए व्यवहार और एपीआई एलिमेंट भी शामिल होते हैं. अगर इनहेरिट किया गया एपीआई, आपके इस्तेमाल के उदाहरण के लिए सही नहीं है, तो ट्री में ऊपर की ओर मौजूद किसी क्लास से इनहेरिट करें. उदाहरण के लिए, ViewGroup या View.

अगर आपको बेस क्लास के तरीकों को ओवरराइड करके UnsupportedOperationException थ्रो करने का मन कर रहा है, तो इस बारे में फिर से सोचें कि कौनसी बेस क्लास का इस्तेमाल किया जा रहा है.

बेस कलेक्शन क्लास का इस्तेमाल करना

किसी कलेक्शन को आर्ग्युमेंट के तौर पर इस्तेमाल करते समय या उसे वैल्यू के तौर पर वापस लाते समय, हमेशा खास तौर पर लागू होने वाली क्लास के बजाय बेस क्लास का इस्तेमाल करें. जैसे, List<Foo> के बजाय ArrayList<Foo> को वापस लाएं.

ऐसी बेस क्लास का इस्तेमाल करें जो एपीआई के लिए सही कंस्ट्रेंट (पाबंदी) तय करती हो. उदाहरण के लिए, जिस एपीआई के कलेक्शन को क्रम से लगाना ज़रूरी है उसके लिए List का इस्तेमाल करें. साथ ही, जिस एपीआई के कलेक्शन में यूनीक एलिमेंट शामिल होने चाहिए उसके लिए Set का इस्तेमाल करें.

Kotlin में, बदले न जा सकने वाले कलेक्शन का इस्तेमाल करें. ज़्यादा जानकारी के लिए, कलेक्शन में बदलाव करने की सुविधा देखें.

ऐब्स्ट्रैक्ट क्लास बनाम इंटरफ़ेस

Java 8 में, डिफ़ॉल्ट इंटरफ़ेस के तरीकों के लिए सहायता जोड़ी गई है. इससे एपीआई डिज़ाइनर, बाइनरी फ़ाइल के साथ काम करने की सुविधा को बनाए रखते हुए इंटरफ़ेस में तरीके जोड़ सकते हैं. प्लैटफ़ॉर्म कोड और सभी Jetpack लाइब्रेरी को Java 8 या इसके बाद के वर्शन को टारगेट करना चाहिए.

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

जिन मामलों में डिफ़ॉल्ट लागू करने के लिए, कंस्ट्रक्टर या इंटरनल स्टेट की ज़रूरत होती है उनमें ऐब्स्ट्रैक्ट क्लास का इस्तेमाल ज़रूरी है.

दोनों ही मामलों में, एपीआई डिज़ाइनर, किसी एक तरीके को ऐब्स्ट्रैक्ट छोड़ सकते हैं, ताकि लैम्डा के तौर पर इसका इस्तेमाल करना आसान हो:

public interface AnimationEndCallback {
  // Always called, must be implemented.
  public void onFinished(Animation anim);
  // Optional callbacks.
  public default void onStopped(Animation anim) { }
  public default void onCanceled(Animation anim) { }
}

क्लास के नाम से यह पता चलना चाहिए कि वे किस क्लास से एक्सटेंड हो रही हैं

उदाहरण के लिए, Service को बढ़ाने वाली क्लास का नाम FooService होना चाहिए, ताकि यह साफ़ तौर पर समझ में आ सके:

public class IntentHelper extends Service {}
public class IntentService extends Service {}

सामान्य प्रत्यय

उपयोगी तरीकों के कलेक्शन के लिए, Helper और Util जैसे सामान्य क्लास नेम सफ़िक्स का इस्तेमाल न करें. इसके बजाय, तरीकों को सीधे तौर पर उनसे जुड़ी क्लास में या Kotlin एक्सटेंशन फ़ंक्शन में रखें.

अगर कोई तरीका कई क्लास को ब्रिज कर रहा है, तो उस क्लास को ऐसा नाम दें जिससे पता चले कि वह क्या करती है.

बहुत कम मामलों में, Helper सफ़िक्स का इस्तेमाल करना सही हो सकता है:

  • इस कुकी का इस्तेमाल, डिफ़ॉल्ट व्यवहार को कंपोज़ करने के लिए किया जाता है
  • इसमें मौजूदा क्लास के व्यवहार को नई क्लास में डेलिगेट करना शामिल हो सकता है
  • इसके लिए, सेव की गई स्थिति की ज़रूरत पड़ सकती है
  • आम तौर पर View

उदाहरण के लिए, अगर टूलटिप को बैकपोर्ट करने के लिए, View से जुड़ी स्थिति को बनाए रखने और बैकपोर्ट इंस्टॉल करने के लिए View पर कई तरीकों को कॉल करने की ज़रूरत होती है, तो TooltipHelper क्लास का नाम स्वीकार किया जाएगा.

आईडीएल से जनरेट किए गए कोड को सीधे तौर पर सार्वजनिक एपीआई के तौर पर उपलब्ध न कराएं

आईडीएल से जनरेट किए गए कोड को लागू करने की जानकारी के तौर पर सेव करें. इसमें protobuf, सॉकेट, FlatBuffers या Java और NDK के अलावा कोई अन्य एपीआई शामिल है. हालांकि, Android में ज़्यादातर आईडीएल, AIDL में होता है. इसलिए, इस पेज पर AIDL के बारे में बताया गया है.

जनरेट की गई AIDL क्लास, एपीआई स्टाइल गाइड की ज़रूरी शर्तों को पूरा नहीं करती हैं. उदाहरण के लिए, वे ओवरलोडिंग का इस्तेमाल नहीं कर सकतीं. साथ ही, AIDL टूल को भाषा के एपीआई के साथ काम करने के लिए डिज़ाइन नहीं किया गया है. इसलिए, उन्हें सार्वजनिक एपीआई में एम्बेड नहीं किया जा सकता.

इसके बजाय, AIDL इंटरफ़ेस के ऊपर एक सार्वजनिक एपीआई लेयर जोड़ें. भले ही, यह शुरू में एक शैलो रैपर हो.

Binder इंटरफ़ेस

अगर Binder इंटरफ़ेस, लागू करने से जुड़ी जानकारी है, तो इसे आने वाले समय में कभी भी बदला जा सकता है. साथ ही, सार्वजनिक लेयर की मदद से, ज़रूरी बैकवर्ड कंपैटिबिलिटी को बनाए रखा जा सकता है. उदाहरण के लिए, आपको इंटरनल कॉल में नए तर्क जोड़ने पड़ सकते हैं या शेयर की गई मेमोरी का इस्तेमाल करके, बैचिंग या स्ट्रीमिंग का इस्तेमाल करके, या इसी तरह के अन्य तरीकों से आईपीसी ट्रैफ़िक को ऑप्टिमाइज़ करना पड़ सकता है. अगर आपका AIDL इंटरफ़ेस भी सार्वजनिक एपीआई है, तो इनमें से कोई भी काम नहीं किया जा सकता.

उदाहरण के लिए, FooService को सीधे तौर पर सार्वजनिक एपीआई के तौर पर उपलब्ध न कराएं:

// BAD: Public API generated from IFooService.aidl
public class IFooService {
   public void doFoo(String foo);
}

इसके बजाय, Binder इंटरफ़ेस को मैनेजर या किसी अन्य क्लास में रैप करें:

/**
 * @hide
 */
public class IFooService {
   public void doFoo(String foo);
}

public IFooManager {
   public void doFoo(String foo) {
      mFooService.doFoo(foo);
   }
}

अगर बाद में इस कॉल के लिए किसी नए तर्क की ज़रूरत पड़ती है, तो इंटरनल इंटरफ़ेस को कम से कम और सुविधाजनक बनाया जा सकता है. साथ ही, सार्वजनिक एपीआई में ओवरलोड जोड़े जा सकते हैं. इस्तेमाल में बदलाव होने पर, रैपिंग लेयर का इस्तेमाल करके, पुराने सिस्टम के साथ काम करने से जुड़ी अन्य समस्याओं को हल किया जा सकता है:

/**
 * @hide
 */
public class IFooService {
   public void doFoo(String foo, int flags);
}

public IFooManager {
   public void doFoo(String foo) {
      if (mAppTargetSdkLevel < 26) {
         useOldFooLogic(); // Apps targeting API before 26 are broken otherwise
         mFooService.doFoo(foo, FLAG_THAT_ONE_WEIRD_HACK);
      } else {
         mFooService.doFoo(foo, 0);
      }
   }

   public void doFoo(String foo, int flags) {
      mFooService.doFoo(foo, flags);
   }
}

Android प्लैटफ़ॉर्म का हिस्सा न होने वाले Binder इंटरफ़ेस (उदाहरण के लिए, Google Play services से एक्सपोर्ट किया गया ऐसा सर्विस इंटरफ़ेस जिसे ऐप्लिकेशन इस्तेमाल कर सकते हैं) के लिए, स्टेबल, पब्लिश किए गए, और वर्शन वाले आईपीसी इंटरफ़ेस की ज़रूरत होती है. इसका मतलब है कि इंटरफ़ेस को बेहतर बनाना ज़्यादा मुश्किल होता है. हालांकि, अन्य एपीआई के दिशा-निर्देशों का पालन करने और आईपीसी इंटरफ़ेस के नए वर्शन के लिए, एक ही सार्वजनिक एपीआई का इस्तेमाल करना आसान बनाने के लिए, इसके चारों ओर एक रैपर लेयर रखना अब भी फ़ायदेमंद है. ऐसा तब किया जा सकता है, जब इसकी ज़रूरत हो.

सार्वजनिक एपीआई में रॉ बाइंडर ऑब्जेक्ट का इस्तेमाल न करें

Binder ऑब्जेक्ट का कोई मतलब नहीं होता. इसलिए, इसका इस्तेमाल सार्वजनिक एपीआई में नहीं किया जाना चाहिए. Binder या IBinder को टोकन के तौर पर इस्तेमाल करना एक सामान्य तरीका है, क्योंकि इसमें आइडेंटिटी सिमैंटिक होता है. रॉ Binder ऑब्जेक्ट का इस्तेमाल करने के बजाय, रैपर टोकन क्लास का इस्तेमाल करें.

public final class IdentifiableObject {
  public Binder getToken() {...}
}
public final class IdentifiableObjectToken {
  /**
   * @hide
   */
  public Binder getRawValue() {...}

  /**
   * @hide
   */
  public static IdentifiableObjectToken wrapToken(Binder rawValue) {...}
}

public final class IdentifiableObject {
  public IdentifiableObjectToken getToken() {...}
}

मैनेजर क्लास फ़ाइनल होनी चाहिए

मैनेजर क्लास को final के तौर पर तय किया जाना चाहिए. मैनेजर क्लास, सिस्टम सेवाओं से इंटरैक्ट करती हैं और इंटरैक्शन का एक ही पॉइंट होती हैं. इसमें बदलाव करने की ज़रूरत नहीं है, इसलिए इसे final के तौर पर सेट करें.

CompletableFuture या Future का इस्तेमाल न करें

java.util.concurrent.CompletableFuture में एक बड़ा एपीआई सर्फ़ेस होता है, जो आने वाले समय की वैल्यू में मनमुताबिक बदलाव करने की अनुमति देता है. साथ ही, इसमें डिफ़ॉल्ट रूप से ऐसी वैल्यू होती हैं जिनमें गड़बड़ी होने की आशंका होती है.

इसके उलट, java.util.concurrent.Future में नॉनब्लॉकिंग लिसनिंग की सुविधा नहीं है. इसलिए, इसे एसिंक्रोनस कोड के साथ इस्तेमाल करना मुश्किल होता है.

प्लैटफ़ॉर्म कोड और लो-लेवल लाइब्रेरी एपीआई, Kotlin और Java, दोनों के साथ काम करते हैं. इनमें कंप्लीशन कॉलबैक, Executor, और अगर एपीआई कैंसल करने की सुविधा के साथ काम करता है, तो CancellationSignal का इस्तेमाल करें.

public void asyncLoadFoo(android.os.CancellationSignal cancellationSignal,
    Executor callbackExecutor,
    android.os.OutcomeReceiver<FooResult, Throwable> callback);

अगर आपको Kotlin को टारगेट करना है, तो suspend फ़ंक्शन का इस्तेमाल करें.

suspend fun asyncLoadFoo(): Foo

Java के लिए इंटिग्रेशन लाइब्रेरी में, Guava's ListenableFuture का इस्तेमाल किया जा सकता है.

public com.google.common.util.concurrent.ListenableFuture<Foo> asyncLoadFoo();

'ज़रूरी नहीं' विकल्प का इस्तेमाल न करें

Optional से कुछ एपीआई प्लैटफ़ॉर्म पर फ़ायदे मिल सकते हैं. हालांकि, यह मौजूदा Android एपीआई प्लैटफ़ॉर्म के साथ काम नहीं करता. @Nullable और @NonNull, null सुरक्षा के लिए टूलिंग सहायता उपलब्ध कराते हैं. साथ ही, Kotlin कंपाइलर लेवल पर नल होने की स्थिति से जुड़े अनुबंधों को लागू करता है. इससे Optional की ज़रूरत नहीं पड़ती.

ज़रूरी नहीं कि प्रिमिटिव के लिए, has और get तरीकों का इस्तेमाल किया जाए. अगर वैल्यू सेट नहीं है (has, false दिखाता है), तो get तरीके को IllegalStateException दिखाना चाहिए.

public boolean hasAzimuth() { ... }
public int getAzimuth() {
  if (!hasAzimuth()) {
    throw new IllegalStateException("azimuth is not set");
  }
  return azimuth;
}

जिन क्लास को इंस्टैंशिएट नहीं किया जा सकता उनके लिए प्राइवेट कंस्ट्रक्टर का इस्तेमाल करना

सिर्फ़ Builder से बनाई जा सकने वाली क्लास, सिर्फ़ कॉन्स्टेंट या स्टैटिक तरीके वाली क्लास या अन्य ऐसी क्लास जिन्हें इंस्टैंटिएट नहीं किया जा सकता उनमें कम से कम एक प्राइवेट कंस्ट्रक्टर होना चाहिए. इससे डिफ़ॉल्ट नो-आर्ग कंस्ट्रक्टर का इस्तेमाल करके इंस्टैंटिएट करने से रोका जा सकेगा.

public final class Log {
  // Not instantiable.
  private Log() {}
}

सिंगलटन

सिंगलटन का इस्तेमाल न करने की सलाह दी जाती है, क्योंकि टेस्टिंग से जुड़ी ये समस्याएं आती हैं:

  1. क्लास के ज़रिए कंस्ट्रक्शन को मैनेज किया जाता है, ताकि फ़ेक का इस्तेमाल न किया जा सके
  2. सिंगलटन के स्टैटिक नेचर की वजह से, टेस्ट हर्मेटिक नहीं हो सकते
  3. इन समस्याओं को हल करने के लिए, डेवलपर को सिंगलटन की अंदरूनी जानकारी होनी चाहिए या उसके चारों ओर एक रैपर बनाना चाहिए

सिंगल इंस्टेंस पैटर्न का इस्तेमाल करें. यह पैटर्न, इन समस्याओं को हल करने के लिए ऐब्स्ट्रैक्ट बेस क्लास पर निर्भर करता है.

सिंगल इंस्टेंस

सिंगल इंस्टेंस क्लास, private या internal कंस्ट्रक्टर के साथ ऐब्स्ट्रैक्ट बेस क्लास का इस्तेमाल करती हैं. साथ ही, इंस्टेंस पाने के लिए स्टैटिक getInstance() तरीका उपलब्ध कराती हैं. getInstance() वाले तरीके को, बाद के कॉल पर एक ही ऑब्जेक्ट दिखाना ज़रूरी है.

getInstance() से मिला ऑब्जेक्ट, ऐब्स्ट्रैक्ट बेस क्लास का निजी तौर पर लागू किया गया ऑब्जेक्ट होना चाहिए.

class Singleton private constructor(...) {
  companion object {
    private val _instance: Singleton by lazy { Singleton(...) }

    fun getInstance(): Singleton {
      return _instance
    }
  }
}
abstract class SingleInstance private constructor(...) {
  companion object {
    private val _instance: SingleInstance by lazy { SingleInstanceImp(...) }
    fun getInstance(): SingleInstance {
      return _instance
    }
  }
}

सिंगल इंस्टेंस, सिंगलटन से इस मामले में अलग होता है कि डेवलपर, SingleInstance का फ़र्ज़ी वर्शन बना सकते हैं. साथ ही, रैपर बनाने की ज़रूरत के बिना, लागू करने की प्रोसेस को मैनेज करने के लिए, अपने खुद के डिपेंडेंसी इंजेक्शन फ़्रेमवर्क का इस्तेमाल कर सकते हैं. इसके अलावा, लाइब्रेरी -testing आर्टफ़ैक्ट में अपना फ़र्ज़ी वर्शन उपलब्ध करा सकती है.

जिन क्लास को संसाधन रिलीज़ करने हैं उन्हें AutoCloseable को लागू करना चाहिए

close, release, destroy या इसी तरह के अन्य तरीकों से रिसॉर्स रिलीज़ करने वाली क्लास को java.lang.AutoCloseable लागू करना चाहिए, ताकि डेवलपर try-with-resources ब्लॉक का इस्तेमाल करते समय इन रिसॉर्स को अपने-आप मिटा सकें.

android.* में नई View सबक्लास न बनाएं.

प्लैटफ़ॉर्म के पब्लिक एपीआई (यानी कि android.*) में ऐसी नई क्लास शामिल न करें जो सीधे या परोक्ष रूप से android.view.View से इनहेरिट होती हैं.

Android का यूज़र इंटरफ़ेस (यूआई) टूलकिट अब Compose-first है. प्लैटफ़ॉर्म पर उपलब्ध नई यूज़र इंटरफ़ेस (यूआई) सुविधाओं को लोअर-लेवल एपीआई के तौर पर उपलब्ध कराया जाना चाहिए. इनका इस्तेमाल, Jetpack Compose को लागू करने के लिए किया जा सकता है. साथ ही, Jetpack लाइब्रेरी में डेवलपर के लिए, व्यू पर आधारित यूआई कॉम्पोनेंट को लागू करने के लिए भी इनका इस्तेमाल किया जा सकता है. लाइब्रेरी में इन कॉम्पोनेंट को उपलब्ध कराने से, प्लैटफ़ॉर्म की सुविधाएं उपलब्ध न होने पर, बैकपोर्ट किए गए वर्शन को लागू करने के अवसर मिलते हैं.

फ़ील्ड्स की फ़िल्में

ये नियम, क्लास के सार्वजनिक फ़ील्ड के बारे में हैं.

रॉ फ़ील्ड को सार्वजनिक न करें

Java क्लास को फ़ील्ड सीधे तौर पर नहीं दिखाने चाहिए. फ़ील्ड निजी होने चाहिए और इन्हें सिर्फ़ सार्वजनिक गेटर और सेटर का इस्तेमाल करके ऐक्सेस किया जाना चाहिए. भले ही, ये फ़ील्ड फ़ाइनल हों या न हों.

कुछ मामलों में, बुनियादी डेटा स्ट्रक्चर में फ़ील्ड को तय करने या फ़ील्ड को वापस पाने के तरीके को बेहतर बनाने की ज़रूरत नहीं होती. ऐसे मामलों में, फ़ील्ड के नाम, वैरिएबल का नाम रखने से जुड़े स्टैंडर्ड नियमों के मुताबिक होने चाहिए. उदाहरण के लिए, Point.x और Point.y.

Kotlin क्लास, प्रॉपर्टी को ऐक्सेस करने की अनुमति दे सकती हैं.

एक्सपोज़ किए गए फ़ील्ड को फ़ाइनल के तौर पर मार्क किया जाना चाहिए

कच्चे फ़ील्ड का इस्तेमाल करने से बचने का सुझाव दिया जाता है (@see Don't expose raw fields). हालांकि, अगर कोई फ़ील्ड सार्वजनिक फ़ील्ड के तौर पर दिखता है, तो उसे final के तौर पर मार्क करें.

इंटरनल फ़ील्ड को सार्वजनिक नहीं किया जाना चाहिए

सार्वजनिक एपीआई में, इंटरनल फ़ील्ड के नामों का रेफ़रंस न दें.

public int mFlags;

सुरक्षित के बजाय सार्वजनिक का इस्तेमाल करें

@see Use public instead of protected

कॉन्स्टेंट

ये सार्वजनिक कॉन्स्टेंट के बारे में नियम हैं.

फ़्लैग कॉन्स्टेंट, int या long वैल्यू से ओवरलैप नहीं होने चाहिए

फ़्लैग का मतलब ऐसे बिट से है जिन्हें किसी यूनियन वैल्यू में जोड़ा जा सकता है. अगर ऐसा नहीं है, तो वैरिएबल या कॉन्स्टेंट को flag न कहें.

public static final int FLAG_SOMETHING = 2;
public static final int FLAG_SOMETHING = 3;
public static final int FLAG_PRIVATE = 1 << 2;
public static final int FLAG_PRESENTATION = 1 << 3;

सार्वजनिक फ़्लैग कॉन्स्टेंट तय करने के बारे में ज़्यादा जानकारी के लिए, बिटमास्क फ़्लैग के लिए @IntDef देखें.

स्टैटिक फ़ाइनल कॉन्स्टेंट के लिए, सभी कैपिटल लेटर और अंडरस्कोर से अलग किए गए नाम रखने के तरीके का इस्तेमाल किया जाना चाहिए

कॉन्स्टेंट में मौजूद सभी शब्दों के पहले अक्षर बड़े होने चाहिए. साथ ही, एक से ज़्यादा शब्दों को _ से अलग किया जाना चाहिए. उदाहरण के लिए:

public static final int fooThing = 5
public static final int FOO_THING = 5

स्थिरांकों के लिए स्टैंडर्ड प्रीफ़िक्स का इस्तेमाल करना

Android में इस्तेमाल किए जाने वाले कई कॉन्स्टेंट, स्टैंडर्ड चीज़ों के लिए होते हैं. जैसे, फ़्लैग, कुंजियां, और कार्रवाइयां. इन कॉन्स्टेंट में स्टैंडर्ड प्रीफ़िक्स होने चाहिए, ताकि इन्हें आसानी से पहचाना जा सके.

उदाहरण के लिए, इंटेंट एक्स्ट्रा EXTRA_ से शुरू होने चाहिए. इवेंट के नाम की शुरुआत ACTION_ से होनी चाहिए. Context.bindService() के साथ इस्तेमाल किए गए कॉन्स्टेंट, BIND_ से शुरू होने चाहिए.

मुख्य कॉन्स्टेंट के नाम और स्कोप

स्ट्रिंग कॉन्स्टेंट की वैल्यू, कॉन्स्टेंट के नाम के मुताबिक होनी चाहिए. साथ ही, इन्हें आम तौर पर पैकेज या डोमेन के स्कोप में रखा जाना चाहिए. उदाहरण के लिए:

public static final String FOO_THING = "foo"

इसका नाम न तो एक जैसा है और न ही इसका स्कोप सही है. इसके बजाय, इन बातों का ध्यान रखें:

public static final String FOO_THING = "android.fooservice.FOO_THING"

स्कोप किए गए स्ट्रिंग कॉन्स्टेंट में android के प्रीफ़िक्स, Android ओपन सोर्स प्रोजेक्ट के लिए रिज़र्व किए गए हैं.

इंटेंट ऐक्शन और अतिरिक्त जानकारी के साथ-साथ बंडल एंट्री को नेमस्पेस किया जाना चाहिए. इसके लिए, उस पैकेज का नाम इस्तेमाल करें जिसमें उन्हें तय किया गया है.

package android.foo.bar {
  public static final String ACTION_BAZ = "android.foo.bar.action.BAZ"
  public static final String EXTRA_BAZ = "android.foo.bar.extra.BAZ"
}

सुरक्षित के बजाय सार्वजनिक का इस्तेमाल करें

@see Use public instead of protected

एक जैसे प्रीफ़िक्स का इस्तेमाल करना

एक जैसे कॉन्स्टेंट, एक ही प्रीफ़िक्स से शुरू होने चाहिए. उदाहरण के लिए, फ़्लैग वैल्यू के साथ इस्तेमाल करने के लिए कॉन्स्टेंट का सेट:

public static final int SOME_VALUE = 0x01;

public static final int SOME_OTHER_VALUE = 0x10;

public static final int SOME_THIRD_VALUE = 0x100;
public static final int FLAG_SOME_VALUE = 0x01;

public static final int FLAG_SOME_OTHER_VALUE = 0x10;

public static final int FLAG_SOME_THIRD_VALUE = 0x100;

@see कॉन्सटेंट के लिए स्टैंडर्ड प्रीफ़िक्स इस्तेमाल करना

संसाधनों के लिए एक जैसे नाम इस्तेमाल करना

सार्वजनिक आइडेंटिफ़ायर, एट्रिब्यूट, और वैल्यू के नाम, कैमल केस नाम रखने के नियम के मुताबिक होने चाहिए. उदाहरण के लिए, @id/accessibilityActionPageUp या @attr/textAppearance. ये नाम, Java में सार्वजनिक फ़ील्ड के नाम की तरह होने चाहिए.

कुछ मामलों में, सार्वजनिक आइडेंटिफ़ायर या एट्रिब्यूट में एक सामान्य प्रीफ़िक्स शामिल होता है, जिसे अंडरस्कोर से अलग किया जाता है:

  • प्लैटफ़ॉर्म कॉन्फ़िगरेशन वैल्यू, जैसे कि @string/config_recentsComponentName, config.xml में
  • लेआउट के हिसाब से व्यू एट्रिब्यूट, जैसे कि @attr/layout_marginStart in attrs.xml

पब्लिक थीम और स्टाइल के नाम, पदानुक्रमित PascalCase नेमिंग कन्वेंशन के मुताबिक होने चाहिए. उदाहरण के लिए, @style/Theme.Material.Light.DarkActionBar या @style/Widget.Material.SearchView.ActionBar. यह Java में नेस्ट की गई क्लास की तरह होता है.

लेआउट और ड्रॉएबल रिसॉर्स को सार्वजनिक एपीआई के तौर पर नहीं दिखाना चाहिए. हालांकि, अगर उन्हें दिखाना ज़रूरी है, तो सार्वजनिक लेआउट और ड्रॉएबल के नाम, under_score नेमिंग कन्वेंशन का इस्तेमाल करके रखे जाने चाहिए. उदाहरण के लिए, layout/simple_list_item_1.xml या drawable/title_bar_tall.xml.

जब स्थिरांक बदल सकते हों, तो उन्हें डाइनैमिक बनाएं

कंपाइलर, कॉन्स्टेंट वैल्यू को इनलाइन कर सकता है. इसलिए, वैल्यू को एक जैसा रखना एपीआई अनुबंध का हिस्सा माना जाता है. अगर MIN_FOO या MAX_FOO कॉन्स्टेंट की वैल्यू में आने वाले समय में बदलाव हो सकता है, तो उन्हें डाइनैमिक तरीके से इस्तेमाल करें.

CameraManager.MAX_CAMERAS
CameraManager.getMaxCameras()

कॉलबैक के लिए, नए सिस्टम के साथ काम करने की सुविधा का इस्तेमाल करना

एपीआई के आने वाले वर्शन में तय किए गए कॉन्स्टेंट के बारे में उन ऐप्लिकेशन को पता नहीं होता जो एपीआई के पुराने वर्शन को टारगेट करते हैं. इस वजह से, ऐप्लिकेशन को डिलीवर किए गए कॉन्स्टेंट को उस ऐप्लिकेशन के टारगेट एपीआई वर्शन को ध्यान में रखना चाहिए. साथ ही, नए कॉन्स्टेंट को एक जैसी वैल्यू पर मैप करना चाहिए. यह उदाहरण देखें:

एसडीके का काल्पनिक सोर्स:

// Added in API level 22
public static final int STATUS_SUCCESS = 1;
public static final int STATUS_FAILURE = 2;
// Added in API level 23
public static final int STATUS_FAILURE_RETRY = 3;
// Added in API level 26
public static final int STATUS_FAILURE_ABORT = 4;

targetSdkVersion="22" की सुविधा वाला काल्पनिक ऐप्लिकेशन:

if (result == STATUS_FAILURE) {
  // Oh no!
} else {
  // Success!
}

इस मामले में, ऐप्लिकेशन को एपीआई लेवल 22 की सीमाओं के हिसाब से डिज़ाइन किया गया था. साथ ही, यह (कुछ हद तक) सही अनुमान लगाया गया था कि सिर्फ़ दो स्थितियां हो सकती हैं. हालांकि, अगर ऐप्लिकेशन को नया STATUS_FAILURE_RETRY मिलता है, तो वह इसे सफल मानता है.

कॉन्स्टेंट वैल्यू देने वाले तरीके, इस तरह के मामलों को सुरक्षित तरीके से हैंडल कर सकते हैं. इसके लिए, वे अपने आउटपुट को ऐप्लिकेशन के टारगेट किए गए एपीआई लेवल के हिसाब से सीमित करते हैं:

private int mapResultForTargetSdk(Context context, int result) {
  int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
  if (targetSdkVersion < 26) {
    if (result == STATUS_FAILURE_ABORT) {
      return STATUS_FAILURE;
    }
    if (targetSdkVersion < 23) {
      if (result == STATUS_FAILURE_RETRY) {
        return STATUS_FAILURE;
      }
    }
  }
  return result;
}

डेवलपर यह अनुमान नहीं लगा सकते कि आने वाले समय में कॉन्स्टेंट की सूची में बदलाव होगा या नहीं. अगर आपने किसी एपीआई को UNKNOWN या UNSPECIFIED कॉन्स्टेंट के साथ तय किया है, तो डेवलपर यह मान लेते हैं कि पब्लिश किए गए कॉन्स्टेंट पूरी तरह से काम करते हैं. अगर आपको यह उम्मीद सेट नहीं करनी है, तो इस बारे में फिर से सोचें कि क्या आपके एपीआई के लिए, कैच-ऑल कॉन्स्टेंट का इस्तेमाल करना सही है.

इसके अलावा, लाइब्रेरी ऐप्लिकेशन से अलग अपना targetSdkVersion तय नहीं कर सकतीं. साथ ही, लाइब्रेरी कोड से targetSdkVersion के व्यवहार में होने वाले बदलावों को मैनेज करना मुश्किल होता है और इसमें गड़बड़ियां हो सकती हैं.

पूर्णांक या स्ट्रिंग कॉन्स्टेंट

अगर वैल्यू के लिए नेमस्पेस को आपके पैकेज के बाहर नहीं बढ़ाया जा सकता, तो पूर्णांक स्थिरांक और @IntDef का इस्तेमाल करें. अगर नेमस्पेस शेयर किया गया है या आपके पैकेज के बाहर के कोड से बढ़ाया जा सकता है, तो स्ट्रिंग कॉन्स्टेंट का इस्तेमाल करें.

डेटा क्लास

डेटा क्लास, न बदली जा सकने वाली प्रॉपर्टी का सेट होती हैं. साथ ही, ये डेटा के साथ इंटरैक्ट करने के लिए, यूटिलिटी फ़ंक्शन का छोटा और अच्छी तरह से तय किया गया सेट उपलब्ध कराती हैं.

सार्वजनिक Kotlin एपीआई में data class का इस्तेमाल न करें, क्योंकि Kotlin कंपाइलर, जनरेट किए गए कोड के लिए भाषा के एपीआई या बाइनरी कंपैटिबिलिटी की गारंटी नहीं देता है. इसके बजाय, ज़रूरी फ़ंक्शन को मैन्युअल तरीके से लागू करें.

इंस्टैंशिएट करना

Java में, डेटा क्लास को कंस्ट्रक्टर देना चाहिए, जब कुछ प्रॉपर्टी हों या जब कई प्रॉपर्टी हों, तो Builder पैटर्न का इस्तेमाल करना चाहिए.

Kotlin में, डेटा क्लास को डिफ़ॉल्ट आर्ग्युमेंट के साथ कंस्ट्रक्टर देना चाहिए. भले ही, प्रॉपर्टी की संख्या कुछ भी हो. Java क्लाइंट को टारगेट करते समय, Kotlin में तय की गई डेटा क्लास को बिल्डर उपलब्ध कराने से भी फ़ायदा मिल सकता है.

बदलाव और कॉपी करना

जिन मामलों में डेटा में बदलाव करना होता है उनमें, कॉपी कंस्ट्रक्टर (Java) के साथ Builder क्लास या नया ऑब्जेक्ट दिखाने वाला copy() मेंबर फ़ंक्शन (Kotlin) दें.

Kotlin में copy() फ़ंक्शन देते समय, आर्ग्युमेंट को क्लास के कंस्ट्रक्टर से मेल खाना चाहिए. साथ ही, डिफ़ॉल्ट वैल्यू को ऑब्जेक्ट की मौजूदा वैल्यू का इस्तेमाल करके पॉप्युलेट किया जाना चाहिए:

class Typography(
  val labelMedium: TextStyle = TypographyTokens.LabelMedium,
  val labelSmall: TextStyle = TypographyTokens.LabelSmall
) {
    fun copy(
      labelMedium: TextStyle = this.labelMedium,
      labelSmall: TextStyle = this.labelSmall
    ): Typography = Typography(
      labelMedium = labelMedium,
      labelSmall = labelSmall
    )
}

अन्य व्यवहार

डेटा क्लास को equals() और hashCode(), दोनों को लागू करना चाहिए. साथ ही, हर प्रॉपर्टी को इन तरीकों के लागू करने के तरीके में शामिल किया जाना चाहिए.

डेटा क्लास, toString() को लागू कर सकती हैं. इसके लिए, Kotlin की डेटा क्लास को लागू करने के तरीके से मिलता-जुलता सुझाया गया फ़ॉर्मैट इस्तेमाल किया जा सकता है. उदाहरण के लिए, User(var1=Alex, var2=42).

माटिंग में इस्तेमाल हुए तरीके

ये नियमों के बारे में खास जानकारी देते हैं. जैसे, पैरामीटर, तरीके के नाम, रिटर्न टाइप, और ऐक्सेस स्पेसिफ़ायर.

समय

इन नियमों में बताया गया है कि तारीख और अवधि जैसे समय के कॉन्सेप्ट को एपीआई में कैसे दिखाया जाना चाहिए.

जहां भी हो सके वहां java.time.* टाइप का इस्तेमाल करें

java.time.Duration, java.time.Instant, और कई अन्य java.time.* टाइप, सभी प्लैटफ़ॉर्म वर्शन पर उपलब्ध हैं. इसके लिए, desugaring का इस्तेमाल किया जाता है. साथ ही, एपीआई पैरामीटर या रिटर्न वैल्यू में समय बताने के लिए, इनका इस्तेमाल किया जाना चाहिए.

एपीआई के सिर्फ़ उन वैरिएंट को दिखाएं जो java.time.Duration या java.time.Instant स्वीकार करते हैं या उन्हें दिखाते हैं. साथ ही, एक ही इस्तेमाल के उदाहरण वाले प्रिमिटिव वैरिएंट को तब तक न दिखाएं, जब तक कि एपीआई डोमेन ऐसा न हो जहां इस्तेमाल के पैटर्न में ऑब्जेक्ट असाइन करने से परफ़ॉर्मेंस पर काफ़ी असर पड़ता हो.

अवधि बताने वाले तरीकों का नाम अवधि होना चाहिए

अगर समय की वैल्यू से किसी अवधि का पता चलता है, तो पैरामीटर का नाम "duration" रखें, न कि "time".

ValueAnimator.setTime(java.time.Duration);
ValueAnimator.setDuration(java.time.Duration);

अपवाद:

"timeout" का इस्तेमाल तब करना चाहिए, जब अवधि खास तौर पर टाइमआउट वैल्यू पर लागू होती हो.

समय के किसी खास बिंदु के बारे में बताने के लिए, java.time.Instant टाइप वाला "time" इस्तेमाल करना सही है. अवधि के बारे में बताने के लिए इसका इस्तेमाल नहीं किया जा सकता.

अवधि या समय को प्रिमिटिव के तौर पर दिखाने वाले तरीकों का नाम, उनकी समय इकाई के हिसाब से होना चाहिए. साथ ही, उनमें लंबे

ऐसे तरीके जो अवधि को प्रिमिटिव के तौर पर स्वीकार करते हैं या दिखाते हैं उनके नाम के आखिर में, समय की जुड़ी हुई इकाइयों (जैसे कि Millis, Nanos, Seconds) को जोड़ना चाहिए, ताकि java.time.Duration के साथ इस्तेमाल करने के लिए, बिना सजावट वाले नाम को रिज़र्व किया जा सके. Time देखें.

तरीकों को उनकी यूनिट और समय के आधार पर सही तरीके से एनोटेट किया जाना चाहिए:

  • @CurrentTimeMillisLong: वैल्यू, एक नॉन-नेगेटिव टाइमस्टैंप है. इसे 1970-01-01T00:00:00Z से मिलीसेकंड की संख्या के तौर पर मेज़र किया जाता है.
  • @CurrentTimeSecondsLong: वैल्यू, टाइमस्टैंप होती है. यह 1970-01-01T00:00:00Z से लेकर अब तक के सेकंड की संख्या के तौर पर मेज़र की जाती है.
  • @DurationMillisLong: वैल्यू, मिलीसेकंड में बताई गई अवधि होती है. यह अवधि शून्य या इससे ज़्यादा होनी चाहिए.
  • @ElapsedRealtimeLong: वैल्यू, SystemClock.elapsedRealtime() टाइम बेस में एक नॉन-नेगेटिव टाइमस्टैंप है.
  • @UptimeMillisLong: वैल्यू, SystemClock.uptimeMillis() टाइम बेस में एक नॉन-नेगेटिव टाइमस्टैंप है.

प्रिमिटिव टाइम पैरामीटर या रिटर्न वैल्यू के लिए, long का इस्तेमाल किया जाना चाहिए, न कि int का.

ValueAnimator.setDuration(@DurationMillisLong long);
ValueAnimator.setDurationNanos(long);

समय की इकाइयों को दिखाने वाले तरीकों में, इकाइयों के नामों के लिए छोटे नाम का इस्तेमाल नहीं किया जाना चाहिए

public void setIntervalNs(long intervalNs);

public void setTimeoutUs(long timeoutUs);
public void setIntervalNanos(long intervalNanos);

public void setTimeoutMicros(long timeoutMicros);

लंबे समय के आर्ग्युमेंट को एनोटेट करना

इस प्लैटफ़ॉर्म में कई एनोटेशन शामिल हैं, ताकि long-टाइप की समय इकाइयों के लिए बेहतर टाइपिंग की जा सके:

  • @CurrentTimeMillisLong: वैल्यू, एक नॉन-नेगेटिव टाइमस्टैंप है. इसे 1970-01-01T00:00:00Z से मिलीसेकंड की संख्या के तौर पर मेज़र किया जाता है. इसलिए, यह System.currentTimeMillis() टाइम बेस में होता है.
  • @CurrentTimeSecondsLong: वैल्यू, एक नॉन-नेगेटिव टाइमस्टैंप है. इसे 1970-01-01T00:00:00Z के बाद से सेकंड की संख्या के तौर पर मेज़र किया जाता है.
  • @DurationMillisLong: वैल्यू, मिलीसेकंड में बताई गई अवधि होती है. यह अवधि शून्य या इससे ज़्यादा होनी चाहिए.
  • @ElapsedRealtimeLong: वैल्यू, SystemClock#elapsedRealtime() टाइम बेस में एक नॉन-नेगेटिव टाइमस्टैंप है.
  • @UptimeMillisLong: वैल्यू, SystemClock#uptimeMillis() टाइम बेस में एक नॉन-नेगेटिव टाइमस्टैंप है.

मेज़रमेंट की इकाइयां

समय के अलावा माप की किसी अन्य इकाई को दिखाने वाले सभी तरीकों के लिए, CamelCased एसआई यूनिट प्रीफ़िक्स का इस्तेमाल करें.

public  long[] getFrequenciesKhz();

public  float getStreamVolumeDb();

वैकल्पिक पैरामीटर को ओवरलोड के आखिर में रखें

अगर आपके पास ऐसे तरीके हैं जिनमें वैकल्पिक पैरामीटर शामिल हैं, तो उन पैरामीटर को आखिर में रखें. साथ ही, अन्य पैरामीटर के साथ एक जैसा क्रम बनाए रखें:

public int doFoo(boolean flag);

public int doFoo(int id, boolean flag);
public int doFoo(boolean flag);

public int doFoo(boolean flag, int id);

ज़रूरी नहीं वाले आर्ग्युमेंट के लिए ओवरलोड जोड़ते समय, आसान तरीकों का व्यवहार ठीक उसी तरह होना चाहिए जैसे ज़्यादा जटिल तरीकों को डिफ़ॉल्ट आर्ग्युमेंट दिए गए हों.

उपयोग से जुड़ी सलाह: अगर किसी तरीके में एक से ज़्यादा रूप हैं, तो उसमें सिर्फ़ वैकल्पिक आर्ग्युमेंट जोड़ने या अलग-अलग तरह के आर्ग्युमेंट स्वीकार करने के लिए ही ओवरलोड न करें. अगर ओवरलोड किए गए तरीके से कोई बुनियादी काम अलग तरीके से किया जाता है, तो उसे नया नाम दें.

डिफ़ॉल्ट पैरामीटर वाले तरीकों को @JvmOverloads (सिर्फ़ Kotlin) के साथ एनोटेट किया जाना चाहिए

डिफ़ॉल्ट पैरामीटर वाले तरीकों और कंस्ट्रक्टर को @JvmOverloads के साथ एनोटेट किया जाना चाहिए, ताकि बाइनरी कंपैटिबिलिटी बनी रहे.

ज़्यादा जानकारी के लिए, Kotlin-Java इंटरऑप की आधिकारिक गाइड में डिफ़ॉल्ट के लिए फ़ंक्शन ओवरलोड देखें.

class Greeting @JvmOverloads constructor(
  loudness: Int = 5
) {
  @JvmOverloads
  fun sayHello(prefix: String = "Dr.", name: String) = // ...
}

डिफ़ॉल्ट पैरामीटर वैल्यू न हटाएं (सिर्फ़ Kotlin के लिए)

अगर किसी तरीके को डिफ़ॉल्ट वैल्यू वाले पैरामीटर के साथ शिप किया गया है, तो डिफ़ॉल्ट वैल्यू को हटाना, सोर्स कोड में किया गया ऐसा बदलाव है जिससे सोर्स कोड काम करना बंद कर सकता है.

पहचान करने वाले सबसे खास तरीके के पैरामीटर सबसे पहले होने चाहिए

अगर आपके पास कई पैरामीटर वाला कोई तरीका है, तो सबसे काम के पैरामीटर को सबसे पहले रखें. फ़्लैग और अन्य विकल्पों के बारे में बताने वाले पैरामीटर, उन पैरामीटर से कम ज़रूरी होते हैं जो उस ऑब्जेक्ट के बारे में बताते हैं जिस पर कार्रवाई की जा रही है. अगर कोई कंप्लीशन कॉलबैक है, तो उसे आखिर में रखें.

public void openFile(int flags, String name);

public void openFileAsync(OnFileOpenedListener listener, String name, int flags);

public void setFlags(int mask, int flags);
public void openFile(String name, int flags);

public void openFileAsync(String name, int flags, OnFileOpenedListener listener);

public void setFlags(int flags, int mask);

यह भी देखें: ओवरलोड में वैकल्पिक पैरामीटर को आखिर में रखना

बिल्डर

जटिल Java ऑब्जेक्ट बनाने के लिए, बिल्डर पैटर्न का इस्तेमाल करने का सुझाव दिया जाता है. साथ ही, Android में इसका इस्तेमाल आम तौर पर इन मामलों में किया जाता है:

  • नतीजे के तौर पर मिले ऑब्जेक्ट की प्रॉपर्टी में बदलाव नहीं किया जा सकता
  • ज़रूरी प्रॉपर्टी की संख्या बहुत ज़्यादा है. उदाहरण के लिए, कई कंस्ट्रक्टर आर्ग्युमेंट
  • कंस्ट्रक्शन के समय प्रॉपर्टी के बीच एक जटिल संबंध होता है. उदाहरण के लिए, पुष्टि करने का चरण ज़रूरी है. ध्यान दें कि इस तरह की जटिलता से अक्सर, एपीआई के इस्तेमाल में आने वाली समस्याओं का पता चलता है.

सोचें कि आपको बिल्डर की ज़रूरत है या नहीं. बिल्डर, एपीआई के लिए तब फ़ायदेमंद होते हैं, जब इनका इस्तेमाल इन कामों के लिए किया जाता है:

  • क्रिएशन के लिए उपलब्ध कई वैकल्पिक पैरामीटर में से सिर्फ़ कुछ को कॉन्फ़िगर करना
  • कई अलग-अलग तरह के ज़रूरी या वैकल्पिक पैरामीटर कॉन्फ़िगर करना. कभी-कभी, ये पैरामीटर एक जैसे या मिलते-जुलते टाइप के होते हैं. ऐसे में, कॉल साइटों को पढ़ना मुश्किल हो सकता है या उनमें गड़बड़ियां हो सकती हैं
  • किसी ऑब्जेक्ट को धीरे-धीरे बनाने की प्रोसेस को कॉन्फ़िगर करें. इसमें कॉन्फ़िगरेशन कोड के कई अलग-अलग हिस्से, बिल्डर को लागू करने की जानकारी के तौर पर कॉल कर सकते हैं
  • किसी टाइप को बढ़ाने की अनुमति दें, ताकि एपीआई के आने वाले वर्शन में, क्रिएशन के अतिरिक्त पैरामीटर जोड़े जा सकें

अगर आपके पास तीन या इससे कम ज़रूरी पैरामीटर वाला कोई टाइप है और कोई भी वैकल्पिक पैरामीटर नहीं है, तो बिल्डर को लगभग हमेशा स्किप किया जा सकता है और सामान्य कंस्ट्रक्टर का इस्तेमाल किया जा सकता है.

Kotlin से सोर्स की गई क्लास को, बिल्डर के बजाय डिफ़ॉल्ट आर्ग्युमेंट वाले @JvmOverloads-एनोटेट किए गए कंस्ट्रक्टर को प्राथमिकता देनी चाहिए. हालांकि, पहले बताए गए मामलों में Java क्लाइंट के लिए इस्तेमाल में आसानी को बेहतर बनाने के लिए, बिल्डर भी उपलब्ध कराए जा सकते हैं.

class Tone @JvmOverloads constructor(
  val duration: Long = 1000,
  val frequency: Int = 2600,
  val dtmfConfigs: List<DtmfConfig> = emptyList()
) {
  class Builder {
    // ...
  }
}

बिल्डर क्लास को बिल्डर वापस करना होगा

बिल्डर क्लास को, हर तरीके से बिल्डर ऑब्जेक्ट (जैसे, this) को वापस करके, मेथड चेनिंग की सुविधा चालू करनी होगी. हालांकि, build() को छोड़कर. बनाए गए अतिरिक्त ऑब्जेक्ट को आर्ग्युमेंट के तौर पर पास किया जाना चाहिए. किसी दूसरे ऑब्जेक्ट के बिल्डर को वापस न करें. उदाहरण के लिए:

public static class Builder {
  public void setDuration(long);
  public void setFrequency(int);
  public DtmfConfigBuilder addDtmfConfig();
  public Tone build();
}
public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }
}

कुछ मामलों में, बेस बिल्डर क्लास को एक्सटेंशन के साथ काम करना होता है. ऐसे में, सामान्य रिटर्न टाइप का इस्तेमाल करें:

public abstract class Builder<T extends Builder<T>> {
  abstract T setValue(int);
}

public class TypeBuilder<T extends TypeBuilder<T>> extends Builder<T> {
  T setValue(int);
  T setTypeSpecificValue(long);
}

बिल्डर क्लास को कंस्ट्रक्टर के ज़रिए बनाया जाना चाहिए

Android API की मदद से, बिल्डर को एक जैसा बनाए रखने के लिए, सभी बिल्डर को कंस्ट्रक्टर के ज़रिए बनाया जाना ज़रूरी है. इन्हें स्टैटिक क्रिएटर मेथड के ज़रिए नहीं बनाया जाना चाहिए. Kotlin पर आधारित एपीआई के लिए, Builder को सार्वजनिक होना चाहिए. भले ही, Kotlin के उपयोगकर्ताओं को फ़ैक्ट्री मेथड/डीएसएल स्टाइल क्रिएशन मैकेनिज़्म के ज़रिए, बिल्डर पर भरोसा करना पड़े. लाइब्रेरी को @PublishedApi internal का इस्तेमाल नहीं करना चाहिए, ताकि Kotlin क्लाइंट से Builder क्लास कंस्ट्रक्टर को चुनिंदा तौर पर छिपाया जा सके.

public class Tone {
  public static Builder builder();
  public static class Builder {
  }
}
public class Tone {
  public static class Builder {
    public Builder();
  }
}

बिल्डर कंस्ट्रक्टर के सभी आर्ग्युमेंट ज़रूरी होने चाहिए (जैसे, @NonNull)

वैकल्पिक तौर पर, उदाहरण के लिए @Nullable, आर्ग्युमेंट को सेटर के तरीकों में ले जाना चाहिए. अगर कोई ज़रूरी आर्ग्युमेंट नहीं दिया गया है, तो बिल्डर कंस्ट्रक्टर को NullPointerException थ्रो करना चाहिए (Objects.requireNonNull का इस्तेमाल करने पर विचार करें).

बिल्डर क्लास, बिल्ट टाइप की फ़ाइनल स्टैटिक इनर क्लास होनी चाहिए

पैकेज में लॉजिकल संगठन के लिए, बिल्डर क्लास को आम तौर पर उनके बिल्ट टाइप की फ़ाइनल इनर क्लास के तौर पर दिखाया जाना चाहिए. उदाहरण के लिए, ToneBuilder के बजाय Tone.Builder.

बिल्डर, किसी मौजूदा इंस्टेंस से नया इंस्टेंस बनाने के लिए कंस्ट्रक्टर शामिल कर सकते हैं

बिल्डर, कॉपी कंस्ट्रक्टर को शामिल कर सकते हैं, ताकि मौजूदा बिल्डर या बनाए गए ऑब्जेक्ट से नया बिल्डर इंस्टेंस बनाया जा सके. उन्हें मौजूदा बिल्डर या बिल्ड ऑब्जेक्ट से बिल्डर इंस्टेंस बनाने के लिए, अन्य तरीके नहीं देने चाहिए.

public class Tone {
  public static class Builder {
    public Builder clone();
  }

  public Builder toBuilder();
}
public class Tone {
  public static class Builder {
    public Builder(Builder original);
    public Builder(Tone original);
  }
}
अगर बिल्डर के पास कॉपी कंस्ट्रक्टर है, तो बिल्डर सेटर को @Nullable आर्ग्युमेंट लेने चाहिए

अगर किसी मौजूदा इंस्टेंस से बिल्डर का नया इंस्टेंस बनाया जा सकता है, तो रीसेट करना ज़रूरी है. अगर कोई कॉपी कंस्ट्रक्टर उपलब्ध नहीं है, तो बिल्डर के पास @Nullable या @NonNullable आर्ग्युमेंट हो सकते हैं.

public static class Builder {
  public Builder(Builder original);
  public Builder setObjectValue(@Nullable Object value);
}
बिल्डर सेटर, ज़रूरी नहीं कि प्रॉपर्टी के लिए @Nullable आर्ग्युमेंट ले

सेकंड-डिग्री इनपुट के लिए, अक्सर शून्य वैल्यू का इस्तेमाल करना आसान होता है. खास तौर पर, Kotlin में. यह बिल्डर और ओवरलोड के बजाय डिफ़ॉल्ट आर्ग्युमेंट का इस्तेमाल करता है.

इसके अलावा, @Nullable सेटर, उन्हें गेटर से मैच करेंगे. यह ज़रूरी है कि वैकल्पिक प्रॉपर्टी के लिए, @Nullable हो.

Value createValue(@Nullable OptionalValue optionalValue) {
  Value.Builder builder = new Value.Builder();
  if (optionalValue != null) {
    builder.setOptionalValue(optionalValue);
  }
  return builder.build();
}
Value createValue(@Nullable OptionalValue optionalValue) {
  return new Value.Builder()
    .setOptionalValue(optionalValue);
    .build();
}

// Or in other cases:

Value createValue() {
  return new Value.Builder()
    .setOptionalValue(condition ? new OptionalValue() : null);
    .build();
}

Kotlin में इसका इस्तेमाल आम तौर पर ऐसे किया जाता है:

fun createValue(optionalValue: OptionalValue? = null) =
  Value.Builder()
    .apply { optionalValue?.let { setOptionalValue(it) } }
    .build()
fun createValue(optionalValue: OptionalValue? = null) =
  Value.Builder()
    .setOptionalValue(optionalValue)
    .build()

सेटर और गेटर, दोनों में डिफ़ॉल्ट वैल्यू (अगर सेटर को कॉल नहीं किया जाता है) और null का मतलब सही तरीके से बताया जाना चाहिए.

/**
 * ...
 *
 * <p>Defaults to {@code null}, which means the optional value won't be used.
 */

बिल्डर सेटर, बदली जा सकने वाली प्रॉपर्टी के लिए उपलब्ध कराए जा सकते हैं. इसके लिए, बिल्ट क्लास पर सेटर उपलब्ध होने चाहिए

अगर आपकी क्लास में बदलने वाली प्रॉपर्टी हैं और उसे Builder क्लास की ज़रूरत है, तो पहले खुद से पूछें कि क्या आपकी क्लास में असल में बदलने वाली प्रॉपर्टी होनी चाहिए.

इसके बाद, अगर आपको लगता है कि आपको बदलने वाली प्रॉपर्टी की ज़रूरत है, तो तय करें कि इस्तेमाल के आपके अनुमानित उदाहरण के लिए, यहां दिए गए कौनसे विकल्प बेहतर हैं:

  1. बनाया गया ऑब्जेक्ट तुरंत इस्तेमाल किया जा सकता है. इसलिए, सभी काम की प्रॉपर्टी के लिए, सेटर ज़रूर दिए जाने चाहिए. भले ही, वे प्रॉपर्टी बदली जा सकती हों या नहीं.

    map.put(key, new Value.Builder(requiredValue)
        .setImmutableProperty(immutableValue)
        .setUsefulMutableProperty(usefulValue)
        .build());
    
  2. बनाए गए ऑब्जेक्ट का इस्तेमाल करने से पहले, कुछ और कॉल करने पड़ सकते हैं. इसलिए, बदलने वाली प्रॉपर्टी के लिए सेटर नहीं दिए जाने चाहिए.

    Value v = new Value.Builder(requiredValue)
        .setImmutableProperty(immutableValue)
        .build();
    v.setUsefulMutableProperty(usefulValue)
    Result r = v.performSomeAction();
    Key k = callSomeMethod(r);
    map.put(k, v);
    

दोनों स्थितियों को एक साथ न मिलाएं.

Value v = new Value.Builder(requiredValue)
    .setImmutableProperty(immutableValue)
    .setUsefulMutableProperty(usefulValue)
    .build();
Result r = v.performSomeAction();
Key k = callSomeMethod(r);
map.put(k, v);

बिल्डर में गेटर नहीं होने चाहिए

गेटर, बिल्डर पर नहीं, बल्कि बनाए गए ऑब्जेक्ट पर होना चाहिए.

बिल्डर सेटर के पास, बनाई गई क्लास पर उससे जुड़े गेटर होने चाहिए

public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }
}
public class Tone {
  public static class Builder {
    public Builder setDuration(long);
    public Builder setFrequency(int);
    public Builder addDtmfConfig(DtmfConfig);
    public Tone build();
  }

  public long getDuration();
  public int getFrequency();
  public @NonNull List<DtmfConfig> getDtmfConfigs();
}

बिल्डर के तरीके का नामकरण

बिल्डर के तरीके के नामों में setFoo(), addFoo() या clearFoo() स्टाइल का इस्तेमाल किया जाना चाहिए.

बिल्डर क्लास को build() तरीके का एलान करना चाहिए

बिल्डर क्लास को build() मेथड के बारे में बताना चाहिए. यह मेथड, बनाए गए ऑब्जेक्ट का इंस्टेंस दिखाता है.

Builder build() तरीकों को @NonNull ऑब्जेक्ट लौटाने चाहिए

बिल्डर के build() तरीके से, बनाए गए ऑब्जेक्ट का नॉननल इंस्टेंस वापस मिलने की उम्मीद की जाती है. अगर अमान्य पैरामीटर की वजह से ऑब्जेक्ट नहीं बनाया जा सकता है, तो पुष्टि करने की प्रोसेस को build तरीके पर टाला जा सकता है. साथ ही, IllegalStateException को थ्रो किया जाना चाहिए.

इंटरनल लॉक को ऐक्सेस करने की अनुमति न दें

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

इसके बजाय, किसी इंटरनल, प्राइवेट ऑब्जेक्ट के लिए ज़रूरी लॉकिंग करें.

public synchronized void doThing() { ... }
private final Object mThingLock = new Object();

public void doThing() {
  synchronized (mThingLock) {
    ...
  }
}

ऐक्सेसर स्टाइल वाले तरीकों को Kotlin प्रॉपर्टी के दिशा-निर्देशों का पालन करना चाहिए

Kotlin सोर्स से देखने पर, ऐक्सेसर स्टाइल वाले तरीके -- get, set या is प्रीफ़िक्स का इस्तेमाल करने वाले तरीके -- Kotlin प्रॉपर्टी के तौर पर भी उपलब्ध होंगे. उदाहरण के लिए, Java में तय किया गया int getField(), Kotlin में प्रॉपर्टी val field: Int के तौर पर उपलब्ध है.

इस वजह से, और ऐक्सेसर तरीके के व्यवहार के बारे में डेवलपर की उम्मीदों को पूरा करने के लिए, ऐक्सेसर तरीके के प्रीफ़िक्स का इस्तेमाल करने वाले तरीकों को Java फ़ील्ड की तरह काम करना चाहिए. ऐक्सेसर-स्टाइल वाले प्रीफ़िक्स का इस्तेमाल इन मामलों में न करें:

  • इस तरीके के कुछ साइड इफ़ेक्ट हैं. इसलिए, तरीके का ऐसा नाम चुनें जिससे ज़्यादा जानकारी मिलती हो
  • इस तरीके में काफ़ी ज़्यादा कंप्यूटेशनल पावर की ज़रूरत होती है -- compute का इस्तेमाल करें
  • इस तरीके में, किसी वैल्यू को वापस लाने के लिए लंबे समय तक काम को ब्लॉक किया जाता है. जैसे, आईपीसी या अन्य आई/ओ -- fetch को प्राथमिकता दें
  • यह तरीका, थ्रेड को तब तक ब्लॉक करता है, जब तक वह कोई वैल्यू नहीं लौटा सकता. इसलिए, await का इस्तेमाल करें
  • यह तरीका, हर कॉल पर एक नया ऑब्जेक्ट इंस्टेंस दिखाता है. इसलिए, create का इस्तेमाल करें
  • इस तरीके से वैल्यू नहीं मिल सकती -- request का इस्तेमाल करें

ध्यान दें कि एक बार में ज़्यादा संसाधन इस्तेमाल करने वाला काम करने और बाद के कॉल के लिए वैल्यू को कैश मेमोरी में सेव करने को भी, ज़्यादा संसाधन इस्तेमाल करने वाला काम माना जाता है. जंक को फ़्रेम में नहीं बांटा जाता.

बूलियन ऐक्सेसर के तरीकों के लिए, is प्रीफ़िक्स का इस्तेमाल करना

यह Java में बूलियन तरीकों और फ़ील्ड के लिए, नाम रखने का स्टैंडर्ड तरीका है. आम तौर पर, बूलियन तरीके और वैरिएबल के नाम ऐसे सवालों के तौर पर लिखे जाने चाहिए जिनके जवाब, रिटर्न वैल्यू से मिलते हों.

Java के बूलियन ऐक्सेसर मेथड के नाम, set/is नेमिंग स्कीम के मुताबिक होने चाहिए. साथ ही, फ़ील्ड के लिए is का इस्तेमाल करना चाहिए. जैसे:

// Visibility is a direct property. The object "is" visible:
void setVisible(boolean visible);
boolean isVisible();

// Factory reset protection is an indirect property.
void setFactoryResetProtectionEnabled(boolean enabled);
boolean isFactoryResetProtectionEnabled();

final boolean isAvailable;

Java ऐक्सेस करने के तरीकों के लिए set/is या Java फ़ील्ड के लिए is का इस्तेमाल करने से, उन्हें Kotlin से प्रॉपर्टी के तौर पर इस्तेमाल किया जा सकेगा:

obj.isVisible = true
obj.isFactoryResetProtectionEnabled = false
if (!obj.isAvailable) return

प्रॉपर्टी और ऐक्सेस करने के तरीकों के लिए, आम तौर पर पॉज़िटिव नेमिंग का इस्तेमाल करना चाहिए. उदाहरण के लिए, Enabled के बजाय Disabled. नेगेटिव शब्दों का इस्तेमाल करने से, true और false का मतलब बदल जाता है. साथ ही, इससे व्यवहार के बारे में तर्क देना ज़्यादा मुश्किल हो जाता है.

// Passing false here is a double-negative.
void setFactoryResetProtectionDisabled(boolean disabled);

अगर बूलियन, किसी प्रॉपर्टी को शामिल करने या उसके मालिकाना हक के बारे में बताता है, तो is के बजाय has का इस्तेमाल किया जा सकता है. हालांकि, यह Kotlin प्रॉपर्टी के सिंटैक्स के साथ काम नहीं करेगा:

// Transient state is an indirect property used to track state
// related to the object. The object is not transient; rather,
// the object "has" transient state associated with it:
void setHasTransientState(boolean hasTransientState);
boolean hasTransientState();

कुछ ऐसे वैकल्पिक प्रीफ़िक्स हैं जो ज़्यादा सही हो सकते हैं. जैसे, can और should:

// "Can" describes a behavior that the object may provide,
// and here is more concise than setRecordingEnabled or
// setRecordingAllowed. The object "can" record:
void setCanRecord(boolean canRecord);
boolean canRecord();

// "Should" describes a hint or property that is not strictly
// enforced, and here is more explicit than setFitWidthEnabled.
// The object "should" fit width:
void setShouldFitWidth(boolean shouldFitWidth);
boolean shouldFitWidth();

व्यवहार या सुविधाओं को टॉगल करने वाले तरीकों में, is प्रीफ़िक्स और Enabled सफ़िक्स का इस्तेमाल किया जा सकता है:

// "Enabled" describes the availability of a property, and is
// more appropriate here than "can use" or "should use" the
// property:
void setWiFiRoamingSettingEnabled(boolean enabled)
boolean isWiFiRoamingSettingEnabled()

इसी तरह, अन्य व्यवहारों या सुविधाओं पर निर्भरता दिखाने वाले तरीकों में, is प्रीफ़िक्स और Supported या Required सफ़िक्स का इस्तेमाल किया जा सकता है:

// "Supported" describes whether this API would work on devices that support
// multiple users. The API "supports" multi-user:
void setMultiUserSupported(boolean supported)
boolean isMultiUserSupported()
// "Required" describes whether this API depends on devices that support
// multiple users. The API "requires" multi-user:
void setMultiUserRequired(boolean required)
boolean isMultiUserRequired()

आम तौर पर, तरीके के नामों को ऐसे सवालों के तौर पर लिखा जाना चाहिए जिनके जवाब, दिखाई गई वैल्यू से मिलते हों.

Kotlin प्रॉपर्टी के तरीके

क्लास प्रॉपर्टी var foo: Foo के लिए, Kotlin get/set तरीके जनरेट करेगा. इसके लिए, वह एक ही नियम का इस्तेमाल करेगा: गेटर के लिए, get को पहले जोड़ें और पहले वर्ण को कैपिटल करें. सेटर के लिए, set को पहले जोड़ें और पहले वर्ण को कैपिटल करें. प्रॉपर्टी के एलान से, public Foo getFoo() और public void setFoo(Foo foo) नाम के तरीके जनरेट होंगे.

अगर प्रॉपर्टी का टाइप Boolean है, तो नाम जनरेट करने के लिए एक अतिरिक्त नियम लागू होता है: अगर प्रॉपर्टी का नाम is से शुरू होता है, तो getter तरीके के नाम के लिए get को पहले नहीं जोड़ा जाता. प्रॉपर्टी के नाम का इस्तेमाल getter के तौर पर किया जाता है. इसलिए, नाम रखने के दिशा-निर्देशों का पालन करने के लिए, Boolean प्रॉपर्टी के नाम में is प्रीफ़िक्स का इस्तेमाल करें:

var isVisible: Boolean

अगर आपकी प्रॉपर्टी ऊपर बताई गई किसी भी शर्त को पूरा करती है और उसका नाम सही प्रीफ़िक्स से शुरू होता है, तो प्रॉपर्टी पर @get:JvmName एनोटेशन का इस्तेमाल करें. इससे, सही नाम को मैन्युअल तरीके से सेट किया जा सकेगा:

@get:JvmName("hasTransientState")
var hasTransientState: Boolean

@get:JvmName("canRecord")
var canRecord: Boolean

@get:JvmName("shouldFitWidth")
var shouldFitWidth: Boolean

बिटमास्क ऐक्सेसर

बिटमास्क फ़्लैग तय करने से जुड़े एपीआई के दिशा-निर्देशों के लिए, बिटमास्क फ़्लैग के लिए @IntDef का इस्तेमाल करना देखें.

सेटर

दो सेटर तरीके उपलब्ध कराए जाने चाहिए: एक ऐसा जो पूरी बिटस्ट्रिंग लेता है और सभी मौजूदा फ़्लैग को बदल देता है. दूसरा ऐसा जो ज़्यादा फ़्लेक्सिबिलिटी की अनुमति देने के लिए कस्टम बिटमास्क लेता है.

/**
 * Sets the state of all scroll indicators.
 * <p>
 * See {@link #setScrollIndicators(int, int)} for usage information.
 *
 * @param indicators a bitmask of indicators that should be enabled, or
 *                   {@code 0} to disable all indicators
 * @see #setScrollIndicators(int, int)
 * @see #getScrollIndicators()
 */
public void setScrollIndicators(@ScrollIndicators int indicators);

/**
 * Sets the state of the scroll indicators specified by the mask. To change
 * all scroll indicators at once, see {@link #setScrollIndicators(int)}.
 * <p>
 * When a scroll indicator is enabled, it will be displayed if the view
 * can scroll in the direction of the indicator.
 * <p>
 * Multiple indicator types may be enabled or disabled by passing the
 * logical OR of the specified types. If multiple types are specified, they
 * will all be set to the same enabled state.
 * <p>
 * For example, to enable the top scroll indicator:
 * {@code setScrollIndicators(SCROLL_INDICATOR_TOP, SCROLL_INDICATOR_TOP)}
 * <p>
 * To disable the top scroll indicator:
 * {@code setScrollIndicators(0, SCROLL_INDICATOR_TOP)}
 *
 * @param indicators a bitmask of values to set; may be a single flag,
 *                   the logical OR of multiple flags, or 0 to clear
 * @param mask a bitmask indicating which indicator flags to modify
 * @see #setScrollIndicators(int)
 * @see #getScrollIndicators()
 */
public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask);

गैटर

पूरा बिटमास्क पाने के लिए, एक गेटर उपलब्ध कराया जाना चाहिए.

/**
 * Returns a bitmask representing the enabled scroll indicators.
 * <p>
 * For example, if the top and left scroll indicators are enabled and all
 * other indicators are disabled, the return value will be
 * {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
 * <p>
 * To check whether the bottom scroll indicator is enabled, use the value
 * of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
 *
 * @return a bitmask representing the enabled scroll indicators
 */
@ScrollIndicators
public int getScrollIndicators();

सुरक्षित के बजाय सार्वजनिक का इस्तेमाल करें

सार्वजनिक एपीआई में, हमेशा protected के बजाय public को प्राथमिकता दें. सुरक्षित ऐक्सेस की सुविधा लंबे समय तक इस्तेमाल करने में मुश्किल होती है. ऐसा इसलिए, क्योंकि लागू करने वालों को उन मामलों में सार्वजनिक ऐक्सेसर उपलब्ध कराने के लिए, इस सुविधा को रद्द करना पड़ता है जहां डिफ़ॉल्ट रूप से बाहरी ऐक्सेस उतना ही अच्छा होता.

ध्यान रखें कि protectedविज़िबिलिटी से, डेवलपर को एपीआई कॉल करने से नहीं रोका जाता. इससे सिर्फ़ एपीआई कॉल करने की प्रोसेस थोड़ी मुश्किल हो जाती है.

equals() और hashCode() में से किसी एक या दोनों को लागू न करें

अगर आपने किसी एक को बदला है, तो आपको दूसरे को भी बदलना होगा.

डेटा क्लास के लिए toString() लागू करना

डेवलपर को अपने कोड को डीबग करने में मदद करने के लिए, डेटा क्लास को toString() को ओवरराइड करने के लिए कहा जाता है.

दस्तावेज़ में यह जानकारी शामिल करें कि आउटपुट, प्रोग्राम के व्यवहार के लिए है या डीबग करने के लिए

तय करें कि आपको प्रोग्राम के व्यवहार को लागू करने के लिए, अपने तरीके का इस्तेमाल करना है या नहीं. उदाहरण के लिए, UUID.toString() और File.toString() प्रोग्राम के इस्तेमाल के लिए, उनके खास फ़ॉर्मैट के बारे में जानकारी देते हैं. अगर आपको सिर्फ़ डीबग करने के लिए जानकारी दिखानी है, जैसे कि Intent, तो सुपरक्लास से दस्तावेज़ इनहेरिट करने का सुझाव दें.

अतिरिक्त जानकारी शामिल न करें

toString() में उपलब्ध सभी जानकारी, ऑब्जेक्ट के सार्वजनिक एपीआई के ज़रिए भी उपलब्ध होनी चाहिए. अगर ऐसा नहीं किया जाता है, तो डेवलपर को toString() के आउटपुट को पार्स करने और उस पर भरोसा करने के लिए बढ़ावा दिया जाएगा. इससे आने वाले समय में बदलाव नहीं किए जा सकेंगे. सबसे सही तरीका यह है कि toString() को सिर्फ़ ऑब्जेक्ट के सार्वजनिक एपीआई का इस्तेमाल करके लागू किया जाए.

डीबग आउटपुट पर भरोसा न करने के लिए प्रोत्साहित करना

डेवलपर को डीबग आउटपुट पर निर्भर रहने से रोका नहीं जा सकता. हालांकि, अपने ऑब्जेक्ट के System.identityHashCode को उसके toString() आउटपुट में शामिल करने से, यह संभावना बहुत कम हो जाएगी कि दो अलग-अलग ऑब्जेक्ट का toString() आउटपुट एक जैसा होगा.

@Override
public String toString() {
  return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " {mFoo=" + mFoo + "}";
}

इससे डेवलपर को, आपके ऑब्जेक्ट पर assertThat(a.toString()).isEqualTo(b.toString()) जैसे टेस्ट असर्शन लिखने से रोका जा सकता है.

नए ऑब्जेक्ट वापस लाते समय, createFoo का इस्तेमाल करें

नई वैल्यू बनाने वाले तरीकों के लिए, create प्रीफ़िक्स का इस्तेमाल करें. जैसे, नए ऑब्जेक्ट बनाना. इसके लिए, get या new का इस्तेमाल न करें.

जब कोई तरीका, ऑब्जेक्ट को वापस लाने के लिए ऑब्जेक्ट बनाता है, तो तरीके के नाम में यह बात साफ़ तौर पर बताएं.

public FooThing getFooThing() {
  return new FooThing();
}
public FooThing createFooThing() {
  return new FooThing();
}

फ़ाइल ऑब्जेक्ट स्वीकार करने वाले तरीकों को स्ट्रीम भी स्वीकार करनी चाहिए

Android पर डेटा सेव करने की जगहें, हमेशा डिस्क पर मौजूद फ़ाइलें नहीं होती हैं. उदाहरण के लिए, उपयोगकर्ता की सीमाओं के बाहर पास किए गए कॉन्टेंट को content:// Uri के तौर पर दिखाया जाता है. अलग-अलग डेटा सोर्स को प्रोसेस करने के लिए, File ऑब्जेक्ट स्वीकार करने वाले एपीआई को InputStream, OutputStream या दोनों को भी स्वीकार करना चाहिए.

public void setDataSource(File file)
public void setDataSource(InputStream stream)

बॉक्स किए गए वर्शन के बजाय, रॉ प्रिमिटिव लें और उन्हें वापस करें

अगर आपको किसी वैल्यू के मौजूद न होने या शून्य वैल्यू के बारे में बताना है, तो -1, Integer.MAX_VALUE या Integer.MIN_VALUE का इस्तेमाल करें.

public java.lang.Integer getLength()
public void setLength(java.lang.Integer)
public int getLength()
public void setLength(int value)

प्रिमिटिव टाइप के क्लास इक्विवेलेंट से बचने पर, इन क्लास के मेमोरी ओवरहेड से बचा जा सकता है. साथ ही, वैल्यू के लिए मेथड ऐक्सेस और इससे भी ज़्यादा ज़रूरी, प्रिमिटिव और ऑब्जेक्ट टाइप के बीच कास्टिंग से आने वाली ऑटोबॉक्सिंग से बचा जा सकता है. इन तरीकों से बचने पर, मेमोरी और कुछ समय के लिए किए गए ऐसे असाइनमेंट को सेव किया जा सकता है जिनकी वजह से, गार्बेज कलेक्शन की प्रोसेस बार-बार और ज़्यादा समय तक चलती है.

मान्य पैरामीटर और रिटर्न वैल्यू के बारे में साफ़ तौर पर बताने के लिए, एनोटेशन का इस्तेमाल करना

डेवलपर एनोटेशन इसलिए जोड़े गए थे, ताकि अलग-अलग स्थितियों में इस्तेमाल की जा सकने वाली वैल्यू के बारे में साफ़ तौर पर बताया जा सके. इससे टूल को डेवलपर की मदद करने में आसानी होती है. ऐसा तब होता है, जब डेवलपर गलत वैल्यू देता है. उदाहरण के लिए, जब फ़्रेमवर्क को कॉन्स्टेंट वैल्यू के किसी खास सेट में से किसी एक की ज़रूरत होती है, तब कोई भी int पास करना. ज़रूरत के हिसाब से, इनमें से किसी भी एनोटेशन का इस्तेमाल करें:

शून्यता

Java API के लिए, साफ़ तौर पर यह जानकारी देना ज़रूरी है कि कोई वैल्यू शून्य हो सकती है या नहीं. हालांकि, Kotlin भाषा में यह जानकारी पहले से मौजूद होती है. इसलिए, Kotlin API में इस जानकारी का इस्तेमाल कभी नहीं करना चाहिए.

@Nullable: इससे पता चलता है कि दी गई रिटर्न वैल्यू, पैरामीटर या फ़ील्ड, शून्य हो सकता है:

@Nullable
public String getName()

public void setName(@Nullable String name)

@NonNull: इससे पता चलता है कि दी गई रिटर्न वैल्यू, पैरामीटर या फ़ील्ड, शून्य नहीं हो सकता. Android में, चीज़ों को @Nullable के तौर पर मार्क करने की सुविधा हाल ही में लॉन्च हुई है. इसलिए, Android के ज़्यादातर एपीआई तरीकों के बारे में लगातार दस्तावेज़ नहीं बनाए जाते. इसलिए, हमारे पास "unknown, @Nullable, @NonNull" की तीन स्थितियां हैं. यही वजह है कि @NonNull, एपीआई के दिशा-निर्देशों का हिस्सा है:

@NonNull
public String getName()

public void setName(@NonNull String name)

Android प्लैटफ़ॉर्म के दस्तावेज़ों के लिए, अपने तरीके के पैरामीटर एनोटेट करने पर, "यह वैल्यू शून्य हो सकती है" के तौर पर दस्तावेज़ अपने-आप जनरेट हो जाएगा. ऐसा तब तक होगा, जब तक पैरामीटर के दस्तावेज़ में "null" का इस्तेमाल साफ़ तौर पर कहीं और न किया गया हो.

"नॉट नलेबल" के तौर पर मार्क किए गए मौजूदा तरीके: एपीआई में मौजूद ऐसे तरीके जिनमें @Nullable एनोटेशन का इस्तेमाल नहीं किया गया है उन्हें @Nullable के तौर पर एनोटेट किया जा सकता है. ऐसा तब किया जा सकता है, जब तरीका खास और साफ़ तौर पर बताई गई स्थितियों में null वैल्यू दिखाता हो. जैसे, findViewById(). IllegalArgumentException थ्रो करने वाले कंपैनियन @NotNull requireFoo() तरीके, उन डेवलपर के लिए जोड़े जाने चाहिए जो नल की जांच नहीं करना चाहते.

इंटरफ़ेस के तरीके: नए एपीआई को इंटरफ़ेस के तरीकों को लागू करते समय, सही एनोटेशन जोड़ना चाहिए.जैसे, Parcelable.writeToParcel() (यानी कि लागू करने वाली क्लास में वह तरीका writeToParcel(@NonNull Parcel, int) होना चाहिए, न कि writeToParcel(Parcel, int)); हालांकि, जिन मौजूदा एपीआई में एनोटेशन नहीं हैं उन्हें "ठीक" करने की ज़रूरत नहीं है.

शून्य होने की स्थिति में लागू होने वाली नीति

Java में, @NonNull पैरामीटर के लिए इनपुट की पुष्टि करने के लिए, सुझाए गए तरीकों का इस्तेमाल किया जाता है. इसके लिए, Objects.requireNonNull() का इस्तेमाल किया जाता है. साथ ही, जब पैरामीटर शून्य होते हैं, तब NullPointerException को थ्रो किया जाता है. यह काम Kotlin में अपने-आप होता है.

संसाधन

संसाधन आइडेंटिफ़ायर: खास संसाधनों के आईडी दिखाने वाले पूर्णांक पैरामीटर को संसाधन-टाइप की सही परिभाषा के साथ एनोटेट किया जाना चाहिए. @AnyRes के अलावा, हर तरह के रिसॉर्स के लिए एनोटेशन होता है. जैसे, @StringRes, @ColorRes, और @AnimRes. उदाहरण के लिए:

public void setTitle(@StringRes int resId)

स्थिर सेट के लिए @IntDef

मैजिक कॉन्स्टेंट: String और int पैरामीटर, सार्वजनिक कॉन्स्टेंट से दिखाए गए संभावित वैल्यू के सीमित सेट में से किसी एक को पाने के लिए होते हैं. इन्हें @StringDef या @IntDef के साथ सही तरीके से एनोटेट किया जाना चाहिए. इन एनोटेशन की मदद से, एक नया एनोटेशन बनाया जा सकता है. इसका इस्तेमाल, अनुमति वाले पैरामीटर के लिए टाइपडेफ़ की तरह किया जा सकता है. उदाहरण के लिए:

/** @hide */
@IntDef(prefix = {"NAVIGATION_MODE_"}, value = {
  NAVIGATION_MODE_STANDARD,
  NAVIGATION_MODE_LIST,
  NAVIGATION_MODE_TABS
})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

@NavigationMode
public int getNavigationMode();
public void setNavigationMode(@NavigationMode int mode);

एनोटेट किए गए पैरामीटर की पुष्टि करने के लिए, इन तरीकों का सुझाव दिया जाता है. साथ ही, अगर पैरामीटर @IntDef का हिस्सा नहीं है, तो IllegalArgumentException दिखाएं

बिटमास्क फ़्लैग के लिए @IntDef

एनोटेशन से यह भी पता चल सकता है कि कॉन्स्टेंट, फ़्लैग हैं. इन्हें & और I के साथ जोड़ा जा सकता है:

/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
  FLAG_USE_LOGO,
  FLAG_SHOW_HOME,
  FLAG_HOME_AS_UP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}

स्ट्रिंग कॉन्स्टेंट सेट के लिए @StringDef

@StringDef एनोटेशन भी होता है. यह पिछले सेक्शन में दिए गए @IntDef एनोटेशन की तरह ही होता है. हालांकि, यह String कॉन्स्टेंट के लिए होता है. एक से ज़्यादा "prefix" वैल्यू शामिल की जा सकती हैं. इनका इस्तेमाल, सभी वैल्यू के लिए दस्तावेज़ अपने-आप जनरेट करने के लिए किया जाता है.

एसडीके के कॉन्स्टेंट के लिए @SdkConstant

@SdkConstant एनोटेशन का इस्तेमाल करके, सार्वजनिक फ़ील्ड को तब मार्क करें, जब वे इनमें से कोई SdkConstant वैल्यू हों: ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE.

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL = "android.intent.action.CALL";

बदलावों के लिए, नल वैल्यू स्वीकार करने की सुविधा उपलब्ध कराएं

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

टाइप अभिभावक बच्चा
रिटर्न टाइप बिना एनोटेशन वाले टिप्पणी नहीं की गई है या शून्य नहीं है
रिटर्न टाइप Null किया जा सकता है शून्य हो सकता है या शून्य नहीं हो सकता
रिटर्न टाइप NonNull NonNull
मज़ेदार तर्क बिना एनोटेशन वाले बिना एनोटेशन वाला या शून्य हो सकता है
मज़ेदार तर्क Null किया जा सकता है Null किया जा सकता है
मज़ेदार तर्क NonNull शून्य हो सकता है या शून्य नहीं हो सकता

जहां हो सके वहां नॉन-नलेबल (जैसे कि @NonNull) आर्ग्युमेंट का इस्तेमाल करें

जब तरीकों को ओवरलोड किया जाता है, तो यह बेहतर होता है कि सभी तर्क गैर-शून्य हों.

public void startActivity(@NonNull Component component) { ... }
public void startActivity(@NonNull Component component, @NonNull Bundle options) { ... }

यह नियम, प्रॉपर्टी सेट करने वाले ऐसे फ़ंक्शन पर भी लागू होता है जिनमें बहुत ज़्यादा पैरामीटर होते हैं. प्राइमरी आर्ग्युमेंट, nonnull होना चाहिए. साथ ही, प्रॉपर्टी को हटाने के लिए अलग से कोई तरीका लागू किया जाना चाहिए. इससे "बेकार" कॉल को रोका जा सकता है. इनमें डेवलपर को ऐसे ट्रेलिंग पैरामीटर सेट करने पड़ते हैं जिनकी ज़रूरत नहीं होती.

public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode)
public void setTitleItem(@Nullable IconCompat icon, @ImageMode mode, boolean isLoading)

// Nonsense call to clear property
setTitleItem(null, MODE_RAW, false);
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode)
public void setTitleItem(@NonNull IconCompat icon, @ImageMode mode, boolean isLoading)
public void clearTitleItem()

कंटेनर के लिए, नॉन-नलेबल (जैसे कि @NonNull) रिटर्न टाइप को प्राथमिकता दें

Bundle या Collection जैसे कंटेनर टाइप के लिए, खाली कंटेनर दिखाएं. साथ ही, जहां लागू हो वहां कंटेनर में बदलाव न किया जा सके. ऐसे मामलों में जहां कंटेनर की उपलब्धता में अंतर करने के लिए null का इस्तेमाल किया जाता है, वहां एक अलग बूलियन तरीका इस्तेमाल करें.

@NonNull
public Bundle getExtras() { ... }

get और set पेयर के लिए, नल वैल्यू स्वीकार करने की सुविधा से जुड़े एनोटेशन एक जैसे होने चाहिए

किसी एक लॉजिकल प्रॉपर्टी के लिए, get और set के तरीके हमेशा एक जैसे होने चाहिए. इस दिशा-निर्देश का पालन न करने पर, Kotlin के प्रॉपर्टी सिंटैक्स का इस्तेमाल नहीं किया जा सकेगा. साथ ही, मौजूदा प्रॉपर्टी के तरीकों में, शून्य हो सकने वाली वैल्यू के बारे में बताने वाले एनोटेशन जोड़ने से, Kotlin का इस्तेमाल करने वाले लोगों के लिए सोर्स कोड में बदलाव करना मुश्किल हो जाएगा.

@NonNull
public Bundle getExtras() { ... }
public void setExtras(@NonNull Bundle bundle) { ... }

गड़बड़ी या समस्या होने पर दिखाई जाने वाली वैल्यू

सभी एपीआई को ऐप्लिकेशन को गड़बड़ियों पर प्रतिक्रिया करने की अनुमति देनी चाहिए. false, -1, null या "कुछ गड़बड़ी हुई" की अन्य कैच-ऑल वैल्यू वापस भेजने से, डेवलपर को यह नहीं पता चलता कि उपयोगकर्ता की उम्मीदों को पूरा करने या फ़ील्ड में अपने ऐप्लिकेशन की विश्वसनीयता को सटीक तरीके से ट्रैक करने में क्या गड़बड़ी हुई है. एपीआई डिज़ाइन करते समय, मान लें कि आपको एक ऐप्लिकेशन बनाना है. अगर आपको कोई गड़बड़ी मिलती है, तो क्या एपीआई आपको इतनी जानकारी देता है कि आप उसे उपयोगकर्ता को दिखा सकें या उसके हिसाब से कार्रवाई कर सकें?

  1. अपवाद के मैसेज में ज़्यादा जानकारी शामिल करना ठीक है. हालांकि, डेवलपर को गड़बड़ी को ठीक से हैंडल करने के लिए, इस जानकारी को पार्स नहीं करना चाहिए. गड़बड़ी के ज़्यादा जानकारी वाले कोड या अन्य जानकारी को तरीकों के तौर पर दिखाना चाहिए.
  2. पक्का करें कि गड़बड़ी ठीक करने के लिए चुना गया विकल्प, आपको आने वाले समय में नई तरह की गड़बड़ियां ठीक करने की सुविधा देता हो. @IntDef के लिए, इसका मतलब है कि इसमें OTHER या UNKNOWN वैल्यू शामिल करना. नया कोड वापस भेजते समय, कॉलर के targetSdkVersion की जांच की जा सकती है, ताकि ऐसा गड़बड़ी कोड वापस न भेजा जाए जिसके बारे में ऐप्लिकेशन को जानकारी न हो. अपवादों के लिए, एक सामान्य सुपरक्लास बनाएं. इससे आपके अपवाद लागू होंगे. इससे उस टाइप को हैंडल करने वाला कोई भी कोड, सबटाइप को भी पकड़ लेगा और उन्हें हैंडल करेगा.
  3. डेवलपर के लिए, गलती से किसी गड़बड़ी को अनदेखा करना मुश्किल या नामुमकिन होना चाहिए. अगर आपकी गड़बड़ी की जानकारी, वैल्यू लौटाकर दी जाती है, तो अपने तरीके को @CheckResult से एनोटेट करें.

डेवलपर की किसी गलती की वजह से, अगर कोई गड़बड़ी होती है या कोई समस्या आती है, तो ? extends RuntimeException को थ्रो करें. उदाहरण के लिए, इनपुट पैरामीटर पर मौजूद पाबंदियों को अनदेखा करना या ऑब्ज़र्वेबल स्टेट की जांच न करना.

सेटर या ऐक्शन (उदाहरण के लिए, perform) के तरीके, पूर्णांक स्थिति कोड दिखा सकते हैं. ऐसा तब होता है, जब एसिंक्रोनस तरीके से अपडेट की गई स्थिति या डेवलपर के कंट्रोल से बाहर की स्थितियों की वजह से ऐक्शन पूरा न हो पाए.

स्टेटस कोड को, शामिल करने वाली क्लास में public static final फ़ील्ड के तौर पर तय किया जाना चाहिए. इनके पहले ERROR_ प्रीफ़िक्स होना चाहिए. साथ ही, इन्हें @hide @IntDef एनोटेशन में शामिल किया जाना चाहिए.

तरीके के नाम हमेशा क्रिया से शुरू होने चाहिए, न कि विषय से

तरीके का नाम हमेशा क्रिया (जैसे कि get, create, reload वगैरह) से शुरू होना चाहिए, न कि उस ऑब्जेक्ट से जिस पर कार्रवाई की जा रही है.

public void tableReload() {
  mTable.reload();
}
public void reloadTable() {
  mTable.reload();
}

ऐरे के बजाय, कलेक्शन टाइप को रिटर्न या पैरामीटर टाइप के तौर पर इस्तेमाल करना

टाइप किए गए कलेक्शन इंटरफ़ेस, ऐरे की तुलना में कई फ़ायदे देते हैं. जैसे, यूनीकनेस और ऑर्डरिंग के लिए बेहतर एपीआई कॉन्ट्रैक्ट, जेनेरिक के लिए सहायता, और डेवलपर के लिए कई सुविधाजनक तरीके.

प्रिमिटिव के लिए अपवाद

अगर एलिमेंट प्रिमिटिव हैं, तो ऑटो-बॉक्सिंग की लागत से बचने के लिए, ऐरे का इस्तेमाल करें. देखें बॉक्स किए गए वर्शन के बजाय, रॉ प्रिमिटिव लेना और उन्हें वापस करना

परफ़ॉर्मेंस के हिसाब से संवेदनशील कोड के लिए अपवाद

कुछ मामलों में, परफ़ॉर्मेंस के हिसाब से अहम कोड में एपीआई का इस्तेमाल किया जाता है. जैसे, ग्राफ़िक्स या अन्य मेज़र/लेआउट/ड्रॉ एपीआई. ऐसे में, कलेक्शन के बजाय ऐरे का इस्तेमाल किया जा सकता है, ताकि मेमोरी के इस्तेमाल को कम किया जा सके.

Kotlin के लिए अपवाद

Kotlin ऐरे, इनवेरिएंट होते हैं. साथ ही, Kotlin लैंग्वेज में ऐरे के लिए कई यूटिलिटी एपीआई उपलब्ध होते हैं. इसलिए, Kotlin एपीआई के लिए ऐरे, List और Collection के बराबर होते हैं. इन एपीआई को Kotlin से ऐक्सेस किया जा सकता है.

@NonNull कलेक्शन को प्राथमिकता दें

संग्रह ऑब्जेक्ट के लिए, हमेशा @NonNull का इस्तेमाल करें. खाली कलेक्शन वापस करते समय, सही Collections.empty तरीके का इस्तेमाल करें. इससे कम कीमत वाला, सही टाइप किया गया, और न बदलने वाला कलेक्शन ऑब्जेक्ट वापस किया जा सकेगा.

जहां टाइप एनोटेशन काम करते हैं वहां कलेक्शन एलिमेंट के लिए, हमेशा @NonNull को प्राथमिकता दें.

आपको कलेक्शन के बजाय ऐरे का इस्तेमाल करते समय @NonNull को भी प्राथमिकता देनी चाहिए (पिछला आइटम देखें). अगर ऑब्जेक्ट के लिए मेमोरी असाइन करने से जुड़ी कोई समस्या है, तो एक कॉन्स्टेंट बनाएं और उसे पास करें. ऐसा इसलिए, क्योंकि एक खाली कलेक्शन में बदलाव नहीं किया जा सकता. उदाहरण:

private static final int[] EMPTY_USER_IDS = new int[0];

@NonNull
public int[] getUserIds() {
  int [] userIds = mService.getUserIds();
  return userIds != null ? userIds : EMPTY_USER_IDS;
}

कलेक्शन में बदलाव करने की सुविधा

Kotlin API को डिफ़ॉल्ट रूप से, कलेक्शन के लिए सिर्फ़ पढ़ने के लिए उपलब्ध (Mutable नहीं) रिटर्न टाइप इस्तेमाल करने चाहिए. हालांकि, अगर एपीआई अनुबंध में खास तौर पर बदलाव किए जा सकने वाले रिटर्न टाइप की ज़रूरत हो, तो ऐसा किया जा सकता है.

हालांकि, Java API को डिफ़ॉल्ट रूप से बदले जा सकने वाले रिटर्न टाइप का इस्तेमाल करना चाहिए. ऐसा इसलिए, क्योंकि Android प्लैटफ़ॉर्म पर Java API के लिए, अब तक बदले न जा सकने वाले कलेक्शन को आसानी से लागू करने की सुविधा उपलब्ध नहीं है. इस नियम का अपवाद Collections.empty रिटर्न टाइप हैं, जिनमें बदलाव नहीं किया जा सकता. ऐसे मामलों में जहां क्लाइंट, एपीआई के इस्तेमाल के पैटर्न को तोड़ने के लिए, जान-बूझकर या गलती से बदलाव करने की सुविधा का गलत इस्तेमाल कर सकते हैं वहां Java API को कलेक्शन की शैलो कॉपी वापस करने पर विचार करना चाहिए.

@Nullable
public PermissionInfo[] getGrantedPermissions() {
  return mPermissions;
}
@NonNull
public Set<PermissionInfo> getGrantedPermissions() {
  if (mPermissions == null) {
    return Collections.emptySet();
  }
  return new ArraySet<>(mPermissions);
}

बदले जा सकने वाले रिटर्न टाइप

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

/**
 * Returns a view of this object as a list of [Item]s.
 */
fun MyObject.asList(): List<Item> = MyObjectListWrapper(this)

Kotlin .asFoo() के बारे में यहां बताया गया है. साथ ही, इसमें यह भी बताया गया है कि अगर ओरिजनल कलेक्शन में बदलाव होता है, तो .asList() से मिले कलेक्शन में बदलाव किया जा सकता है.

नतीजे के तौर पर मिले डेटा-टाइप ऑब्जेक्ट में बदलाव करने की सुविधा

संग्रह दिखाने वाले एपीआई की तरह ही, डेटा-टाइप ऑब्जेक्ट दिखाने वाले एपीआई को भी, ऑब्जेक्ट दिखाने के बाद उसकी प्रॉपर्टी में बदलाव नहीं करना चाहिए.

val tempResult = DataContainer()

fun add(other: DataContainer): DataContainer {
  tempResult.innerValue = innerValue + other.innerValue
  return tempResult
}
fun add(other: DataContainer): DataContainer {
  return DataContainer(innerValue + other.innerValue)
}

बहुत कम मामलों में, परफ़ॉर्मेंस के हिसाब से संवेदनशील कोड को ऑब्जेक्ट पूलिंग या दोबारा इस्तेमाल करने से फ़ायदा मिल सकता है. अपना ऑब्जेक्ट पूल डेटा स्ट्रक्चर न बनाएं और सार्वजनिक एपीआई में फिर से इस्तेमाल किए गए ऑब्जेक्ट न दिखाएं. इन दोनों ही मामलों में, एक साथ कई लोगों के ऐक्सेस को मैनेज करते समय बहुत सावधानी बरतें.

vararg पैरामीटर टाइप का इस्तेमाल करना

Kotlin और Java, दोनों तरह के एपीआई को vararg का इस्तेमाल करने का सुझाव दिया जाता है. ऐसा उन मामलों में किया जाता है जहां डेवलपर, कॉल साइट पर सिर्फ़ एक ही मकसद से ऐरे बनाता है. वह मकसद, एक ही तरह के कई मिलते-जुलते पैरामीटर पास करना होता है.

public void setFeatures(Feature[] features) { ... }

// Developer code
setFeatures(new Feature[]{Features.A, Features.B, Features.C});
public void setFeatures(Feature... features) { ... }

// Developer code
setFeatures(Features.A, Features.B, Features.C);

डिफ़ेंसिव कॉपी

vararg पैरामीटर के Java और Kotlin, दोनों वर्शन एक ही ऐरे-बैक बाइटकोड में कंपाइल होते हैं. इसलिए, इन्हें Java कोड से कॉल किया जा सकता है. इसके लिए, एक ऐसा ऐरे इस्तेमाल करना होगा जिसमें बदलाव किया जा सकता हो. एपीआई डिज़ाइनर को ज़ोरदार सुझाव दिया जाता है कि वे ऐसे मामलों में ऐरे पैरामीटर की डिफ़ेंसिव शैलो कॉपी बनाएं जहां इसे किसी फ़ील्ड या एनोनिमस इनर क्लास में सेव किया जाएगा.

public void setValues(SomeObject... values) {
   this.values = Arrays.copyOf(values, values.length);
}

ध्यान दें कि डिफ़ेंसिव कॉपी बनाने से, शुरुआती तरीके के कॉल और कॉपी बनाने के बीच एक साथ किए गए बदलावों से सुरक्षा नहीं मिलती. साथ ही, यह ऐरे में मौजूद ऑब्जेक्ट के म्यूटेशन से भी सुरक्षा नहीं करती.

संग्रह के टाइप पैरामीटर या दिखाए गए टाइप के साथ सही सिमैंटिक उपलब्ध कराएं

List<Foo> डिफ़ॉल्ट विकल्प है. हालांकि, ज़्यादा जानकारी देने के लिए, अन्य टाइप का इस्तेमाल करें:

  • अगर आपका एपीआई, एलिमेंट के क्रम के हिसाब से काम नहीं करता है और यह डुप्लीकेट की अनुमति नहीं देता है या डुप्लीकेट का कोई मतलब नहीं है, तो Set<Foo> का इस्तेमाल करें.

  • Collection<Foo>, अगर आपका एपीआई क्रम के हिसाब से काम नहीं करता और डुप्लीकेट की अनुमति देता है.

Kotlin कन्वर्ज़न फ़ंक्शन

Kotlin में, अक्सर .toFoo() और .asFoo() का इस्तेमाल किया जाता है. इससे मौजूदा ऑब्जेक्ट से अलग टाइप का ऑब्जेक्ट मिलता है. यहां Foo, कन्वर्ज़न के रिटर्न टाइप का नाम है. यह, जाने-पहचाने JDK Object.toString() के साथ काम करता है. Kotlin, इसका इस्तेमाल प्रिमिटिव कन्वर्ज़न के लिए करता है. जैसे, 25.toFloat().

.toFoo() और .asFoo() नाम वाले कन्वर्ज़न के बीच का अंतर काफ़ी अहम है:

नया और स्वतंत्र ऑब्जेक्ट बनाते समय .toFoo() का इस्तेमाल करें

.toString() की तरह, "to" कन्वर्ज़न से एक नया, इंडिपेंडेंट ऑब्जेक्ट मिलता है. अगर बाद में मूल ऑब्जेक्ट में बदलाव किया जाता है, तो नए ऑब्जेक्ट में वे बदलाव नहीं दिखेंगे. इसी तरह, अगर बाद में new ऑब्जेक्ट में बदलाव किया जाता है, तो old ऑब्जेक्ट में वे बदलाव नहीं दिखेंगे.

fun Foo.toBundle(): Bundle = Bundle().apply {
    putInt(FOO_VALUE_KEY, value)
}

डिपेंडेंट रैपर, डेकोरेटेड ऑब्जेक्ट या कास्ट बनाते समय .asFoo() का इस्तेमाल करना

Kotlin में, कास्टिंग as कीवर्ड का इस्तेमाल करके की जाती है. इससे इंटरफ़ेस में बदलाव दिखता है, लेकिन पहचान में कोई बदलाव नहीं होता. एक्सटेंशन फ़ंक्शन में प्रीफ़िक्स के तौर पर इस्तेमाल किए जाने पर, .asFoo() रिसीवर को डेकोरेट करता है. मूल रिसीवर ऑब्जेक्ट में किए गए बदलाव, asFoo() से मिले ऑब्जेक्ट में दिखेंगे. नए Foo ऑब्जेक्ट में किए गए बदलाव, ओरिजनल ऑब्जेक्ट में दिख सकते हैं.

fun <T> Flow<T>.asLiveData(): LiveData<T> = liveData {
    collect {
        emit(it)
    }
}

कन्वर्ज़न फ़ंक्शन को एक्सटेंशन फ़ंक्शन के तौर पर लिखा जाना चाहिए

रिसीवर और नतीजे की क्लास, दोनों के बाहर कन्वर्ज़न फ़ंक्शन लिखने से टाइप के बीच कपलिंग कम हो जाती है. किसी कन्वर्ज़न को सही तरीके से ट्रैक करने के लिए, ओरिजनल ऑब्जेक्ट के लिए सिर्फ़ सार्वजनिक एपीआई ऐक्सेस की ज़रूरत होती है. इससे यह पता चलता है कि डेवलपर, अपनी पसंद के कन्वर्ज़न टाइप के लिए भी मिलते-जुलते कन्वर्ज़न लिख सकता है.

ज़रूरत के हिसाब से खास गड़बड़ियां ट्रिगर करना

तरीकों से सामान्य अपवाद नहीं मिलने चाहिए, जैसे कि java.lang.Exception या java.lang.Throwable. इसके बजाय, किसी खास अपवाद का इस्तेमाल किया जाना चाहिए, जैसे कि java.lang.NullPointerException. इससे डेवलपर को बहुत ज़्यादा जानकारी दिए बिना, अपवादों को मैनेज करने की अनुमति मिलती है.

सार्वजनिक तौर पर इस्तेमाल किए गए तरीके को सीधे तौर पर दिए गए तर्कों से जुड़ी गड़बड़ियों के लिए, java.lang.IllegalArgumentException या java.lang.NullPointerException के बजाय java.lang.IllegalStateException थ्रो करना चाहिए.

सुनने वाले और कॉलबैक

ये लिसनर और कॉलबैक मैकेनिज़्म के लिए इस्तेमाल की जाने वाली क्लास और तरीकों से जुड़े नियम हैं.

कॉल बैक क्लास के नाम सिंगुलर होने चाहिए

MyObjectCallbacks के बजाय, MyObjectCallback का इस्तेमाल करें.

कॉलबैक के तरीके के नाम, on फ़ॉर्मैट में होने चाहिए

onFooEvent का मतलब है कि FooEvent हो रहा है और कॉलबैक को इसके जवाब में कार्रवाई करनी चाहिए.

समय के बारे में बताने के लिए, भूतकाल और वर्तमान काल का इस्तेमाल किया जाना चाहिए

इवेंट से जुड़ी कॉलबैक विधियों के नाम ऐसे होने चाहिए जिनसे यह पता चले कि इवेंट पहले ही हो चुका है या हो रहा है.

उदाहरण के लिए, अगर क्लिक करने की कार्रवाई के बाद इस तरीके को कॉल किया जाता है:

public void onClicked()

हालांकि, अगर यह तरीका क्लिक ऐक्शन करने के लिए ज़िम्मेदार है, तो:

public boolean onClick()

कॉलबैक के लिए रजिस्टर करना

जब किसी ऑब्जेक्ट में लिसनर या कॉलबैक को जोड़ा या हटाया जा सकता है, तो उससे जुड़े तरीकों के नाम add और remove या register और unregister होने चाहिए. क्लास या एक ही पैकेज में शामिल अन्य क्लास में इस्तेमाल किए गए मौजूदा कन्वेंशन का पालन करें. जब ऐसा कोई उदाहरण मौजूद न हो, तो जोड़ने और हटाने के विकल्प को चुनें.

कॉलबैक को रजिस्टर या अनरजिस्टर करने वाली सभी तरीकों में, कॉल बैक टाइप का पूरा नाम बताया जाना चाहिए.

public void addFooCallback(@NonNull FooCallback callback);
public void removeFooCallback(@NonNull FooCallback callback);
public void registerFooCallback(@NonNull FooCallback callback);
public void unregisterFooCallback(@NonNull FooCallback callback);

कॉलबैक के लिए गेटर का इस्तेमाल न करें

getFooCallback() तरीके जोड़ें. यह उन मामलों के लिए एक आकर्षक विकल्प है जहां डेवलपर, मौजूदा कॉलबैक को अपने रिप्लेसमेंट के साथ जोड़ना चाहते हैं. हालांकि, यह विकल्प भरोसेमंद नहीं है. साथ ही, इससे कॉम्पोनेंट डेवलपर के लिए मौजूदा स्थिति के बारे में तर्क देना मुश्किल हो जाता है. जैसे, मुझे पता चला कि

  • डेवलपर A, setFooCallback(a) को कॉल करता है
  • डेवलपर B, setFooCallback(new B(getFooCallback())) को कॉल करता है
  • डेवलपर A को अपना कॉलबैक a हटाना है. हालांकि, उसे a के टाइप के बारे में जानकारी नहीं है. साथ ही, a को इस तरह से बनाया गया है कि वह रैप किए गए कॉलबैक में इस तरह के बदलावों की अनुमति दे सके.BB

कॉलबैक डिस्पैच को कंट्रोल करने के लिए, एक्ज़ीक्यूटर को स्वीकार करें

ऐसे कॉलबैक रजिस्टर करते समय जिनमें थ्रेडिंग की कोई खास शर्त नहीं होती (यूज़र इंटरफ़ेस टूलकिट के बाहर कहीं भी), यह सुझाव दिया जाता है कि रजिस्ट्रेशन के दौरान Executor पैरामीटर शामिल करें. इससे डेवलपर को वह थ्रेड तय करने की अनुमति मिलती है जिस पर कॉलबैक शुरू किए जाएंगे.

public void registerFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

वैकल्पिक पैरामीटर के बारे में हमारे सामान्य दिशा-निर्देशों के अपवाद के तौर पर, Executor को छोड़कर ओवरलोड उपलब्ध कराया जा सकता है. भले ही, यह पैरामीटर की सूची में आखिरी तर्क न हो. अगर Executor नहीं दिया गया है, तो मुख्य थ्रेड पर Looper.getMainLooper() का इस्तेमाल करके कॉलबैक शुरू किया जाना चाहिए. साथ ही, इसे ओवरलोड किए गए तरीके से जुड़े दस्तावेज़ में शामिल किया जाना चाहिए.

/**
 * ...
 * Note that the callback will be executed on the main thread using
 * {@link Looper.getMainLooper()}. To specify the execution thread, use
 * {@link registerFooCallback(Executor, FooCallback)}.
 * ...
 */
public void registerFooCallback(
    @NonNull FooCallback callback)

public void registerFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

Executor लागू करने से जुड़ी समस्याएं: ध्यान दें कि यहां दिया गया कोड, मान्य एक्ज़ीक्यूटर है!

public class SynchronousExecutor implements Executor {
    @Override
    public void execute(Runnable r) {
        r.run();
    }
}

इसका मतलब है कि इस फ़ॉर्म को लेने वाले एपीआई लागू करते समय, ऐप्लिकेशन प्रोसेस साइड पर आने वाले बाइंडर ऑब्जेक्ट को लागू करने के लिए, ऐप्लिकेशन के दिए गए Executor पर ऐप्लिकेशन के कॉलबैक को शुरू करने से पहले, Binder.clearCallingIdentity() को कॉल करना ज़रूरी है. इस तरह, अनुमति की जांच के लिए बाइंडर आइडेंटिटी (जैसे कि Binder.getCallingUid()) का इस्तेमाल करने वाला कोई भी ऐप्लिकेशन कोड, ऐप्लिकेशन को सही तरीके से एट्रिब्यूट करता है. साथ ही, यह ऐप्लिकेशन को कॉल करने वाली सिस्टम प्रोसेस को एट्रिब्यूट नहीं करता है. अगर आपके एपीआई के उपयोगकर्ताओं को कॉलर की यूआईडी या पीआईडी की जानकारी चाहिए, तो यह आपके एपीआई का एक हिस्सा होना चाहिए. यह इस बात पर निर्भर नहीं होना चाहिए कि उन्होंने Executor कहां से चलाया है.

आपके एपीआई में, Executor तय करने की सुविधा होनी चाहिए. परफ़ॉर्मेंस के लिहाज़ से अहम मामलों में, ऐप्लिकेशन को कोड तुरंत या एपीआई से मिले सुझाव के साथ सिंक करके चलाना पड़ सकता है. Executor को स्वीकार करने से इसकी अनुमति मिल जाती है. सुरक्षा के तौर पर, HandlerThread या ट्रैम्पोलिन जैसा कोई दूसरा एलिमेंट बनाने से, इस्तेमाल के इस उदाहरण का मकसद पूरा नहीं होता.

अगर कोई ऐप्लिकेशन अपनी प्रोसेस में कहीं महंगा कोड चलाने वाला है, तो उसे ऐसा करने दें. ऐप्लिकेशन डेवलपर, आपकी पाबंदियों को हटाने के लिए जो तरीके ढूंढेंगे उन्हें लंबे समय तक बनाए रखना बहुत मुश्किल होगा.

एक कॉलबैक के लिए अपवाद: जब रिपोर्ट किए जा रहे इवेंट के लिए, सिर्फ़ एक कॉलबैक इंस्टेंस का इस्तेमाल करना हो, तो इस स्टाइल का इस्तेमाल करें:

public void setFooCallback(
    @NonNull @CallbackExecutor Executor executor,
    @NonNull FooCallback callback)

public void clearFooCallback()

Handler के बजाय Executor का इस्तेमाल करना

Android के Handler का इस्तेमाल, कॉलबैक के एक्ज़ीक्यूशन को किसी खास Looper थ्रेड पर रीडायरेक्ट करने के लिए, स्टैंडर्ड के तौर पर किया जाता था. इस स्टैंडर्ड में बदलाव किया गया है, ताकि Executor को प्राथमिकता दी जा सके. ऐसा इसलिए, क्योंकि ज़्यादातर ऐप्लिकेशन डेवलपर अपनी थ्रेड पूल मैनेज करते हैं. इससे मुख्य या यूज़र इंटरफ़ेस (यूआई) थ्रेड, ऐप्लिकेशन के लिए उपलब्ध Looper थ्रेड बन जाती है. Executor का इस्तेमाल करके, डेवलपर को अपने मौजूदा/पसंदीदा एक्ज़ीक्यूशन कॉन्टेक्स्ट को फिर से इस्तेमाल करने के लिए ज़रूरी कंट्रोल दें.

एक साथ कई काम करने की सुविधा देने वाली आधुनिक लाइब्रेरी, जैसे कि kotlinx.coroutines या RxJava, शेड्यूलिंग के अपने तरीके उपलब्ध कराती हैं. ये तरीके, ज़रूरत पड़ने पर खुद ही डिस्पैच करते हैं. इसलिए, डबल थ्रेड हॉप से होने वाली देरी से बचने के लिए, डायरेक्ट एक्ज़ीक्यूटर (जैसे कि Runnable::run) का इस्तेमाल करने की सुविधा देना ज़रूरी है. उदाहरण के लिए, Handler का इस्तेमाल करके Looper थ्रेड में पोस्ट करने के लिए एक हॉप और फिर ऐप्लिकेशन के कॉन्करेंसी फ़्रेमवर्क से दूसरा हॉप.

इस दिशा-निर्देश के अपवाद बहुत कम होते हैं. आम तौर पर, इन मामलों में छूट पाने के लिए अपील की जाती है:

मुझे Looper का इस्तेमाल करना होगा, क्योंकि मुझे इवेंट के लिए epoll करने के लिए Looper की ज़रूरत है. इस अपवाद के अनुरोध को स्वीकार कर लिया गया है, क्योंकि इस स्थिति में Executor के फ़ायदे नहीं मिल सकते.

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

Handler, एक ही क्लास के अन्य मिलते-जुलते एपीआई के साथ स्थानीय तौर पर काम करता है. अपवाद का यह अनुरोध कुछ मामलों में स्वीकार किया जाता है. Executor-आधारित ओवरलोड को जोड़ने के लिए प्राथमिकता दी जाती है. साथ ही, Handler को लागू करने के तरीके को माइग्रेट करके, Executor को लागू करने के नए तरीके का इस्तेमाल किया जाता है. (myHandler::post एक मान्य Executor है!) क्लास के साइज़, Handler के मौजूदा तरीकों की संख्या, और इस बात की संभावना के आधार पर कि डेवलपर को नए तरीके के साथ-साथ Handler पर आधारित मौजूदा तरीकों का इस्तेमाल करना होगा, नई Handler पर आधारित विधि जोड़ने के लिए अपवाद दिया जा सकता है.

रजिस्ट्रेशन में सिमेट्री

अगर किसी चीज़ को जोड़ने या रजिस्टर करने का कोई तरीका है, तो उसे हटाने/अनरजिस्टर करने का तरीका भी होना चाहिए. तरीका

registerThing(Thing)

से मेल खाने वाला

unregisterThing(Thing)

अनुरोध का आइडेंटिफ़ायर देना

अगर डेवलपर के लिए किसी कॉलबैक का फिर से इस्तेमाल करना सही है, तो अनुरोध से कॉलबैक को जोड़ने के लिए, आइडेंटिफ़ायर ऑब्जेक्ट दें.

class RequestParameters {
  public int getId() { ... }
}

class RequestExecutor {
  public void executeRequest(
    RequestParameters parameters,
    Consumer<RequestParameters> onRequestCompletedListener) { ... }
}

कई तरीकों वाले कॉलबैक ऑब्जेक्ट

एक से ज़्यादा तरीकों वाले कॉलबैक को interface को प्राथमिकता देनी चाहिए. साथ ही, पहले से रिलीज़ किए गए इंटरफ़ेस में जोड़ते समय default तरीकों का इस्तेमाल करना चाहिए. पहले, इस दिशा-निर्देश में Java 7 में abstract class के तरीकों के उपलब्ध न होने की वजह से, abstract class का सुझाव दिया गया था.default

public interface MostlyOptionalCallback {
  void onImportantAction();
  default void onOptionalInformation() {
    // Empty stub, this method is optional.
  }
}

नॉनब्लॉकिंग फ़ंक्शन कॉल को मॉडल करते समय, android.os.OutcomeReceiver का इस्तेमाल करें

OutcomeReceiver<R,E>, नतीजे की वैल्यू R तब रिपोर्ट करता है, जब ऑपरेशन पूरा हो जाता है. अगर ऐसा नहीं होता है, तो E : Throwable रिपोर्ट करता है. ये वही काम हैं जो किसी सामान्य तरीके से किए जा सकते हैं. किसी ऐसे ब्लॉकिंग तरीके को नॉनब्लॉकिंग एसिंक तरीके में बदलते समय OutcomeReceiver का इस्तेमाल कॉलबैक टाइप के तौर पर करें जो कोई नतीजा दिखाता है या कोई अपवाद दिखाता है:

interface FooType {
  // Before:
  public FooResult requestFoo(FooRequest request);

  // After:
  public void requestFooAsync(FooRequest request, Executor executor,
      OutcomeReceiver<FooResult, Throwable> callback);
}

इस तरह से बदले गए एसिंक तरीके हमेशा void दिखाते हैं. requestFoo से मिलने वाले किसी भी नतीजे को, दिए गए executor पर कॉल करके, requestFooAsync के callback पैरामीटर के OutcomeReceiver.onResult को रिपोर्ट किया जाता है. requestFoo से मिलने वाली किसी भी गड़बड़ी की जानकारी, OutcomeReceiver.onError तरीके को उसी तरह से दी जाती है.

एसिंक तरीके के नतीजों की रिपोर्टिंग के लिए OutcomeReceiver का इस्तेमाल करने से, androidx.core:core-ktx से Continuation.asOutcomeReceiver एक्सटेंशन का इस्तेमाल करने वाले एसिंक तरीकों के लिए, Kotlin suspend fun रैपर भी मिलता है:

suspend fun FooType.requestFoo(request: FooRequest): FooResult =
  suspendCancellableCoroutine { continuation ->
    requestFooAsync(request, Runnable::run, continuation.asOutcomeReceiver())
  }

इस तरह के एक्सटेंशन, Kotlin क्लाइंट को नॉनब्लॉकिंग एसिंक तरीके से कॉल करने की सुविधा देते हैं. इससे, कॉल करने वाले थ्रेड को ब्लॉक किए बिना, फ़ंक्शन कॉल करने में आसानी होती है. ये प्लैटफ़ॉर्म एपीआई के लिए 1-1 एक्सटेंशन, Jetpack में androidx.core:core-ktx आर्टफ़ैक्ट के तौर पर उपलब्ध कराए जा सकते हैं. हालांकि, ऐसा तब किया जाता है, जब इन्हें स्टैंडर्ड वर्शन के साथ इस्तेमाल किया जा सकता हो और इस्तेमाल करने से पहले, इनकी जांच कर ली गई हो. ज़्यादा जानकारी, रद्द करने से जुड़ी बातों, और सैंपल के लिए, asOutcomeReceiver का दस्तावेज़ देखें.

एसिंक तरीके, किसी नतीजे को वापस लाने वाले तरीके के सिमैंटिक से मेल नहीं खाते. साथ ही, काम पूरा होने पर अपवाद नहीं फेंकते. इसलिए, उन्हें कॉलबैक टाइप के तौर पर OutcomeReceiver का इस्तेमाल नहीं करना चाहिए. इसके बजाय, यहां दिए गए विकल्पों में से कोई एक चुनें.

नए सिंगल ऐब्स्ट्रैक्ट मैथड (एसएएम) टाइप बनाने के बजाय, फ़ंक्शनल इंटरफ़ेस का इस्तेमाल करें

एपीआई लेवल 24 में, java.util.function.* (रेफ़रंस दस्तावेज़) टाइप जोड़े गए हैं. ये Consumer<T> जैसे सामान्य एसएएम इंटरफ़ेस उपलब्ध कराते हैं. इनका इस्तेमाल कॉलबैक लैम्ब्डा के तौर पर किया जा सकता है. कई मामलों में, नए एसएएम इंटरफ़ेस बनाने से टाइप की सुरक्षा या मकसद को कम्यूनिकेट करने के मामले में कम फ़ायदा मिलता है. हालांकि, इससे Android API का दायरा बेवजह बढ़ जाता है.

नए इंटरफ़ेस बनाने के बजाय, इन सामान्य इंटरफ़ेस का इस्तेमाल करें:

एसएएम पैरामीटर की जगह

Kotlin से SAM पैरामीटर का इस्तेमाल करने के लिए, उन्हें आखिर में रखा जाना चाहिए. भले ही, मैथड को अन्य पैरामीटर के साथ ओवरलोड किया जा रहा हो.

public void schedule(Runnable runnable)

public void schedule(int delay, Runnable runnable)

Docs

ये एपीआई के सार्वजनिक दस्तावेज़ों (Javadoc) के बारे में नियम हैं.

सभी सार्वजनिक एपीआई के बारे में जानकारी दी जानी चाहिए

सभी सार्वजनिक एपीआई के लिए, यह बताने वाला दस्तावेज़ होना चाहिए कि डेवलपर एपीआई का इस्तेमाल कैसे करेगा. मान लें कि डेवलपर को ऑटोकंप्लीट सुविधा का इस्तेमाल करके या एपीआई के रेफ़रंस दस्तावेज़ ब्राउज़ करते समय, तरीका मिल गया है. साथ ही, उसके पास आस-पास के एपीआई के बारे में थोड़ी जानकारी है. उदाहरण के लिए, एक ही क्लास.

माटिंग में इस्तेमाल हुए तरीके

तरीके के पैरामीटर और रिटर्न वैल्यू को, @param और @return दस्तावेज़ एनोटेशन का इस्तेमाल करके दस्तावेज़ में शामिल किया जाना चाहिए. Javadoc के मुख्य हिस्से को इस तरह फ़ॉर्मैट करो कि उसके पहले "यह तरीका..." लिखा हो.

अगर किसी तरीके में कोई पैरामीटर नहीं लिया जाता है, कोई खास शर्त नहीं होती है, और वह वही वैल्यू दिखाता है जो तरीके के नाम से पता चलती है, तो @return को हटाया जा सकता है. साथ ही, इस तरह के दस्तावेज़ लिखे जा सकते हैं:

/**
 * Returns the priority of the thread.
 */
@IntRange(from = 1, to = 10)
public int getPriority() { ... }

दस्तावेज़ों में, मिलते-जुलते कॉन्स्टेंट, तरीकों, और अन्य एलिमेंट के लिए, दूसरे दस्तावेज़ों के लिंक होने चाहिए. सिर्फ़ सामान्य टेक्स्ट वाले शब्दों के बजाय, Javadoc टैग (उदाहरण के लिए, @see और {@link foo}) का इस्तेमाल करें.

सोर्स के इस उदाहरण के लिए:

public static final int FOO = 0;
public static final int BAR = 1;

रॉ टेक्स्ट या कोड फ़ॉन्ट का इस्तेमाल न करें:

/**
 * Sets value to one of FOO or <code>BAR</code>.
 *
 * @param value the value being set, one of FOO or BAR
 */
public void setValue(int value) { ... }

इसके बजाय, लिंक का इस्तेमाल करें:

/**
 * Sets value to one of {@link #FOO} or {@link #BAR}.
 *
 * @param value the value being set
 */
public void setValue(@ValueType int value) { ... }

ध्यान दें कि किसी पैरामीटर पर IntDef जैसे एनोटेशन का इस्तेमाल करने से, अनुमति वाले टाइप के बारे में बताने वाला दस्तावेज़ अपने-आप जनरेट हो जाता है.@ValueType IntDef के बारे में ज़्यादा जानने के लिए, एनोटेशन से जुड़ी गाइडेंस देखें.

Javadoc जोड़ते समय, update-api या docs टारगेट चलाएं

@link या @see टैग जोड़ते समय, इस नियम का पालन करना ज़रूरी है. साथ ही, यह भी पक्का करें कि आउटपुट आपकी उम्मीद के मुताबिक हो. Javadoc में गड़बड़ी का मैसेज अक्सर खराब लिंक की वजह से दिखता है. update-api या docs Make टारगेट, इस जांच को पूरा करता है. हालांकि, अगर आपको सिर्फ़ Javadoc में बदलाव करना है और आपको update-api टारगेट चलाने की ज़रूरत नहीं है, तो docs टारगेट का इस्तेमाल करना ज़्यादा तेज़ हो सकता है.

Java वैल्यू में अंतर करने के लिए, {@code foo} का इस्तेमाल करें

Java की वैल्यू, जैसे कि true, false, और null को {@code...} के साथ रैप करें, ताकि उन्हें दस्तावेज़ के टेक्स्ट से अलग किया जा सके.

Kotlin सोर्स में दस्तावेज़ लिखते समय, कोड को बैकटिक में रैप किया जा सकता है. ऐसा मार्कडाउन के लिए किया जाता है.

@param और @return की खास जानकारी, एक वाक्य का हिस्सा होनी चाहिए

पैरामीटर और रिटर्न वैल्यू की खास जानकारी, छोटे अक्षर से शुरू होनी चाहिए. साथ ही, इसमें सिर्फ़ एक वाक्य का हिस्सा होना चाहिए. अगर आपके पास एक वाक्य से ज़्यादा जानकारी है, तो उसे Javadoc के तरीके के मुख्य हिस्से में ले जाएं:

/**
 * @param e The element to be appended to the list. This must not be
 *       null. If the list contains no entries, this element will
 *       be added at the beginning.
 * @return This method returns true on success.
 */

इसे इसमें बदला जाना चाहिए:

/**
 * @param e element to be appended to this list, must be non-{@code null}
 * @return {@code true} on success, {@code false} otherwise
 */

Docs में की गई व्याख्याओं के बारे में जानकारी देना ज़रूरी है

दस्तावेज़ में यह जानकारी दी गई है कि एनोटेशन @hide और @removed को सार्वजनिक एपीआई से क्यों छिपाया गया है. @deprecated एनोटेशन के साथ मार्क किए गए एपीआई एलिमेंट को बदलने के तरीके के बारे में निर्देश शामिल करें.

अपवादों के बारे में बताने के लिए @throws का इस्तेमाल करना

अगर कोई तरीका, जांच किया गया अपवाद देता है, तो IOException के साथ अपवाद को @throws के साथ दस्तावेज़ में शामिल करें. Java क्लाइंट के लिए, Kotlin से सोर्स किए गए एपीआई के लिए, फ़ंक्शन को @Throws से एनोटेट करें.

अगर कोई तरीका, ऐसी बिना जांच की गई गड़बड़ी दिखाता है जिसे ठीक किया जा सकता है, तो उदाहरण के लिए IllegalArgumentException या IllegalStateException, गड़बड़ी के बारे में दस्तावेज़ में जानकारी दें. साथ ही, यह भी बताएं कि गड़बड़ी क्यों हुई. थ्रो किए गए अपवाद से यह भी पता चलना चाहिए कि इसे क्यों थ्रो किया गया था.

कुछ मामलों में, बिना जांच किए गए अपवाद को इंप्लिसिट माना जाता है और उन्हें दस्तावेज़ में शामिल करने की ज़रूरत नहीं होती. जैसे, NullPointerException या IllegalArgumentException, जहां कोई आर्ग्युमेंट, @IntDef या इसी तरह के किसी एनोटेशन से मेल नहीं खाता है. यह एनोटेशन, एपीआई अनुबंध को तरीके के सिग्नेचर में शामिल करता है:

/**
 * ...
 * @throws IOException If it cannot find the schema for {@code toVersion}
 * @throws IllegalStateException If the schema validation fails
 */
public SupportSQLiteDatabase runMigrationsAndValidate(String name, int version,
    boolean validateDroppedTables, Migration... migrations) throws IOException {
  // ...
  if (!dbPath.exists()) {
    throw new IllegalStateException("Cannot find the database file for " + name
        + ". Before calling runMigrations, you must first create the database "
        + "using createDatabase.");
  }
  // ...

या Kotlin में:

/**
 * ...
 * @throws IOException If something goes wrong reading the file, such as a bad
 *                     database header or missing permissions
 */
@Throws(IOException::class)
fun readVersion(databaseFile: File): Int {
  // ...
  val read = input.read(buffer)
    if (read != 4) {
      throw IOException("Bad database header, unable to read 4 bytes at " +
          "offset 60")
    }
  }
  // ...

अगर कोई तरीका, एसिंक्रोनस कोड को लागू करता है, तो उसमें अपवाद हो सकते हैं. ऐसे में, यह ध्यान रखें कि डेवलपर को इन अपवादों के बारे में कैसे पता चलेगा और वह इनका जवाब कैसे देगा. आम तौर पर, इसमें अपवाद को कॉलबैक पर फ़ॉरवर्ड करना और उन्हें पाने वाले तरीके पर थ्रो किए गए अपवादों को दस्तावेज़ में शामिल करना होता है. एसिंक्रोनस अपवादों को @throws के साथ तब तक दस्तावेज़ में शामिल नहीं किया जाना चाहिए, जब तक उन्हें एनोटेट किए गए तरीके से फिर से नहीं फेंका जाता.

दस्तावेज़ के पहले वाक्य के आखिर में पूर्ण विराम लगाएं

Doclava टूल, दस्तावेज़ों को आसानी से पार्स करता है. साथ ही, जैसे ही उसे अवधि (.) के बाद स्पेस दिखता है, वह सिनोप्सिस दस्तावेज़ (पहला वाक्य, जिसका इस्तेमाल क्लास के दस्तावेज़ों के सबसे ऊपर मौजूद खास जानकारी में किया जाता है) को खत्म कर देता है. इससे दो समस्याएं होती हैं:

  • अगर कोई छोटा दस्तावेज़, अवधि के साथ खत्म नहीं होता है और उस सदस्य ने ऐसे दस्तावेज़ इनहेरिट किए हैं जिन्हें टूल ने चुना है, तो खास जानकारी में उन इनहेरिट किए गए दस्तावेज़ों को भी शामिल किया जाता है. उदाहरण के लिए, R.attr दस्तावेज़ में actionBarTabStyle देखें. इसमें, डाइमेंशन की जानकारी को खास जानकारी में जोड़ा गया है.
  • इसी वजह से, पहले वाक्य में "e.g." का इस्तेमाल न करें, क्योंकि Doclava "g." के बाद सिनोप्सिस दस्तावेज़ों को खत्म कर देता है. उदाहरण के लिए, View.java में TEXT_ALIGNMENT_CENTER देखें. ध्यान दें कि Metalava इस गड़बड़ी को अपने-आप ठीक कर देता है. इसके लिए, वह अवधि के बाद एक नॉनब्रेकिंग स्पेस डालता है. हालांकि, पहली बार में ही यह गड़बड़ी न करें.

दस्तावेज़ों को एचटीएमएल में रेंडर करने के लिए फ़ॉर्मैट करना

Javadoc को एचटीएमएल में रेंडर किया जाता है. इसलिए, इन दस्तावेज़ों को इसी फ़ॉर्मैट में तैयार करें:

  • लाइन ब्रेक के लिए, साफ़ तौर पर <p> टैग का इस्तेमाल किया जाना चाहिए. आखिरी </p> टैग न जोड़ें.

  • सूचियों या टेबल को रेंडर करने के लिए, ASCII का इस्तेमाल न करें.

  • बिना क्रम वाली और क्रम वाली सूचियों के लिए, <ul> और <ol> का इस्तेमाल करना चाहिए. हर आइटम की शुरुआत <li> टैग से होनी चाहिए. हालांकि, इसके लिए </li> टैग का इस्तेमाल करना ज़रूरी नहीं है. आखिरी आइटम के बाद, क्लोज़िंग </ul> या </ol> टैग देना ज़रूरी है.

  • टेबल में लाइनों के लिए <table>, <tr>, हेडर के लिए <th>, और सेल के लिए <td> का इस्तेमाल किया जाना चाहिए. सभी टेबल टैग के लिए, मिलते-जुलते क्लोज़िंग टैग की ज़रूरत होती है. किसी भी टैग के लिए, बंद होने की सूचना देने के लिए class="deprecated" का इस्तेमाल किया जा सकता है.

  • इनलाइन कोड फ़ॉन्ट बनाने के लिए, {@code foo} का इस्तेमाल करें.

  • कोड ब्लॉक बनाने के लिए, <pre> का इस्तेमाल करें.

  • <pre> ब्लॉक में मौजूद सभी टेक्स्ट को ब्राउज़र पार्स करता है. इसलिए, ब्रैकेट <> का इस्तेमाल करते समय सावधानी बरतें. &lt; और &gt; एचटीएमएल इकाइयों का इस्तेमाल करके, इनसे बचा जा सकता है.

  • इसके अलावा, अगर आपको अपने कोड स्निपेट में रॉ ब्रैकेट <> छोड़ने हैं, तो समस्या पैदा करने वाले सेक्शन को {@code foo} में रैप करें. उदाहरण के लिए:

    <pre>{@code <manifest>}</pre>
    

एपीआई के रेफ़रंस की स्टाइल गाइड का पालन करें

क्लास की खास जानकारी, तरीके के ब्यौरे, पैरामीटर के ब्यौरे, और अन्य आइटम के लिए एक जैसी स्टाइल का इस्तेमाल करने के लिए, Javadoc टूल के लिए दस्तावेज़ से जुड़ी टिप्पणियां कैसे लिखें में दिए गए, Java भाषा के आधिकारिक दिशा-निर्देशों में दिए गए सुझावों का पालन करें.

Android फ़्रेमवर्क से जुड़े नियम

ये नियम, एपीआई, पैटर्न, और डेटा स्ट्रक्चर के बारे में हैं. ये सिर्फ़ Android फ़्रेमवर्क में बनाए गए एपीआई और व्यवहारों पर लागू होते हैं. उदाहरण के लिए, Bundle या Parcelable.

इंटेंट बिल्डर को create*Intent() पैटर्न का इस्तेमाल करना चाहिए

इरादे के लिए क्रिएटर्स को, createFooIntent() नाम के तरीकों का इस्तेमाल करना चाहिए.

सामान्य मकसद के लिए नए डेटा स्ट्रक्चर बनाने के बजाय, बंडल का इस्तेमाल करें

टाइप की गई वैल्यू मैपिंग के लिए, किसी भी कुंजी को दिखाने के लिए नए सामान्य डेटा स्ट्रक्चर बनाने से बचें. इसके बजाय, Bundle का इस्तेमाल करें.

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

अगर प्लैटफ़ॉर्म डेटा को पढ़ता है, तो Bundle का इस्तेमाल न करें. इसके बजाय, स्ट्रॉन्गली टाइप की गई डेटा क्लास का इस्तेमाल करें.

Parcelable लागू करने वाले क्लास में, public CREATOR फ़ील्ड होना चाहिए

पार्सल किए जा सकने वाले ऑब्जेक्ट के लिए, CREATOR के ज़रिए इन्फ्लेशन का पता चलता है, न कि रॉ कंस्ट्रक्टर के ज़रिए. अगर कोई क्लास Parcelable को लागू करती है, तो उसका CREATOR फ़ील्ड भी सार्वजनिक एपीआई होना चाहिए. साथ ही, Parcel आर्ग्युमेंट लेने वाला क्लास कंस्ट्रक्टर निजी होना चाहिए.

यूज़र इंटरफ़ेस स्ट्रिंग के लिए CharSequence का इस्तेमाल करना

जब किसी स्ट्रिंग को यूज़र इंटरफ़ेस में दिखाया जाता है, तब CharSequence का इस्तेमाल करें, ताकि Spannable इंस्टेंस की अनुमति दी जा सके.

अगर सिर्फ़ कोई ऐसी कुंजी, लेबल या वैल्यू है जो लोगों को नहीं दिखती है, तो String का इस्तेमाल किया जा सकता है.

एनम का इस्तेमाल न करें

IntDef का इस्तेमाल सभी प्लैटफ़ॉर्म एपीआई में enums के बजाय किया जाना चाहिए. साथ ही, इसे अनबंडल्ड और लाइब्रेरी एपीआई में भी इस्तेमाल किया जाना चाहिए. इनम का इस्तेमाल सिर्फ़ तब करें, जब आपको यकीन हो कि नई वैल्यू नहीं जोड़ी जाएंगी.

IntDef के फ़ायदे:

  • समय के साथ वैल्यू जोड़ने की सुविधा चालू करता है
  • रनटाइम में किसी क्लास या ऑब्जेक्ट का इस्तेमाल नहीं किया गया है, सिर्फ़ प्रिमिटिव का इस्तेमाल किया गया है
    • R8 या कोड छोटा करने की सुविधा का इस्तेमाल करके, अनबंडल किए गए लाइब्रेरी एपीआई के लिए इस लागत से बचा जा सकता है. हालांकि, इस ऑप्टिमाइज़ेशन का असर प्लैटफ़ॉर्म एपीआई क्लास पर नहीं पड़ता.

Enum के फ़ायदे

  • Java और Kotlin की भाषा से जुड़ी सुविधा
  • यह कुकी, स्विच और when स्टेटमेंट के इस्तेमाल को पूरी तरह से चालू करती है
    • ध्यान दें - समय के साथ वैल्यू नहीं बदलनी चाहिए, पिछली सूची देखें
  • नाम ऐसा होना चाहिए जिससे यह पता चले कि यह किस तरह की सुविधा है और इसे आसानी से ढूंढा जा सके
  • इससे कंपाइल टाइम की पुष्टि करने की सुविधा चालू होती है
    • उदाहरण के लिए, Kotlin में when स्टेटमेंट, जो वैल्यू दिखाता है
  • यह एक ऐसी क्लास है जो इंटरफ़ेस लागू कर सकती है, स्टैटिक हेल्पर रख सकती है, मेंबर या एक्सटेंशन के तरीके दिखा सकती है, और फ़ील्ड दिखा सकती है.

Android पैकेज लेयरिंग हैरारकी का पालन करना

android.* पैकेज की हैरारकी में एक क्रम होता है. इसमें, निचले लेवल के पैकेज, ऊपरी लेवल के पैकेज पर निर्भर नहीं हो सकते.

Google, अन्य कंपनियों, और उनके प्रॉडक्ट के बारे में जानकारी न दें

Android प्लैटफ़ॉर्म एक ओपन-सोर्स प्रोजेक्ट है. इसका मकसद, वेंडर के हिसाब से काम न करना है. एपीआई सामान्य होना चाहिए. साथ ही, इसका इस्तेमाल सिस्टम इंटिग्रेटर या ज़रूरी अनुमतियां वाले ऐप्लिकेशन भी कर सकते हों.

पार्सल किए जा सकने वाले ऑब्जेक्ट को फ़ाइनल होना चाहिए

प्लैटफ़ॉर्म की ओर से तय की गई Parcelable क्लास हमेशा framework.jar से लोड की जाती हैं. इसलिए, किसी ऐप्लिकेशन के लिए Parcelable के लागू करने के तरीके को बदलने की कोशिश करना अमान्य है.

अगर भेजने वाला ऐप्लिकेशन, Parcelable को बढ़ाता है, तो पाने वाले ऐप्लिकेशन के पास भेजने वाले ऐप्लिकेशन का कस्टम तरीके से लागू किया गया कोड नहीं होगा, ताकि वह Parcelable को अनपैक कर सके. पिछले वर्शन के साथ काम करने की सुविधा के बारे में जानकारी: अगर आपकी क्लास पहले फ़ाइनल नहीं थी, लेकिन उसमें सार्वजनिक तौर पर उपलब्ध कंस्ट्रक्टर नहीं था, तो अब भी उसे final के तौर पर मार्क किया जा सकता है.

सिस्टम प्रोसेस को कॉल करने वाले तरीकों को RemoteException को RuntimeException के तौर पर फिर से थ्रो करना चाहिए

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

अगर आपको पता है कि Binder कॉल का दूसरा हिस्सा सिस्टम प्रोसेस है, तो यह बॉयलरप्लेट कोड सबसे सही तरीका है:

try {
    ...
} catch (RemoteException e) {
    throw e.rethrowFromSystemServer();
}

एपीआई में हुए बदलावों के लिए खास अपवादों को थ्रो करना

एपीआई लेवल के हिसाब से, सार्वजनिक एपीआई के काम करने के तरीके में बदलाव हो सकता है. इससे ऐप्लिकेशन क्रैश हो सकते हैं. उदाहरण के लिए, नई सुरक्षा नीतियों को लागू करने के लिए ऐसा किया जा सकता है.

जब एपीआई को ऐसे अनुरोध के लिए गड़बड़ी का मैसेज दिखाना हो जो पहले मान्य था, तब सामान्य गड़बड़ी के बजाय कोई नई गड़बड़ी दिखाएं. उदाहरण के लिए, SecurityException के बजाय ExportedFlagRequired (और ExportedFlagRequired को SecurityException तक बढ़ाया जा सकता है).

इससे ऐप्लिकेशन डेवलपर और टूल को, एपीआई के व्यवहार में हुए बदलावों का पता लगाने में मदद मिलेगी.

क्लोन करने के बजाय कॉपी कंस्ट्रक्टर लागू करना

Java clone() तरीके का इस्तेमाल न करने का सुझाव दिया जाता है. ऐसा इसलिए, क्योंकि Object क्लास में एपीआई अनुबंध मौजूद नहीं होते. साथ ही, clone() का इस्तेमाल करने वाली क्लास को बढ़ाने में भी समस्याएं आती हैं. इसके बजाय, एक ऐसे कॉपी कंस्ट्रक्टर का इस्तेमाल करें जो एक ही टाइप का ऑब्जेक्ट लेता हो.

/**
 * Constructs a shallow copy of {@code other}.
 */
public Foo(Foo other)

जिन क्लास को बनाने के लिए बिल्डर पर भरोसा करना पड़ता है उन्हें बिल्डर कॉपी कंस्ट्रक्टर जोड़ना चाहिए, ताकि कॉपी में बदलाव किया जा सके.

public class Foo {
    public static final class Builder {
        /**
         * Constructs a Foo builder using data from {@code other}.
         */
        public Builder(Foo other)

FileDescriptor के बजाय ParcelFileDescriptor का इस्तेमाल करना

java.io.FileDescriptor ऑब्जेक्ट में स्वामित्व की जानकारी सही तरीके से नहीं दी गई है. इसकी वजह से, बंद करने के बाद इस्तेमाल से जुड़ी गड़बड़ियां हो सकती हैं. इसके बजाय, एपीआई को ParcelFileDescriptor इंस्टेंस स्वीकार करने चाहिए या उन्हें वापस भेजना चाहिए. लेगसी कोड, ज़रूरत पड़ने पर dup() या getFileDescriptor() का इस्तेमाल करके, PFD और FD के बीच फ़ाइल को बदल सकता है.

विषम साइज़ वाली संख्यात्मक वैल्यू का इस्तेमाल न करें

short या byte वैल्यू का सीधे तौर पर इस्तेमाल न करें, क्योंकि इनसे अक्सर यह तय होता है कि आने वाले समय में एपीआई को कैसे बेहतर बनाया जा सकता है.

BitSet का इस्तेमाल न करें

java.util.BitSet को लागू करना आसान है, लेकिन यह सार्वजनिक एपीआई के लिए सही नहीं है. इसमें बदलाव किया जा सकता है. साथ ही, इसमें बार-बार कॉल किए जाने वाले तरीकों के लिए मेमोरी एलोकेट करने की ज़रूरत होती है. इसके अलावा, यह नहीं बताता कि हर बिट का मतलब क्या है.

बेहतर परफ़ॉर्मेंस के लिए, @IntDef के साथ int या long का इस्तेमाल करें. कम परफ़ॉर्मेंस वाले उदाहरणों के लिए, Set<EnumType> का इस्तेमाल करें. रॉ बाइनरी डेटा के लिए, byte[] का इस्तेमाल करें.

android.net.Uri को प्राथमिकता दें

android.net.Uri, Android API में यूआरआई के लिए पसंदीदा एनकैप्सुलेशन है.

java.net.URI का इस्तेमाल न करें, क्योंकि यह यूआरआई को पार्स करने में बहुत ज़्यादा सख्त है. साथ ही, java.net.URL का इस्तेमाल कभी न करें, क्योंकि समानता की इसकी परिभाषा बहुत खराब है.

@IntDef, @LongDef या @StringDef के तौर पर मार्क किए गए एनोटेशन छिपाएं

@IntDef, @LongDef या @StringDef के तौर पर मार्क किए गए एनोटेशन, मान्य कॉन्स्टेंट का एक सेट दिखाते हैं. इन्हें एपीआई को पास किया जा सकता है. हालांकि, जब इन्हें एपीआई के तौर पर एक्सपोर्ट किया जाता है, तो कंपाइलर कॉन्स्टेंट को इनलाइन करता है. साथ ही, एनोटेशन के एपीआई स्टब (प्लैटफ़ॉर्म के लिए) या JAR (लाइब्रेरी के लिए) में सिर्फ़ (अब काम न आने वाली) वैल्यू बनी रहती हैं.

इसलिए, इन एनोटेशन के इस्तेमाल को प्लैटफ़ॉर्म में @hide docs एनोटेशन या लाइब्रेरी में @RestrictTo.Scope.LIBRARY) code एनोटेशन के साथ मार्क किया जाना चाहिए. इन दोनों मामलों में, उन्हें @Retention(RetentionPolicy.SOURCE) के तौर पर मार्क किया जाना चाहिए, ताकि वे एपीआई स्टब या JAR में न दिखें.

@RestrictTo(RestrictTo.Scope.LIBRARY)
@Retention(RetentionPolicy.SOURCE)
@IntDef({
  STREAM_TYPE_FULL_IMAGE_DATA,
  STREAM_TYPE_EXIF_DATA_ONLY,
})
public @interface ExifStreamType {}

प्लेटफ़ॉर्म एसडीके और लाइब्रेरी एएआर बनाते समय, एक टूल एनोटेशन निकालता है और उन्हें कंपाइल किए गए सोर्स से अलग बंडल करता है. Android Studio, बंडल किए गए इस फ़ॉर्मैट को पढ़ता है और टाइप की परिभाषाओं को लागू करता है.

सेटिंग प्रोवाइडर की नई कुंजियां न जोड़ें

Settings.Global, Settings.System या Settings.Secure से नई कुंजियां न दिखाएं.

इसके बजाय, किसी काम की क्लास में सही तरीके से काम करने वाला getter और setter Java API जोड़ें. आम तौर पर, यह "manager" क्लास होती है. ज़रूरत के मुताबिक, क्लाइंट को बदलावों के बारे में सूचना देने के लिए, लिसनर मैकेनिज़्म या ब्रॉडकास्ट जोड़ें.

SettingsProvider सेटिंग में, गेटर/सेटर की तुलना में कई समस्याएं हैं:

  • टाइप सेफ़्टी नहीं होती.
  • डिफ़ॉल्ट वैल्यू देने का कोई एक तरीका नहीं है.
  • अनुमतियों को पसंद के मुताबिक बनाने का कोई सही तरीका नहीं है.
    • उदाहरण के लिए, कस्टम अनुमति की मदद से अपनी सेटिंग को सुरक्षित नहीं किया जा सकता.
  • कस्टम लॉजिक को सही तरीके से जोड़ने का कोई तरीका नहीं है.
    • उदाहरण के लिए, सेटिंग B की वैल्यू के आधार पर सेटिंग A की वैल्यू को नहीं बदला जा सकता.

उदाहरण: Settings.Secure.LOCATION_MODE काफ़ी समय से मौजूद है. हालांकि, लोकेशन टीम ने इसे बंद कर दिया है. अब इसकी जगह, सही Java API LocationManager.isLocationEnabled() और MODE_CHANGED_ACTION ब्रॉडकास्ट का इस्तेमाल किया जाता है. इससे टीम को ज़्यादा सुविधा मिलती है. साथ ही, एपीआई के सिमैंटिक अब ज़्यादा साफ़ तौर पर समझ में आते हैं.

Activity और AsyncTask को एक्सटेंड न करें

AsyncTask, लागू करने से जुड़ी जानकारी है. इसके बजाय, लिसनर या androidx में ListenableFuture एपीआई को दिखाएं.

Activity सबक्लास नहीं बनाई जा सकतीं. आपकी सुविधा के लिए गतिविधि को बढ़ाने से, यह उन अन्य सुविधाओं के साथ काम नहीं करती जिनके लिए उपयोगकर्ताओं को एक ही काम करना पड़ता है. इसके बजाय, कंपोज़िशन का इस्तेमाल करें. इसके लिए, LifecycleObserver जैसे टूल का इस्तेमाल करें.

कॉन्टेक्स्ट के getUser() का इस्तेमाल करना

Context से जुड़ी क्लास, जैसे कि Context.getSystemService() से मिली कोई भी चीज़, को Context से जुड़े उपयोगकर्ता का इस्तेमाल करना चाहिए. इसके बजाय, उन सदस्यों को दिखाना चाहिए जो खास उपयोगकर्ताओं को टारगेट करते हैं.

class FooManager {
  Context mContext;

  void fooBar() {
    mIFooBar.fooBarForUser(mContext.getUser());
  }
}
class FooManager {
  Context mContext;

  Foobar getFoobar() {
    // Bad: doesn't appy mContext.getUserId().
    mIFooBar.fooBarForUser(Process.myUserHandle());
  }

  Foobar getFoobar() {
    // Also bad: doesn't appy mContext.getUserId().
    mIFooBar.fooBar();
  }

  Foobar getFoobarForUser(UserHandle user) {
    mIFooBar.fooBarForUser(user);
  }
}

अपवाद: कोई तरीका, उपयोगकर्ता के तर्क को तब स्वीकार कर सकता है, जब वह ऐसी वैल्यू स्वीकार करता हो जो किसी एक उपयोगकर्ता को नहीं दिखाती हैं. जैसे, UserHandle.ALL.

सामान्य पूर्णांकों के बजाय UserHandle का इस्तेमाल करना

टाइप की सुरक्षा देने और उपयोगकर्ता आईडी को यूआईडी के साथ मिलाने से बचने के लिए, UserHandle का इस्तेमाल करना बेहतर होता है.

Foobar getFoobarForUser(UserHandle user);
Foobar getFoobarForUser(int userId);

जहां ज़रूरी हो वहां, यूज़र आईडी को दिखाने वाले int को @UserIdInt के साथ एनोटेट किया जाना चाहिए.

Foobar getFoobarForUser(@UserIdInt int user);

ब्रॉडकास्ट इंटेंट के लिए, लिसनर या कॉलबैक को प्राथमिकता देना

ब्रॉडकास्ट इंटेंट बहुत काम के होते हैं. हालांकि, इनकी वजह से ऐसे व्यवहार सामने आए हैं जिनसे सिस्टम की परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. इसलिए, नए ब्रॉडकास्ट इंटेंट को सोच-समझकर जोड़ना चाहिए.

यहां कुछ ऐसी समस्याएं बताई गई हैं जिनकी वजह से, हम ब्रॉडकास्ट करने के नए इंटेंट को इस्तेमाल करने का सुझाव नहीं देते:

  • FLAG_RECEIVER_REGISTERED_ONLY फ़्लैग के बिना ब्रॉडकास्ट भेजने पर, ये ऐसे सभी ऐप्लिकेशन को ज़बरदस्ती शुरू कर देते हैं जो पहले से नहीं चल रहे हैं. हालांकि, ऐसा कभी-कभी जान-बूझकर किया जाता है. इससे कई ऐप्लिकेशन एक साथ शुरू हो जाते हैं और सिस्टम की परफ़ॉर्मेंस पर बुरा असर पड़ता है. हमारा सुझाव है कि आप अन्य रणनीतियों, जैसे कि JobScheduler का इस्तेमाल करें. इससे अलग-अलग ज़रूरी शर्तें पूरी होने पर, बेहतर तरीके से तालमेल बिठाया जा सकेगा.

  • ब्रॉडकास्ट भेजते समय, ऐप्लिकेशन को भेजे गए कॉन्टेंट को फ़िल्टर करने या उसमें बदलाव करने की सुविधा सीमित होती है. इससे, आने वाले समय में निजता से जुड़ी समस्याओं का जवाब देना मुश्किल हो जाता है. साथ ही, डेटा पाने वाले ऐप्लिकेशन के टारगेट एसडीके के आधार पर व्यवहार में बदलाव करना भी मुश्किल हो जाता है.

  • ब्रॉडकास्ट करने के लिए इस्तेमाल की जाने वाली कतारें, शेयर की गई एक संसाधन होती हैं. इसलिए, इन पर ज़्यादा लोड पड़ सकता है. साथ ही, हो सकता है कि आपका इवेंट समय पर डिलीवर न हो. हमने कई ब्रॉडकास्ट कतारों में, एंड-टू-एंड लेटेन्सी को 10 मिनट या उससे ज़्यादा देखा है.

इन वजहों से, हम नई सुविधाओं को ब्रॉडकास्ट इंटेंट के बजाय, लिसनर या कॉलबैक या JobScheduler जैसी अन्य सुविधाओं का इस्तेमाल करने के लिए प्रोत्साहित करते हैं.

अगर ब्रॉडकास्ट इंटेंट अब भी सबसे सही डिज़ाइन है, तो यहां कुछ ऐसे सबसे सही तरीके दिए गए हैं जिन पर ध्यान दिया जाना चाहिए:

  • अगर हो सके, तो Intent.FLAG_RECEIVER_REGISTERED_ONLY का इस्तेमाल करके, ब्रॉडकास्ट को सिर्फ़ उन ऐप्लिकेशन तक सीमित करें जो पहले से चल रहे हैं. उदाहरण के लिए, ACTION_SCREEN_ON इस डिज़ाइन का इस्तेमाल करता है, ताकि ऐप्लिकेशन को चालू होने से रोका जा सके.
  • अगर हो सके, तो Intent.setPackage() या Intent.setComponent() का इस्तेमाल करके, ब्रॉडकास्ट को किसी खास ऐप्लिकेशन पर टारगेट करें. उदाहरण के लिए, ACTION_MEDIA_BUTTON इस डिज़ाइन का इस्तेमाल, मौजूदा ऐप्लिकेशन में मीडिया चलाने के कंट्रोल पर फ़ोकस करने के लिए करता है.
  • अगर हो सके, तो अपने ब्रॉडकास्ट को <protected-broadcast> के तौर पर तय करें, ताकि नुकसान पहुंचाने वाले ऐप्लिकेशन को ओएस के तौर पर काम करने से रोका जा सके.

सिस्टम से जुड़ी डेवलपर सेवाओं में इंटेंट

डेवलपर जिन सेवाओं को बढ़ाना चाहता है और जो सिस्टम से जुड़ी हैं, जैसे कि NotificationListenerService जैसी ऐब्स्ट्रैक्ट सेवाएं, वे सिस्टम से Intent कार्रवाई का जवाब दे सकती हैं. ऐसी सेवाओं को ये शर्तें पूरी करनी चाहिए:

  1. उस क्लास पर SERVICE_INTERFACE स्ट्रिंग कॉन्स्टेंट तय करें जिसमें सेवा का पूरी तरह क्वालिफ़ाइड क्लास नेम शामिल हो. इस कॉन्स्टेंट को @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) के साथ एनोटेट करना ज़रूरी है.
  2. क्लास का ऐसा दस्तावेज़ जिसमें डेवलपर को अपने AndroidManifest.xml में <intent-filter> जोड़ना होगा, ताकि उसे प्लैटफ़ॉर्म से इंटेंट मिल सकें.
  3. सिस्टम-लेवल की अनुमति जोड़ने पर विचार करें, ताकि खराब ऐप्लिकेशन, डेवलपर सेवाओं को Intent न भेज सकें.

Kotlin-Java इंटरऑप

दिशा-निर्देशों की पूरी सूची के लिए, Android की आधिकारिक Kotlin-Java इंटरऑप गाइड देखें. खोज में दिखने की संभावना बढ़ाने के लिए, इस गाइड में कुछ दिशा-निर्देश कॉपी किए गए हैं.

एपीआई दिखने की स्थिति

Kotlin के कुछ एपीआई, जैसे कि suspend funs, Java डेवलपर के लिए नहीं बनाए गए हैं. हालांकि, @JvmSynthetic का इस्तेमाल करके, भाषा के हिसाब से एपीआई की उपलब्धता को कंट्रोल करने की कोशिश न करें. ऐसा इसलिए, क्योंकि इससे डीबगर में एपीआई को दिखाने के तरीके पर साइड इफ़ेक्ट पड़ता है. इससे डीबग करना ज़्यादा मुश्किल हो जाता है.

ज़्यादा जानकारी के लिए, Kotlin-Java इंटरऑप गाइड या एसिंक गाइड देखें.

कंपैनियन ऑब्जेक्ट

Kotlin, स्टैटिक मेंबर को दिखाने के लिए companion object का इस्तेमाल करता है. कुछ मामलों में, ये Companion नाम की इनर क्लास पर Java से दिखेंगे, न कि कंटेनिंग क्लास पर. Companion क्लास, एपीआई टेक्स्ट फ़ाइलों में खाली क्लास के तौर पर दिख सकती हैं. यह सुविधा ठीक से काम कर रही है.

Java के साथ ज़्यादा से ज़्यादा कंपैटिबिलिटी के लिए, कंपैनियन ऑब्जेक्ट के नॉन-कॉन्स्टेंट फ़ील्ड को @JvmField और पब्लिक फ़ंक्शन को @JvmStatic के साथ एनोटेट करें, ताकि उन्हें सीधे तौर पर कंटेनिंग क्लास पर दिखाया जा सके.

companion object {
  @JvmField val BIG_INTEGER_ONE = BigInteger.ONE
  @JvmStatic fun fromPointF(pointf: PointF) {
    /* ... */
  }
}

Android प्लैटफ़ॉर्म एपीआई का डेवलपमेंट

इस सेक्शन में, मौजूदा Android API में किए जा सकने वाले बदलावों के बारे में नीतियों के बारे में बताया गया है. साथ ही, यह भी बताया गया है कि मौजूदा ऐप्लिकेशन और कोडबेस के साथ ज़्यादा से ज़्यादा कंपैटिबिलिटी के लिए, आपको उन बदलावों को कैसे लागू करना चाहिए.

बाइनरी को नुकसान पहुंचाने वाले बदलाव

सार्वजनिक तौर पर उपलब्ध एपीआई के फ़ाइनल वर्शन में, बाइनरी को तोड़ने वाले बदलाव न करें. इस तरह के बदलावों से, make update-api चलाते समय आम तौर पर गड़बड़ियां होती हैं. हालांकि, ऐसे कुछ मामले हो सकते हैं जिनमें Metalava का एपीआई चेक गड़बड़ियों का पता न लगा पाए. अगर आपको कोई शंका है, तो Eclipse Foundation की Evolving Java-based APIs गाइड देखें. इसमें इस बारे में पूरी जानकारी दी गई है कि Java में किस तरह के एपीआई बदलाव काम करते हैं. छिपे हुए (उदाहरण के लिए, सिस्टम) एपीआई में बाइनरी-ब्रेकिंग बदलावों के लिए, बंद करना/बदलना साइकल का पालन करना चाहिए.

सोर्स में बड़े बदलाव

हम सोर्स कोड में ऐसे बदलाव करने से मना करते हैं जिनसे सोर्स कोड में गड़बड़ी होती है. भले ही, वे बाइनरी कोड में गड़बड़ी न करते हों. बाइनरी के साथ काम करने वाले, लेकिन सोर्स कोड में बदलाव करने वाले बदलाव का एक उदाहरण, किसी मौजूदा क्लास में जेनेरिक जोड़ना है. यह बाइनरी के साथ काम करता है, लेकिन इनहेरिटेंस या अस्पष्ट रेफ़रंस की वजह से कंपाइल करने से जुड़ी गड़बड़ियां हो सकती हैं. सोर्स को तोड़ने वाले बदलावों से, make update-api चलाते समय गड़बड़ियां नहीं दिखेंगी. इसलिए, आपको मौजूदा एपीआई सिग्नेचर में किए गए बदलावों के असर को समझना होगा.

कुछ मामलों में, डेवलपर के अनुभव को बेहतर बनाने या कोड को सही करने के लिए, सोर्स कोड में बड़े बदलाव करना ज़रूरी हो जाता है. उदाहरण के लिए, Java सोर्स में nullability एनोटेशन जोड़ने से, Kotlin कोड के साथ इंटरऑपरेबिलिटी बेहतर होती है और गड़बड़ियों की संभावना कम हो जाती है. हालांकि, इसके लिए अक्सर सोर्स कोड में बदलाव करने पड़ते हैं. कभी-कभी, ये बदलाव काफ़ी बड़े होते हैं.

निजी एपीआई में हुए बदलाव

@TestApi के साथ एनोटेट किए गए एपीआई को कभी भी बदला जा सकता है.

आपको @SystemApi के साथ एनोटेट किए गए एपीआई को तीन साल तक सेव करके रखना होगा. आपको इस शेड्यूल के मुताबिक, सिस्टम एपीआई को हटाना होगा या उसमें बदलाव करना होगा:

  • API y - Added
  • API y+1 - समर्थन रोकना
    • कोड को @Deprecated से मार्क करें.
    • बदलाव जोड़ें और @deprecated docs एनोटेशन का इस्तेमाल करके, बंद किए गए कोड के Javadoc में बदलाव को लिंक करें.
    • डेवलपमेंट साइकल के दौरान, इंटरनल उपयोगकर्ताओं के लिए बग फ़ाइल करें. इसमें उन्हें बताएं कि एपीआई बंद किया जा रहा है. इससे यह पुष्टि करने में मदद मिलती है कि एपीआई बदलने से जुड़ी ज़रूरी शर्तें पूरी हो गई हैं.
  • एपीआई y+2 - सॉफ़्ट रिमूवल
    • कोड को @removed से मार्क करें.
    • ज़रूरी नहीं है कि रिलीज़ के लिए, SDK टूल के मौजूदा लेवल को टारगेट करने वाले ऐप्लिकेशन के लिए, थ्रो या नो-ऑप करें.
  • एपीआई y+3 - पूरी तरह से हटाना
    • सोर्स ट्री से कोड को पूरी तरह से हटा दें.

बंद की गई सेवाएं/सुविधाएं

हम एपीआई में बदलाव को बंद करने की प्रोसेस को एपीआई में बदलाव मानते हैं. यह बदलाव, मेजर रिलीज़ (जैसे कि अक्षर) में हो सकता है. एपीआई को बंद करते समय, @Deprecated सोर्स एनोटेशन और @deprecated <summary> दस्तावेज़ एनोटेशन का एक साथ इस्तेमाल करें. आपकी खास जानकारी में माइग्रेशन की रणनीति शामिल होनी चाहिए. इस रणनीति में, किसी दूसरे एपीआई का लिंक दिया जा सकता है या यह बताया जा सकता है कि आपको इस एपीआई का इस्तेमाल क्यों नहीं करना चाहिए:

/**
 * Simple version of ...
 *
 * @deprecated Use the {@link androidx.fragment.app.DialogFragment}
 *             class with {@link androidx.fragment.app.FragmentManager}
 *             instead.
 */
@Deprecated
public final void showDialog(int id)

आपको एक्सएमएल में तय किए गए और Java में दिखाए गए एपीआई को भी बंद करना होगा. इनमें android.R क्लास में दिखाए गए एट्रिब्यूट और स्टाइल की जा सकने वाली प्रॉपर्टी शामिल हैं. साथ ही, आपको इसकी खास जानकारी भी देनी होगी:

<!-- Attribute whether the accessibility service ...
     {@deprecated Not used by the framework}
 -->
<attr name="canRequestEnhancedWebAccessibility" format="boolean" />

किसी एपीआई को कब बंद किया जाता है

एपीआई को बंद करने की सुविधा, नए कोड में एपीआई के इस्तेमाल को रोकने के लिए सबसे ज़्यादा काम आती है.

हम यह भी ज़रूरी मानते हैं कि एपीआई को @deprecated के तौर पर मार्क किया जाए. हालांकि, इससे डेवलपर को उस एपीआई से माइग्रेट करने के लिए कोई खास वजह नहीं मिलती जिसका वे पहले से इस्तेमाल कर रहे हैं.@removed

किसी एपीआई को बंद करने से पहले, डेवलपर पर पड़ने वाले असर के बारे में सोचें. किसी एपीआई को बंद करने पर ये असर पड़ते हैं:

  • javac, कंपाइल करने के दौरान चेतावनी दिखाता है.
    • डिप्रेकेट किए गए एपीआई के इस्तेमाल से जुड़ी चेतावनियों को वैश्विक स्तर पर या बेसलाइन के तौर पर नहीं हटाया जा सकता. इसलिए, -Werror का इस्तेमाल करने वाले डेवलपर को, डिप्रिकेट किए गए एपीआई के हर इस्तेमाल को ठीक करना होगा या हटाना होगा. इसके बाद ही, वे अपने कंपाइल एसडीके के वर्शन को अपडेट कर पाएंगे.
    • बंद की जा चुकी क्लास इंपोर्ट करने पर, बंद होने की सूचनाओं को नहीं हटाया जा सकता. इसलिए, डेवलपर को हर बार, बंद की गई क्लास के लिए पूरी तरह से क्वालिफ़ाइड क्लास का नाम इनलाइन करना होगा. इसके बाद ही, वे कंपाइल एसडीके टूल के वर्शन को अपडेट कर पाएंगे.
  • d.android.com के दस्तावेज़ में, बंद होने की सूचना दी गई है.
  • Android Studio जैसे आईडीई, एपीआई इस्तेमाल करने वाली साइट पर चेतावनी दिखाते हैं.
  • ऐसा हो सकता है कि आईडीई, ऑटोकंप्लीट सुविधा के लिए एपीआई को डाउन-रैंक कर दें या उसे छिपा दें.

इस वजह से, एपीआई को बंद करने से, कोड की सेहत (-Werror का इस्तेमाल करने वाले) के बारे में सबसे ज़्यादा चिंतित डेवलपर, नए एसडीके को अपनाने से हिचकिचा सकते हैं. जिन डेवलपर को अपने मौजूदा कोड में मिली चेतावनियों से कोई फ़र्क़ नहीं पड़ता वे शायद, बंद की जा रही सुविधाओं को पूरी तरह से अनदेखा कर दें.

अगर कोई एसडीके कई सुविधाओं को बंद कर देता है, तो इन दोनों ही मामलों में समस्या बढ़ जाती है.

इस वजह से, हमारा सुझाव है कि एपीआई को सिर्फ़ इन मामलों में बंद करें:

  • हम आने वाले समय में, एपीआई को @remove करने का प्लान बना रहे हैं.
  • एपीआई का इस्तेमाल करने से, गलत या अनिश्चित व्यवहार होता है. इसे हम कंपैटिबिलिटी को तोड़े बिना ठीक नहीं कर सकते.

जब किसी एपीआई का इस्तेमाल बंद कर दिया जाता है और उसे नए एपीआई से बदल दिया जाता है, तो हम ज़ोरदार तरीके से सुझाव देते हैं कि आप Jetpack लाइब्रेरी में, उससे मिलता-जुलता कंपैटिबिलिटी एपीआई जोड़ें. जैसे, androidx.core. इससे पुराने और नए, दोनों तरह के डिवाइसों के लिए सहायता देना आसान हो जाता है.

हम ऐसे एपीआई को बंद करने का सुझाव नहीं देते हैं जो मौजूदा और आने वाले वर्शन में ठीक से काम करते हैं:

/**
 * ...
 * @deprecated Use {@link #doThing(int, Bundle)} instead.
 */
@Deprecated
public void doThing(int action) {
  ...
}

public void doThing(int action, @Nullable Bundle extras) {
  ...
}

एपीआई को बंद करने का फ़ैसला तब लिया जाता है, जब वे अपने दस्तावेज़ में बताए गए तरीके से काम नहीं कर पाते. जैसे:

/**
 * ...
 * @deprecated No longer displayed in the status bar as of API 21.
 */
@Deprecated
public RemoteViews tickerView;

बंद किए गए एपीआई में बदलाव

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

आने वाले समय में रिलीज़ होने वाले वर्शन में, बंद किए गए एपीआई के सर्फ़ेस को बड़ा न करें. किसी ऐसे एपीआई में, लिंटिंग से जुड़ी सही जानकारी वाले एनोटेशन (उदाहरण के लिए, @Nullable) जोड़े जा सकते हैं जिसे अब इस्तेमाल नहीं किया जा सकता. हालांकि, नए एपीआई नहीं जोड़े जाने चाहिए.

नए एपीआई को बहिष्कृत के तौर पर न जोड़ें. अगर प्रीरिलीज़ साइकल के दौरान कोई एपीआई जोड़ा गया था और बाद में उसे बंद कर दिया गया था, तो एपीआई को फ़ाइनल करने से पहले आपको उसे हटाना होगा. ऐसा इसलिए, क्योंकि बंद किए गए एपीआई, सार्वजनिक एपीआई के तौर पर उपलब्ध नहीं होते.

सॉफ़्ट रिमूवल

सॉफ़्ट रिमूवल, सोर्स कोड में किया गया ऐसा बदलाव है जिससे सोर्स कोड काम करना बंद कर देता है. इसलिए, आपको सार्वजनिक एपीआई में इसका इस्तेमाल नहीं करना चाहिए. हालांकि, अगर एपीआई काउंसिल इसकी अनुमति देती है, तो इसका इस्तेमाल किया जा सकता है. सिस्टम एपीआई के लिए, आपको एपीआई को पूरी तरह से हटाने से पहले, मुख्य रिलीज़ की अवधि के लिए एपीआई को बंद करना होगा. एपीआई के सभी दस्तावेज़ों के रेफ़रंस हटाएं. साथ ही, एपीआई को कुछ समय के लिए हटाने पर, @removed <summary> दस्तावेज़ के एनोटेशन का इस्तेमाल करें. आपकी खास जानकारी में, ऐप्लिकेशन हटाने की वजह ज़रूर शामिल होनी चाहिए. इसमें माइग्रेशन की रणनीति भी शामिल की जा सकती है. इसके बारे में हमने बंद करना लेख में बताया है.

सॉफ़्ट तरीके से हटाए गए एपीआई के व्यवहार को पहले जैसा रखा जा सकता है. हालांकि, इससे भी ज़्यादा ज़रूरी यह है कि इसे इस तरह से बनाए रखा जाए कि एपीआई को कॉल करने पर, मौजूदा कॉलर क्रैश न हों. कुछ मामलों में, इसका मतलब है कि आपको अपने ऐप्लिकेशन के व्यवहार को बनाए रखना होगा.

टेस्ट कवरेज को बनाए रखना ज़रूरी है. हालांकि, व्यवहार में होने वाले बदलावों को ध्यान में रखते हुए, टेस्ट के कॉन्टेंट में बदलाव करना पड़ सकता है. टेस्ट से यह पुष्टि होनी चाहिए कि रन टाइम में, मौजूदा कॉलर क्रैश नहीं होते हैं. सॉफ़्ट-हटाए गए एपीआई के व्यवहार को पहले जैसा ही रखा जा सकता है. हालांकि, इससे भी ज़्यादा ज़रूरी यह है कि आप इसे इस तरह से बनाए रखें कि एपीआई को कॉल करने पर, मौजूदा कॉलर क्रैश न हों. कुछ मामलों में, इसका मतलब है कि व्यवहार को बनाए रखना.

आपको टेस्ट कवरेज को बनाए रखना ज़रूरी है. हालांकि, व्यवहार में बदलावों को शामिल करने के लिए, टेस्ट के कॉन्टेंट में बदलाव करना पड़ सकता है. टेस्ट से यह पुष्टि होनी चाहिए कि रन टाइम में, मौजूदा कॉलर क्रैश नहीं होते हैं.

तकनीकी तौर पर, हम एपीआई को SDK स्टब JAR और कंपाइल-टाइम क्लासपाथ से हटा देते हैं. इसके लिए, @remove Javadoc एनोटेशन का इस्तेमाल किया जाता है. हालांकि, यह अब भी रन-टाइम क्लासपाथ पर मौजूद होता है. यह @hide एपीआई की तरह ही होता है:

/**
 * Ringer volume. This is ...
 *
 * @removed Not functional since API 2.
 */
public static final String VOLUME_RING = ...

ऐप्लिकेशन डेवलपर के नज़रिए से, एपीआई अब अपने-आप पूरा होने वाले फ़ॉर्म में नहीं दिखता. साथ ही, एपीआई का रेफ़रंस देने वाला सोर्स कोड तब कंपाइल नहीं होगा, जब compileSdk उस एसडीके के बराबर या उससे बाद का हो जिससे एपीआई को हटाया गया था. हालांकि, सोर्स कोड पहले के एसडीके के साथ कंपाइल होता रहेगा. साथ ही, एपीआई का रेफ़रंस देने वाले बाइनरी काम करते रहेंगे.

एपीआई की कुछ कैटगरी को सॉफ्ट रिमूव नहीं किया जाना चाहिए. आपको एपीआई की कुछ कैटगरी को सॉफ्ट रिमूव नहीं करना चाहिए.

ऐब्स्ट्रैक्ट तरीके

आपको उन क्लास से ऐब्स्ट्रैक्ट तरीके नहीं हटाने चाहिए जिन्हें डेवलपर एक्सटेंड कर सकते हैं. ऐसा करने से, डेवलपर सभी एसडीके लेवल पर क्लास को सही तरीके से नहीं बढ़ा पाते.

कुछ मामलों में, डेवलपर के लिए किसी क्लास को एक्सटेंड करना कभी भी और कभी नहीं मुमकिन होता है. ऐसे में, अबस्ट्रैक्ट तरीके से हटाए गए तरीकों को फिर से जोड़ा जा सकता है.

पूरी तरह से हटाना

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

ऐसे एनोटेशन जिनका सुझाव नहीं दिया जाता

हम @Discouraged एनोटेशन का इस्तेमाल यह बताने के लिए करते हैं कि हम ज़्यादातर (>95%) मामलों में किसी एपीआई का सुझाव नहीं देते हैं. इस्तेमाल न करने का सुझाव दिए गए एपीआई, बंद किए गए एपीआई से अलग होते हैं. ऐसा इसलिए, क्योंकि इस्तेमाल के कुछ खास मामलों में, इन एपीआई का इस्तेमाल करना ज़रूरी होता है. इसलिए, इन्हें बंद नहीं किया जाता. किसी एपीआई को 'इस्तेमाल न करें' के तौर पर मार्क करते समय, आपको इसकी वजह और कोई वैकल्पिक समाधान बताना होगा:

@Discouraged(message = "Use of this function is discouraged because resource
                        reflection makes it harder to perform build
                        optimizations and compile-time verification of code. It
                        is much more efficient to retrieve resources by
                        identifier (such as `R.foo.bar`) than by name (such as
                        `getIdentifier()`)")
public int getIdentifier(String name, String defType, String defPackage) {
    return mResourcesImpl.getIdentifier(name, defType, defPackage);
}

आपको नए एपीआई नहीं जोड़ने चाहिए, क्योंकि ऐसा करने से रोकने के लिए कहा गया है.

मौजूदा एपीआई के काम करने के तरीके में बदलाव

कुछ मामलों में, आपको किसी मौजूदा एपीआई को लागू करने के तरीके में बदलाव करना पड़ सकता है. उदाहरण के लिए, Android 7.0 में हमने DropBoxManager को बेहतर बनाया है, ताकि डेवलपर को यह साफ़ तौर पर बताया जा सके कि जब वे ऐसे इवेंट पोस्ट करने की कोशिश करते हैं जो Binder पर भेजने के लिए बहुत बड़े हैं.

हालांकि, हमारा सुझाव है कि पुराने ऐप्लिकेशन के लिए, सुरक्षित तरीके से काम करने की सुविधा को चालू रखें, ताकि मौजूदा ऐप्लिकेशन को कोई समस्या न हो. हम ऐप्लिकेशन के ApplicationInfo.targetSdkVersion के आधार पर, व्यवहार में होने वाले इन बदलावों को सुरक्षित रखते थे. हालांकि, हमने हाल ही में App Compatibility Framework का इस्तेमाल करना ज़रूरी कर दिया है. यहां इस नए फ़्रेमवर्क का इस्तेमाल करके, व्यवहार में बदलाव लागू करने का एक उदाहरण दिया गया है:

import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;

public class MyClass {
  @ChangeId
  // This means the change will be enabled for target SDK R and higher.
  @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.R)
  // Use a bug number as the value, provide extra detail in the bug.
  // FOO_NOW_DOES_X will be the change name, and 123456789 the change ID.
  static final long FOO_NOW_DOES_X = 123456789L;

  public void doFoo() {
    if (CompatChanges.isChangeEnabled(FOO_NOW_DOES_X)) {
      // do the new thing
    } else {
      // do the old thing
    }
  }
}

ऐप्लिकेशन की संगतता से जुड़े इस फ़्रेमवर्क के डिज़ाइन का इस्तेमाल करके, डेवलपर अपने ऐप्लिकेशन को डीबग करने के दौरान, कुछ समय के लिए व्यवहार में होने वाले खास बदलावों को बंद कर सकते हैं. ऐसा वे प्रीव्यू और बीटा रिलीज़ के दौरान कर सकते हैं. इससे उन्हें व्यवहार में होने वाले कई बदलावों को एक साथ अडजस्ट करने के लिए मजबूर नहीं होना पड़ता.

फ़ॉरवर्ड कंपैटिबिलिटी

फ़ॉरवर्ड कंपैटिबिलिटी, डिज़ाइन की एक ऐसी सुविधा है जिसकी मदद से कोई सिस्टम, अपने बाद के वर्शन के लिए तैयार किए गए इनपुट को स्वीकार कर सकता है. एपीआई डिज़ाइन के मामले में, आपको शुरुआती डिज़ाइन के साथ-साथ आने वाले समय में होने वाले बदलावों पर भी खास ध्यान देना होगा. ऐसा इसलिए, क्योंकि डेवलपर चाहते हैं कि वे कोड को एक बार लिखें, उसे एक बार टेस्ट करें, और वह बिना किसी समस्या के हर जगह चले.

Android में, आगे के वर्शन के साथ काम करने से जुड़ी सबसे आम समस्याएं इन वजहों से होती हैं:

  • किसी सेट में नए कॉन्स्टेंट (जैसे, @IntDef या enum) जोड़ना. पहले यह माना जाता था कि यह सेट पूरा हो गया है. उदाहरण के लिए, जहां switch में default है, जो एक अपवाद दिखाता है.
  • ऐसी सुविधा के लिए सहायता जोड़ी गई है जिसे सीधे तौर पर एपीआई में कैप्चर नहीं किया जाता (उदाहरण के लिए, XML में ColorStateList-टाइप के संसाधन असाइन करने की सुविधा. इससे पहले, सिर्फ़ <color> संसाधनों के लिए सहायता उपलब्ध थी).
  • रन-टाइम की जांचों पर लगी पाबंदियों को कम करना. उदाहरण के लिए, requireNotNull() की ऐसी जांच को हटाना जो पुराने वर्शन में मौजूद थी.

इन सभी मामलों में, डेवलपर को सिर्फ़ रनटाइम में पता चलता है कि कुछ गड़बड़ी है. इससे भी बुरी बात यह है कि उन्हें फ़ील्ड में मौजूद पुराने डिवाइसों से मिली क्रैश रिपोर्ट से इस बारे में पता चल सकता है.

इसके अलावा, ये सभी मामले तकनीकी तौर पर एपीआई में किए गए मान्य बदलाव हैं. इनसे बाइनरी या सोर्स के साथ काम करने की सुविधा पर कोई असर नहीं पड़ता. साथ ही, एपीआई लिंट इन समस्याओं का पता नहीं लगा पाएगा.

इसलिए, एपीआई डिज़ाइनर को मौजूदा क्लास में बदलाव करते समय, सावधानी बरतनी चाहिए. यह सवाल पूछें, "क्या इस बदलाव की वजह से, प्लैटफ़ॉर्म के सबसे नए वर्शन के हिसाब से सिर्फ़ लिखा और टेस्ट किया गया कोड, पुराने वर्शन पर काम नहीं करेगा?"

एक्सएमएल स्कीमा

अगर कोई एक्सएमएल स्कीमा, कॉम्पोनेंट के बीच एक स्टेबल इंटरफ़ेस के तौर पर काम करता है, तो उस स्कीमा को साफ़ तौर पर बताया जाना चाहिए. साथ ही, उसे पिछले वर्शन के साथ काम करने वाले तरीके से विकसित किया जाना चाहिए. यह अन्य Android API की तरह ही होना चाहिए. उदाहरण के लिए, एक्सएमएल एलिमेंट और एट्रिब्यूट का स्ट्रक्चर वैसा ही होना चाहिए जैसा कि Android के अन्य एपीआई सर्फ़ेस पर, तरीकों और वैरिएबल का होता है.

एक्सएमएल फ़ॉर्मैट काम नहीं करेगा

अगर आपको किसी एक्सएमएल एलिमेंट या एट्रिब्यूट को बंद करना है, तो xs:annotation मार्कर जोड़ा जा सकता है. हालांकि, आपको @SystemApi के सामान्य डेवलपमेंट लाइफ़साइकल का पालन करके, सभी मौजूदा एक्सएमएल फ़ाइलों के लिए सहायता जारी रखनी होगी.

<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

तत्वों के टाइप को सुरक्षित रखना ज़रूरी है

स्कीमा, sequence एलिमेंट, choice एलिमेंट, और all एलिमेंट को complexType एलिमेंट के चाइल्ड एलिमेंट के तौर पर इस्तेमाल करने की अनुमति देते हैं. हालांकि, इन चाइल्ड एलिमेंट में चाइल्ड एलिमेंट की संख्या और क्रम अलग-अलग होता है. इसलिए, मौजूदा टाइप में बदलाव करने से, यह बदलाव काम नहीं करेगा.

अगर आपको किसी मौजूदा टाइप में बदलाव करना है, तो सबसे सही तरीका यह है कि पुराने टाइप को बंद कर दिया जाए और उसकी जगह नया टाइप इस्तेमाल किया जाए.

<!-- Original "sequence" value -->
<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

<!-- New "choice" value -->
<xs:element name="fooChoice">
    <xs:complexType>
        <xs:choice>
            <xs:element name="name" type="xs:string"/>
        </xs:choice>
    </xs:complexType>
</xs:element>

मेनलाइन से जुड़े पैटर्न

Mainline एक ऐसा प्रोजेक्ट है जिसकी मदद से, Android OS के सबसिस्टम ("mainline मॉड्यूल") को अलग-अलग अपडेट किया जा सकता है. इसके लिए, पूरी सिस्टम इमेज को अपडेट करने की ज़रूरत नहीं होती.

मेनलाइन मॉड्यूल को कोर प्लैटफ़ॉर्म से "अनबंडल" करना होगा. इसका मतलब है कि हर मॉड्यूल और बाकी दुनिया के बीच सभी इंटरैक्शन, फ़ॉर्मल (सार्वजनिक या सिस्टम) एपीआई का इस्तेमाल करके किए जाने चाहिए.

कुछ ऐसे डिज़ाइन पैटर्न हैं जिनका पालन मेनलाइन मॉड्यूल को करना चाहिए. इस सेक्शन में, इनके बारे में बताया गया है.

<Module>FrameworkInitializer पैटर्न

अगर किसी मेनलाइन मॉड्यूल को @SystemService क्लास (उदाहरण के लिए, @SystemService) को दिखाना है, तो इस पैटर्न का इस्तेमाल करें:JobScheduler

  • अपने मॉड्यूल से <YourModule>FrameworkInitializer क्लास को दिखाएं. यह क्लास, $BOOTCLASSPATH में होनी चाहिए. उदाहरण: StatsFrameworkInitializer

  • इसे @SystemApi(client = MODULE_LIBRARIES) के तौर पर मार्क करें.

  • इसमें public static void registerServiceWrappers() तरीका जोड़ो.

  • SystemServiceRegistry.registerContextAwareService() का इस्तेमाल तब करें, जब किसी सेवा मैनेजर क्लास को Context के रेफ़रंस की ज़रूरत हो.

  • SystemServiceRegistry.registerStaticService() का इस्तेमाल तब करें, जब किसी सेवा मैनेजर क्लास को रजिस्टर करना हो और उसे Context के रेफ़रंस की ज़रूरत न हो.

  • SystemServiceRegistry के स्टैटिक इनिशियलाइज़र से registerServiceWrappers() वाले तरीके को कॉल करें.

<Module>ServiceManager पैटर्न

आम तौर पर, सिस्टम सर्विस बाइंडर ऑब्जेक्ट रजिस्टर करने या उनके रेफ़रंस पाने के लिए, ServiceManager का इस्तेमाल किया जाता है. हालांकि, मेनलाइन मॉड्यूल इसका इस्तेमाल नहीं कर सकते, क्योंकि यह छिपा हुआ है. इस क्लास को छिपाया गया है, क्योंकि मेनलाइन मॉड्यूल को स्टैटिक प्लैटफ़ॉर्म या अन्य मॉड्यूल से दिखाए गए सिस्टम सेवा बाइंडर ऑब्जेक्ट को रजिस्टर या रेफ़र नहीं करना चाहिए.

मॉड्यूल में लागू की गई बाइंडर सेवाओं को रजिस्टर करने और उनके रेफ़रंस पाने के लिए, मेनलाइन मॉड्यूल इस पैटर्न का इस्तेमाल कर सकते हैं.

  • TelephonyServiceManager के डिज़ाइन के मुताबिक, <YourModule>ServiceManager क्लास बनाएं

  • क्लास को @SystemApi के तौर पर दिखाएं. अगर आपको इसे सिर्फ़ $BOOTCLASSPATH क्लास या सिस्टम सर्वर क्लास से ऐक्सेस करना है, तो @SystemApi(client = MODULE_LIBRARIES) का इस्तेमाल करें. अगर ऐसा नहीं है, तो @SystemApi(client = PRIVILEGED_APPS) का इस्तेमाल करें.

  • इस क्लास में ये जानकारी शामिल होगी:

    • यह एक छिपा हुआ कंस्ट्रक्टर है, इसलिए सिर्फ़ स्टैटिक प्लैटफ़ॉर्म कोड इसे इंस्टैंशिएट कर सकता है.
    • सार्वजनिक तौर पर उपलब्ध ऐसे getter मेथड जो किसी खास नाम के लिए ServiceRegisterer इंस्टेंस दिखाते हैं. अगर आपके पास एक बाइंडर ऑब्जेक्ट है, तो आपको एक गेटर मेथड की ज़रूरत होगी. अगर आपके पास दो प्रॉपर्टी हैं, तो आपको दो गेटर की ज़रूरत होगी.
    • ActivityThread.initializeMainlineModules() में, इस क्लास को इंस्टैंशिएट करें और इसे अपने मॉड्यूल से दिखाए गए स्टैटिक मेथड में पास करें. आम तौर पर, आपको अपनी FrameworkInitializer क्लास में एक स्टैटिक @SystemApi(client = MODULE_LIBRARIES) एपीआई जोड़ना होता है, जो इसे लेता है.

इस पैटर्न की वजह से, अन्य मेनलाइन मॉड्यूल इन एपीआई को ऐक्सेस नहीं कर पाएंगे. इसकी वजह यह है कि अन्य मॉड्यूल के पास <YourModule>ServiceManager का इंस्टेंस पाने का कोई तरीका नहीं है. भले ही, get() और register() एपीआई उन्हें दिख रहे हों.

टेलीफ़ोनी सेवा का रेफ़रंस, टेलीफ़ोनी को इस तरह मिलता है: कोड खोजने का लिंक.

अगर आपका ऐप्लिकेशन, नेटिव कोड में सर्विस बाइंडर ऑब्जेक्ट लागू करता है, तो आपको AServiceManager नेटिव एपीआई का इस्तेमाल करना होगा. ये एपीआई, ServiceManager Java API से जुड़े हैं. हालांकि, नेटिव एपीआई सीधे तौर पर मेनलाइन मॉड्यूल के लिए उपलब्ध होते हैं. इनका इस्तेमाल, ऐसे बाइंडर ऑब्जेक्ट को रजिस्टर करने या उनका रेफ़रंस देने के लिए न करें जिनका मालिकाना हक आपके मॉड्यूल के पास नहीं है. अगर आपने नेटिव से कोई बाइंडर ऑब्जेक्ट दिखाया है, तो आपके <YourModule>ServiceManager.ServiceRegisterer को register() तरीके की ज़रूरत नहीं है.

मेनलाइन मॉड्यूल में अनुमति की परिभाषाएं

APK वाले मुख्य मॉड्यूल, अपने APK AndroidManifest.xml में (कस्टम) अनुमतियां तय कर सकते हैं. ये अनुमतियां, सामान्य APK की तरह ही तय की जा सकती हैं.

अगर तय की गई अनुमति का इस्तेमाल सिर्फ़ किसी मॉड्यूल में अंदरूनी तौर पर किया जाता है, तो उसके अनुमति के नाम के आगे APK पैकेज का नाम जोड़ना चाहिए. उदाहरण के लिए:

<permission
    android:name="com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER"
    android:protectionLevel="signature" />

अगर तय की गई अनुमति को अपडेट किए जा सकने वाले प्लैटफ़ॉर्म एपीआई के हिस्से के तौर पर, दूसरे ऐप्लिकेशन को देना है, तो अनुमति के नाम से पहले "android.permission." जोड़ना चाहिए. (जैसे, किसी स्टैटिक प्लैटफ़ॉर्म की अनुमति) के साथ-साथ मॉड्यूल के पैकेज का नाम भी शामिल होता है. इससे यह पता चलता है कि यह मॉड्यूल का प्लैटफ़ॉर्म एपीआई है. साथ ही, नाम से जुड़ी किसी भी समस्या से बचा जा सकता है. उदाहरण के लिए:

<permission
    android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"
    android:label="@string/active_calories_burned_read_content_description"
    android:protectionLevel="dangerous"
    android:permissionGroup="android.permission-group.HEALTH" />

इसके बाद, मॉड्यूल इस अनुमति के नाम को अपने एपीआई सर्फ़ेस में एपीआई कॉन्स्टेंट के तौर पर दिखा सकता है. उदाहरण के लिए, HealthPermissions.READ_ACTIVE_CALORIES_BURNED.