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

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

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

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

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

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

एपीआई के नियम

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

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

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

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

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

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

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

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

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

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

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

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

नए एपीआई को जोड़ने वाले बदलाव में, उसी CL या Gerrit विषय में उससे जुड़े टेस्ट शामिल होने चाहिए.

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

सभी एपीआई के लिए दस्तावेज़ होना चाहिए

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

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

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

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

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

कोड स्टाइल

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

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

Android कोडिंग के नियमों के बारे में यहां बताया गया है. ये नियम, बाहरी योगदान देने वालों के लिए हैं:

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

आम तौर पर, हम Java और Kotlin के स्टैंडर्ड कोडिंग कॉन्वेंशन का पालन करते हैं.

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

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

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

इससे, लागू करने की जानकारी ज़ाहिर हो जाती है. ऐसा करने से बचें.

कक्षाएं

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

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

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

अगर आपको UnsupportedOperationException को थ्रो करने के लिए, बेस क्लास के तरीकों को बदलना है, तो फिर से देखें कि किस बेस क्लास का इस्तेमाल किया जा रहा है.

कलेक्शन की बुनियादी क्लास का इस्तेमाल करना

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

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

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

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

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

बाइंडर इंटरफ़ेस

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

उदाहरण के लिए, 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 इंटरफ़ेस के लिए, स्थिर, पब्लिश किए गए, और वर्शन वाले IPC इंटरफ़ेस की ज़रूरत होती है. उदाहरण के लिए, ऐप्लिकेशन के इस्तेमाल के लिए Google Play services से एक्सपोर्ट किया गया सेवा इंटरफ़ेस. इसका मतलब है कि इंटरफ़ेस को बेहतर बनाना बहुत मुश्किल है. हालांकि, एपीआई के अन्य दिशा-निर्देशों के मुताबिक होने के लिए, अब भी इसके चारों ओर एक रैपर लेयर होना ज़रूरी है. साथ ही, अगर कभी ज़रूरत पड़े, तो IPC इंटरफ़ेस के नए वर्शन के लिए, उसी सार्वजनिक एपीआई का इस्तेमाल करना आसान हो.

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

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 के 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;
}

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

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

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 में नए व्यू सबक्लास न जोड़ें.*

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

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

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

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

रॉ फ़ील्ड को एक्सपोज़ न करें

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

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

Kotlin क्लास में प्रॉपर्टी एक्सपोज़ की जा सकती हैं.

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

रॉ फ़ील्ड का इस्तेमाल करने का सुझाव नहीं दिया जाता (@see रॉ फ़ील्ड को एक्सपोज़ न करें). हालांकि, अगर किसी फ़ील्ड को सार्वजनिक फ़ील्ड के तौर पर एक्सपोज़ किया जाता है, तो उस फ़ील्ड को final के तौर पर मार्क करें.

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

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

public int mFlags;

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

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

कॉन्स्टेंट

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

फ़्लैग की वैल्यू, 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 'सुरक्षित' के बजाय 'सार्वजनिक' का इस्तेमाल करना

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

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

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 में सार्वजनिक फ़ील्ड के नाम रखने के तरीके से मिलता-जुलता है.

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

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

सार्वजनिक थीम और स्टाइल के नाम, हैरारकी वाले 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()

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

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

उदाहरण के लिए, SDK टूल का सोर्स:

// 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 API में data class का इस्तेमाल न करें, क्योंकि Kotlin कंपाइलर, जनरेट किए गए कोड के लिए भाषा के एपीआई या बाइनरी के साथ काम करने की गारंटी नहीं देता. इसके बजाय, ज़रूरी फ़ंक्शन मैन्युअल तरीके से लागू करें.

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

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

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

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

जिन मामलों में डेटा में बदलाव करना ज़रूरी है उनमें, कॉपी कंस्ट्रक्टर (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.* टाइप, डिसगैरिंग की मदद से सभी प्लैटफ़ॉर्म वर्शन पर उपलब्ध हैं. साथ ही, एपीआई पैरामीटर या रिटर्न वैल्यू में समय दिखाते समय, इनका इस्तेमाल करना चाहिए.

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

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

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

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

अपवाद:

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

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

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

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

तरीकों को उनकी इकाई और समय के आधार के साथ सही तरीके से एनोटेट भी किया जाना चाहिए:

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

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

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() टाइम बेस में कोई ऐसी वैल्यू होनी चाहिए जो शून्य से बड़ी हो.

माप की इकाइयां

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

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 {
    // ...
  }
}

बिल्डर क्लास को बिल्डर दिखाना चाहिए

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

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() तरीके से, बनाए गए ऑब्जेक्ट का ऐसा इंस्टेंस दिखना चाहिए जो शून्य न हो. अगर अमान्य पैरामीटर की वजह से ऑब्जेक्ट नहीं बनाया जा सकता, तो पुष्टि करने की प्रोसेस को बिल्ड करने के तरीके पर छोड़ा जा सकता है. साथ ही, 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 को प्राथमिकता दें
  • इस तरीके में, कोई वैल्यू दिखाने के लिए, लंबे समय तक चलने वाले काम को ब्लॉक किया जाता है या किसी और तरीके से काम किया जाता है. जैसे, IPC या अन्य I/O -- 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

आम तौर पर, प्रॉपर्टी और ऐक्सेस करने वाले मेथड के लिए, नेमिंग के पॉज़िटिव तरीके का इस्तेमाल करना चाहिए. उदाहरण के लिए, Disabled के बजाय Enabled. नेगेटिव शब्दों का इस्तेमाल करने से, 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 स्uffix का इस्तेमाल कर सकते हैं:

// "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 को पहले लगाएं और get को पहले लगाएं और सेटर के लिए पहले वर्ण को बड़ा करें.set प्रॉपर्टी के एलान से, public Foo getFoo() और public void setFoo(Foo foo) नाम वाले तरीके बनेंगे.

अगर प्रॉपर्टी Boolean टाइप की है, तो नाम जनरेट करने के लिए एक और नियम लागू होता है: अगर प्रॉपर्टी का नाम is से शुरू होता है, तो get को is के पहले नहीं जोड़ा जाता है. इसके बजाय, प्रॉपर्टी के नाम का ही इस्तेमाल, गेट्टर के तौर पर किया जाता है. इसलिए, नाम रखने के दिशा-निर्देश का पालन करने के लिए, 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();

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

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

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

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

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

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

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

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

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

ज़रूरत से ज़्यादा जानकारी शामिल न करें

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

डीबग आउटपुट पर निर्भरता को कम करना

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

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

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

नए बनाए गए ऑब्जेक्ट दिखाने के लिए, createFoo का इस्तेमाल करना

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

जब कोई मेथड, कोई ऑब्जेक्ट बनाकर उसे दिखाएगा, तो मेथड के नाम में इसकी जानकारी दें.

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

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

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 एपीआई के लिए, वैल्यू के लिए शून्य होने की जानकारी देने वाले एनोटेशन ज़रूरी हैं. हालांकि, वैल्यू के लिए शून्य होने की जानकारी देने का कॉन्सेप्ट, Kotlin भाषा का हिस्सा है. इसलिए, Kotlin एपीआई में वैल्यू के लिए शून्य होने की जानकारी देने वाले एनोटेशन का इस्तेमाल कभी नहीं किया जाना चाहिए.

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

@Nullable
public String getName()

public void setName(@Nullable String name)

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

@NonNull
public String getName()

public void setName(@NonNull String name)

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

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

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

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

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

संसाधन

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

public void setTitle(@StringRes int resId)

कॉन्स्टेंट सेट के लिए @IntDef

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

/** @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 कॉन्स्टेंट के लिए है. एक से ज़्यादा "प्रीफ़िक्स" वैल्यू शामिल की जा सकती हैं. इनका इस्तेमाल, सभी वैल्यू के लिए दस्तावेज़ अपने-आप जनरेट करने के लिए किया जाता है.

SDK टूल के कॉन्स्टेंट के लिए @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";

बदलावों के लिए, वैल्यू न होने की स्थिति के साथ काम करने की सुविधा दें

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

टाइप अभिभावक बच्चा
रिटर्न टाइप एनोटेट नहीं किया गया एनोटेट नहीं किया गया या नॉन-नल
रिटर्न टाइप वैल्यू न होने पर भी काम करता है शून्य हो सकता है या नहीं
रिटर्न टाइप Nonnull Nonnull
मज़ेदार आर्ग्युमेंट एनोटेट नहीं किए गए एनोटेट नहीं किया गया है या शून्य वैल्यू हो सकती है
मज़ेदार आर्ग्युमेंट वैल्यू न होने पर भी काम करता है वैल्यू न होने पर भी काम करता है
मज़ेदार आर्ग्युमेंट Nonnull शून्य हो सकता है या नहीं

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

जब मेथड ओवरलोड किए जाते हैं, तो सभी आर्ग्युमेंट नॉन-नल होने चाहिए.

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

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

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 पेयर के लिए, शून्य होने की जानकारी देने वाले एनोटेशन एक जैसे होने चाहिए

किसी एक लॉजिकल प्रॉपर्टी के लिए, 'पाएं' और 'सेट करें' मेथड पेयर के लिए, वैल्यू न होने की जानकारी देने वाले एनोटेशन हमेशा एक जैसे होने चाहिए. इस दिशा-निर्देश का पालन न करने पर, 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 से ऐक्सेस किए जाने वाले Kotlin एपीआई के लिए, ऐरे List और Collection के बराबर होते हैं.

@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 को डिफ़ॉल्ट रूप से बदले जा सकने वाले रिटर्न टाइप का इस्तेमाल करना चाहिए. ऐसा इसलिए, क्योंकि Java API के Android प्लैटफ़ॉर्म पर लागू होने की वजह से, अब तक बदले जा सकने वाले कलेक्शन को आसानी से लागू नहीं किया जा सकता. इस नियम का अपवाद, 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);
}

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

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

/**
 * 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" कन्वर्ज़न एक नया, अलग ऑब्जेक्ट दिखाता है. अगर बाद में मूल ऑब्जेक्ट में बदलाव किया जाता है, तो नए ऑब्जेक्ट में वे बदलाव नहीं दिखेंगे. इसी तरह, अगर नए ऑब्जेक्ट में बाद में बदलाव किया जाता है, तो पुराने ऑब्जेक्ट में वे बदलाव नहीं दिखेंगे.

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 को दिखाया जाना चाहिए.

लिसनर और कॉलबैक

ये नियम, उन क्लास और तरीकों के बारे में हैं जिनका इस्तेमाल, listener और callback mechanism के लिए किया जाता है.

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

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

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

onFooEvent से पता चलता है कि FooEvent की प्रोसेस चल रही है और कॉलबैक को रिस्पॉन्स के तौर पर काम करना चाहिए.

बीते समय के मुकाबले मौजूदा समय में, समय के व्यवहार के बारे में बताया जाना चाहिए

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

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

public void onClicked()

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

public boolean onClick()

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

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

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

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 हटाना है. हालांकि, B के टाइप के बारे में जानकारी के बिना ऐसा करना संभव नहीं है. साथ ही, B को इस तरह से बनाया गया है कि उसके रैप किए गए कॉलबैक में बदलाव किए जा सकें.

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

जिन कॉलबैक के लिए थ्रेडिंग की कोई खास उम्मीद नहीं होती (यूआई टूलकिट के बाहर कहीं भी), उन्हें रजिस्टर करते समय, रजिस्टर करने के हिस्से के तौर पर 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()

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

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

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

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

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

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

Handler, एक ही क्लास के अन्य मिलते-जुलते एपीआई के साथ स्थानीय तौर पर काम करता है. अपवाद का यह अनुरोध स्थिति के हिसाब से स्वीकार किया जाता है. Executor के आधार पर, ओवरलोड जोड़ने की प्राथमिकता दी जाती है. साथ ही, Handler के लागू होने के बाद, Executor के नए वर्शन का इस्तेमाल करने के लिए, Handler को माइग्रेट किया जाता है. (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 तरीकों को प्राथमिकता देनी चाहिए. पहले, इस दिशा-निर्देश में abstract class का सुझाव दिया गया था, क्योंकि Java 7 में 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 से मिलने वाले नतीजे को requestFooAsync के callback पैरामीटर के OutcomeReceiver.onResult में रिपोर्ट किया जाता है. इसके लिए, दिए गए executor को कॉल किया जाता है. 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 एक्सटेंशन, androidx.core:core-ktx आर्टफ़ैक्ट के हिस्से के तौर पर Jetpack में उपलब्ध कराए जा सकते हैं. हालांकि, इसके लिए यह ज़रूरी है कि स्टैंडर्ड वर्शन के साथ काम करने की जांच और अन्य बातों का ध्यान रखा गया हो. ज़्यादा जानकारी, रद्द करने से जुड़ी बातें, और सैंपल के लिए, asOutcomeReceiver के दस्तावेज़ देखें.

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

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

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

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

SAM पैरामीटर का प्लेसमेंट

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) { ... }

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

Javadoc जोड़ते समय, update-api या docs target चलाना

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

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

true, false, और null जैसी Java वैल्यू को {@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 के साथ दस्तावेज़ में दर्ज करें. Kotlin सोर्स वाले उन एपीआई के लिए, जिनका इस्तेमाल Java क्लाइंट के लिए किया जाना है, फ़ंक्शन के साथ @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 देखें. इसमें, डाइमेंशन की जानकारी, खास जानकारी में जोड़ी गई है.
  • इसी वजह से, पहले वाक्य में "उदाहरण के लिए" का इस्तेमाल न करें, क्योंकि Doclava "g" के बाद, समरी दस्तावेज़ों को खत्म कर देता है. उदाहरण के लिए, View.java में TEXT_ALIGNMENT_CENTER देखें. ध्यान दें कि Metalava, पीरियड के बाद नॉनब्रेकिंग स्पेस डालकर, इस गड़बड़ी को अपने-आप ठीक कर देता है. हालांकि, शुरू से ही यह गड़बड़ी न करें.

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

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

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

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

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

  • टेबल में लाइनों के लिए <table>, हेडर के लिए <th>, और सेल के लिए <td> का इस्तेमाल किया जाना चाहिए.<tr> सभी टेबल टैग के लिए, मैच करने वाले क्लोज़िंग टैग ज़रूरी हैं. किसी टैग के बंद होने की जानकारी देने के लिए, उस पर 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 लागू करने के लिए, सार्वजनिक CREATOR फ़ील्ड होना ज़रूरी है

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

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

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

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

Enums का इस्तेमाल न करना

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

IntDef के फ़ायदे:

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

Enum के फ़ायदे

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

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

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

Google, दूसरी कंपनियों, और उनके प्रॉडक्ट का रेफ़रंस देने से बचें

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

पार्सल करने की सुविधा को लागू करने के बाद, उसमें कोई बदलाव नहीं किया जाना चाहिए

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

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

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

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

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

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

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

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

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

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

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

Object क्लास से एपीआई कॉन्ट्रैक्ट न मिलने और clone() का इस्तेमाल करने वाली क्लास को एक्सटेंड करने में होने वाली समस्याओं की वजह से, Java 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 एपीआई में यूआरआई के लिए पसंदीदा एनकैप्सुलेशन है.

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

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

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

इसलिए, इन एनोटेशन के इस्तेमाल को प्लैटफ़ॉर्म में @hide दस्तावेज़ एनोटेशन या लाइब्रेरी में @RestrictTo.Scope.LIBRARY) कोड एनोटेशन के साथ मार्क किया जाना चाहिए. इन दोनों मामलों में, उन्हें @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 {}

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

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

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

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

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

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

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

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

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

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

Context के 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.

साधारण int के बजाय UserHandle का इस्तेमाल करना

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

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

अगर यूज़र आईडी के तौर पर int का इस्तेमाल करना ज़रूरी है, तो उस पर @UserIdInt का एनोटेशन लगाना ज़रूरी है.

Foobar getFoobarForUser(@UserIdInt int user);

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

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

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

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

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

  • ब्रॉडकास्ट की सूचियां, शेयर किए जाने वाले संसाधन हैं. इसलिए, इनमें बहुत ज़्यादा काम हो सकता है. साथ ही, हो सकता है कि आपके इवेंट की डिलीवरी समय पर न हो पाए. हमें कई ऐसी ब्रॉडकास्ट सूचियां मिली हैं जिनमें एंड-टू-एंड इंतज़ार का समय 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 इंटरऑपरेबिलिटी गाइड देखें. खोज में आने की संभावना बढ़ाने के लिए, कुछ दिशा-निर्देशों को इस गाइड में कॉपी किया गया है.

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

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

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

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

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

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

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

Android प्लैटफ़ॉर्म एपीआई का विकास

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

बाइनरी में बदलाव

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

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

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

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

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

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

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

  • एपीआई y - जोड़ा गया
  • एपीआई y+1 - समर्थन बंद होना
    • कोड को @Deprecated से मार्क करें.
    • बदलाव जोड़ें और @deprecated दस्तावेज़ों के एनोटेशन का इस्तेमाल करके, इस्तेमाल नहीं किए जा रहे कोड के लिए 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" />

किसी एपीआई को कब बंद करना चाहिए

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

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

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

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

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

अगर SDK टूल में, इस्तेमाल में नहीं होने वाले API की संख्या ज़्यादा है, तो इन दोनों मामलों में समस्या और भी बढ़ जाती है.

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

  • हम आने वाले समय में, एपीआई को @remove करने वाले हैं.
  • एपीआई के इस्तेमाल से गलत या अस्पष्ट व्यवहार होता है. हम इसे ठीक नहीं कर सकते, क्योंकि इससे साथ काम करने की सुविधाएं बंद हो जाएंगी.

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

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

/**
 * ...
 * @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> दस्तावेज़ एनोटेशन का इस्तेमाल करें. खास जानकारी में, कॉन्टेंट हटाने की वजह ज़रूर शामिल होनी चाहिए. साथ ही, इसमें माइग्रेशन की रणनीति भी शामिल की जा सकती है. इस बारे में हमने अब काम नहीं करने वाले टूल में बताया है.

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

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

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

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

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

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

एपीआई की कुछ कैटगरी को धीरे-धीरे हटाया नहीं जाना चाहिए. एपीआई की कुछ कैटगरी को सॉफ़्टवाइज हटाना नहीं चाहिए.

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

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

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

कॉन्टेंट को पूरी तरह से हटाना

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

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

हम @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 के आधार पर, ऐप्लिकेशन के व्यवहार में होने वाले इन बदलावों को पहले से ही रोक रखा है. हालांकि, हमने हाल ही में ऐप्लिकेशन के साथ काम करने के लिए उपलब्ध फ़्रेमवर्क का इस्तेमाल करने की ज़रूरी शर्त को लागू किया है. इस नए फ़्रेमवर्क का इस्तेमाल करके, व्यवहार में बदलाव करने का तरीका यहां बताया गया है:

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

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

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

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

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

अगर कोई एक्सएमएल स्कीमा, कॉम्पोनेंट के बीच स्थिर इंटरफ़ेस के तौर पर काम करता है, तो उस स्कीमा के बारे में साफ़ तौर पर बताया जाना चाहिए. साथ ही, उसे Android के अन्य एपीआई की तरह, पुराने वर्शन के साथ काम करने वाले तरीके से अपडेट किया जाना चाहिए. उदाहरण के लिए, एक्सएमएल एलिमेंट और एट्रिब्यूट का स्ट्रक्चर, उसी तरह से बनाए रखा जाना चाहिए जिस तरह 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>

एलिमेंट टाइप को बनाए रखा जाना चाहिए

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

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

<!-- 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 क्लास (उदाहरण के लिए, JobScheduler) एक्सपोज़ करनी हैं, तो इस पैटर्न का इस्तेमाल करें:

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

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

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

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

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

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

<Module>ServiceManager पैटर्न

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

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

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

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

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

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

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

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

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

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

APK वाले मेनलाइन मॉड्यूल, अपने APKAndroidManifest.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.