Gradle प्लग इन लिखना

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

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

AGP API का लाइफ़साइकल

एजीपी अपने एपीआई की स्थिति तय करने के लिए, Gradle सुविधा के लाइफ़साइकल का पालन करता है:

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

बंद करने की नीति

पुराने एपीआई बंद होने और उनकी जगह नए और स्थिर एपीआई और नई डोमेन स्पेसिफ़िक लैंग्वेज (डीएसएल) के इस्तेमाल से, एजीपी बेहतर हो रहा है. यह बदलाव, AGP की कई रिलीज़ में होगा. इस बारे में ज़्यादा जानने के लिए, AGP API/DSL माइग्रेशन टाइमलाइन पर जाएं.

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

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

Gradle बिल्ड की बुनियादी बातें

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

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

Gradle के लेज़ी टाइप की ग्लॉसरी

Gradle में कई तरह के टूल उपलब्ध हैं, जो "धीमे" तरीके से काम करते हैं. साथ ही, ये टूल भारी गणनाओं या Task बनाने की प्रोसेस को, बिल्ड के बाद के चरणों तक के लिए टालने में मदद करते हैं. ये टाइप, कई Gradle और AGP एपीआई के मुख्य हिस्से हैं. यहां दी गई सूची में, धीरे-धीरे लागू होने वाले Gradle के मुख्य टाइप और उनके मुख्य तरीके शामिल हैं.

Provider<T>
यह T टाइप की वैल्यू देता है. यहां "T" का मतलब किसी भी टाइप से है. इसे get() का इस्तेमाल करके, एक्सीक्यूशन फ़ेज़ के दौरान पढ़ा जा सकता है. इसके अलावा, map(), flatMap(), और zip() तरीकों का इस्तेमाल करके, इसे Provider<S> (जहां "S" का मतलब किसी अन्य टाइप से है) में बदला जा सकता है. ध्यान दें कि कॉन्फ़िगरेशन के दौरान, get() को कभी भी कॉल नहीं किया जाना चाहिए.
  • map(): यह फ़ंक्शन, lambda फ़ंक्शन को स्वीकार करता है और S,Provider<S> टाइप का Provider बनाता है. map() के लिए Lambda आर्ग्युमेंट, T वैल्यू लेता है और S वैल्यू दिखाता है. Lambda फ़ंक्शन को तुरंत एक्ज़ीक्यूट नहीं किया जाता है. इसके बजाय, इसे तब तक टाला जाता है, जब नतीजे के तौर पर मिले Provider<S> पर get() को कॉल किया जाता है. इससे पूरी चेन लेज़ी हो जाती है.
  • flatMap(): यह LAMBDA फ़ंक्शन को भी स्वीकार करता है और Provider<S> दिखाता है. हालांकि, LAMBDA फ़ंक्शन, वैल्यू T को स्वीकार करके Provider<S> दिखाता है, न कि सीधे वैल्यू S दिखाता है. जब कॉन्फ़िगरेशन के समय S का पता नहीं लगाया जा सकता और सिर्फ़ Provider<S> मिल सकता है, तो flatMap() का इस्तेमाल करें. आम तौर पर, अगर आपने map() का इस्तेमाल किया है और आपको Provider<Provider<S>> टाइप का नतीजा मिला है, तो इसका मतलब है कि आपको flatMap() का इस्तेमाल करना चाहिए था.
  • zip(): इससे आपको नया Provider बनाने के लिए, दो Provider इंस्टेंस को एक साथ जोड़ने की सुविधा मिलती है. इसमें, दो इनपुट Providers इंस्टेंस की वैल्यू को जोड़ने वाले फ़ंक्शन का इस्तेमाल करके, कैलकुलेट की गई वैल्यू शामिल होती है.
Property<T>
, Provider<T> को लागू करता है, इसलिए यह T टाइप की वैल्यू भी देता है. Provider<T> के मुकाबले, Property<T> के लिए वैल्यू सेट की जा सकती है. Provider<T> सिर्फ़ पढ़ने के लिए होता है. ऐसा करने के दो तरीके हैं:
  • T टाइप की वैल्यू उपलब्ध होने पर, उसे सीधे सेट करें. इसके लिए, आपको बाद में कैलकुलेट करने की ज़रूरत नहीं है.
  • Property<T> की वैल्यू के सोर्स के तौर पर, कोई दूसरा Provider<T> सेट करें. इस मामले में, T वैल्यू सिर्फ़ तब लागू होती है, जब Property.get() को कॉल किया जाता है.
TaskProvider
Provider<Task> लागू करता है. TaskProvider जनरेट करने के लिए, tasks.create() के बजाय tasks.register() का इस्तेमाल करें. इससे यह पक्का किया जा सकेगा कि टास्क सिर्फ़ ज़रूरत पड़ने पर ही इंस्टैंशिएट किए जा सकते हैं. Task बनाने से पहले, Task के आउटपुट को ऐक्सेस करने के लिए flatMap() का इस्तेमाल किया जा सकता है. यह तब काम का हो सकता है, जब आपको आउटपुट को अन्य Task इंस्टेंस के इनपुट के तौर पर इस्तेमाल करना हो.

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

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

यहां दो टास्क, GitVersionTask और ManifestProducerTask को रजिस्टर करने का उदाहरण दिया गया है. साथ ही, Task इंस्टेंस बनाने को तब तक के लिए टाल दिया गया है, जब तक कि वे ज़रूरी न हों. ManifestProducerTask इनपुट वैल्यू को GitVersionTask के आउटपुट से मिली Provider पर सेट किया गया है. इसलिए, ManifestProducerTask, GitVersionTask पर निर्भर करता है.

// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
    project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
        it.gitVersionOutputFile.set(
            File(project.buildDir, "intermediates/gitVersionProvider/output")
        )
    }

...

/**
 * Register another task in the configuration block (also executed lazily,
 * only if the task is required).
 */
val manifestProducer =
    project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
        /**
         * Connect this task's input (gitInfoFile) to the output of
         * gitVersionProvider.
         */
        it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
    }

ये दोनों टास्क सिर्फ़ तब लागू होंगे, जब उनका साफ़ तौर पर अनुरोध किया गया हो. यह, Gradle को कॉल करने के दौरान हो सकता है. उदाहरण के लिए, अगर ./gradlew debugManifestProducer को चलाया जाता है या ManifestProducerTask का आउटपुट किसी दूसरे टास्क से जुड़ा होता है और उसकी वैल्यू ज़रूरी हो जाती है.

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

Gradle बिल्ड के फ़ेज़

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

इनमें से कुछ ऑप्टिमाइज़ेशन लागू करने के लिए, Gradle स्क्रिप्ट और प्लग इन को Gradle के बिल्ड के हर चरण के दौरान, सख्त नियमों का पालन करना होगा: शुरू करना, कॉन्फ़िगर करना, और लागू करना. इस गाइड में, हम कॉन्फ़िगरेशन और एक्सीक्यूशन के चरणों पर फ़ोकस करेंगे. Gredle बिल्ड लाइफ़साइकल गाइड में, आपको सभी चरणों के बारे में ज़्यादा जानकारी मिल सकती है.

कॉन्फ़िगरेशन का चरण

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

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

लागू करने का फ़ेज़

लागू होने के चरण में, अनुरोध किए गए टास्क और उनसे जुड़े टास्क पूरे हो जाते हैं. खास तौर पर, @TaskAction से मार्क किए गए Task क्लास के तरीके को कार्यान्वित किया जाता है. टास्क को लागू करने के दौरान, आपके पास इनपुट (जैसे, फ़ाइलें) से पढ़ने की अनुमति होती है. साथ ही, Provider<T>.get() को कॉल करके, लेज़ी प्रोवाइडर को हल किया जा सकता है. इस तरह से, धीमी गति से काम करने वाले प्रोवाइडर को ठीक करने पर, map() या flatMap() कॉल का क्रम शुरू होता है. यह कॉल, प्रोवाइडर में मौजूद टास्क की डिपेंडेंसी की जानकारी के हिसाब से होता है. ज़रूरी वैल्यू पाने के लिए, टास्क धीरे-धीरे चलाए जाते हैं.

वैरिएंट एपीआई, आर्टफ़ैक्ट, और टास्क

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

Android बिल्ड फ़्लो और एक्सटेंशन पॉइंट

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

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

  1. डीएसएल पार्स करना: इस दौरान, बिल्ड स्क्रिप्ट का आकलन किया जाता है. साथ ही, android ब्लॉक से Android डीएसएल ऑब्जेक्ट की अलग-अलग प्रॉपर्टी बनाई और सेट की जाती हैं. इस चरण के दौरान, नीचे दिए गए सेक्शन में बताए गए Variant API कॉलबैक को भी रजिस्टर किया जाता है.
  2. finalizeDsl(): ऐसा कॉलबैक जिसकी मदद से, कॉम्पोनेंट (वैरिएंट) बनाने के लिए, डीएसएल ऑब्जेक्ट को लॉक किए जाने से पहले उन्हें बदला जा सकता है. VariantBuilder ऑब्जेक्ट, डीएसएल ऑब्जेक्ट में मौजूद डेटा के आधार पर बनाए जाते हैं.

  3. डीएसएल लॉक करना: डीएसएल अब लॉक हो गया है और इसमें बदलाव नहीं किया जा सकता.

  4. beforeVariants(): इस कॉलबैक से यह तय हो सकता है कि कौनसे कॉम्पोनेंट बनाए जाएं और VariantBuilder की मदद से उनकी कुछ प्रॉपर्टी क्या हों. हालांकि, इससे अब भी बाइल्ड फ़्लो और जनरेट किए गए आर्टफ़ैक्ट में बदलाव किए जा सकते हैं.

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

  6. onVariants(): इस कॉलबैक में, आपको बनाए गए Variant ऑब्जेक्ट का ऐक्सेस मिलता है. साथ ही, इनमें मौजूद Property वैल्यू के लिए वैल्यू या प्रोवाइडर सेट किए जा सकते हैं, ताकि इन्हें धीरे-धीरे कैलकुलेट किया जा सके.

  7. वैरिएंट लॉक करना: वैरिएंट ऑब्जेक्ट अब लॉक हो गए हैं और उनमें बदलाव नहीं किया जा सकता.

  8. बनाए गए टास्क: Variant ऑब्जेक्ट और उनकी Property वैल्यू का इस्तेमाल, Task इंस्टेंस बनाने के लिए किया जाता है. ये इंस्टेंस, बिल्ड करने के लिए ज़रूरी होते हैं.

AGP ने एक AndroidComponentsExtension लॉन्च किया है. इसकी मदद से, finalizeDsl(), beforeVariants(), और onVariants() के लिए कॉलबैक रजिस्टर किए जा सकते हैं. यह एक्सटेंशन, androidComponents ब्लॉक की मदद से बिल्ड स्क्रिप्ट में उपलब्ध है:

// This is used only for configuring the Android build through DSL.
android { ... }

// The androidComponents block is separate from the DSL.
androidComponents {
   finalizeDsl { extension ->
      ...
   }
}

हालांकि, हमारा सुझाव है कि Android ब्लॉक के डीएसएल का इस्तेमाल करके, सिर्फ़ डिक्लेरेटिव कॉन्फ़िगरेशन के लिए बिल्ड स्क्रिप्ट रखें. साथ ही, किसी भी कस्टम इंपरिएटिव लॉजिक को buildSrc या बाहरी प्लग इन में ले जाएं. अपने प्रोजेक्ट में प्लगिन बनाने का तरीका जानने के लिए, Gradle रेसिपी के GitHub रिपॉज़िटरी में buildSrc सैंपल भी देखे जा सकते हैं. यहां प्लग इन कोड से कॉलबैक रजिस्टर करने का उदाहरण दिया गया है:

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            ...
        }
    }
}

आइए, उपलब्ध कॉलबैक और इस्तेमाल के उन उदाहरणों के बारे में ज़्यादा जानें जिनके लिए आपका प्लग इन काम कर सकता है:

finalizeDsl(callback: (DslExtensionT) -> Unit)

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

abstract class ExamplePlugin: Plugin<Project> {

    override fun apply(project: Project) {
        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.finalizeDsl { extension ->
            extension.buildTypes.create("extra").let {
                it.isJniDebuggable = true
            }
        }
    }
}

beforeVariants()

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

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

beforeVariants() कॉलबैक में VariantSelector को वैकल्पिक तौर पर इस्तेमाल किया जा सकता है. इसे androidComponentsExtension पर selector() तरीके से पाया जा सकता है. इसका इस्तेमाल, कॉलबैक को ट्रिगर करने में हिस्सा लेने वाले कॉम्पोनेंट को फ़िल्टर करने के लिए किया जा सकता है. ऐसा, उनके नाम, बिल्ड टाइप या प्रॉडक्ट फ़्लेवर के आधार पर किया जा सकता है.

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

onVariants() के कॉल होने तक, AGP के बनाए जाने वाले सभी आर्टफ़ैक्ट पहले ही तय कर लिए जाते हैं. इसलिए, उन्हें बंद नहीं किया जा सकता. हालांकि, Variant ऑब्जेक्ट में Property एट्रिब्यूट के लिए वैल्यू सेट करके, टास्क के लिए इस्तेमाल की गई कुछ वैल्यू में बदलाव किया जा सकता है. एजीपी के टास्क पूरे होने के बाद ही, Property की वैल्यू रिज़ॉल्व की जा सकती हैं. इसलिए, उन्हें अपनी ज़रूरत के हिसाब से बनाए गए टास्क से, सेवा देने वालों के साथ सुरक्षित तरीके से जोड़ा जा सकता है. ये वैल्यू, फ़ाइलों या नेटवर्क जैसे बाहरी इनपुट की मदद से कैलकुलेट की जा सकती हैं.

// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
    // Gather the output when we are in single mode (no multi-apk).
    val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

    // Create version code generating task
    val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
        it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
    }
    /**
     * Wire version code from the task output.
     * map() will create a lazy provider that:
     * 1. Runs just before the consumer(s), ensuring that the producer
     * (VersionCodeTask) has run and therefore the file is created.
     * 2. Contains task dependency information so that the consumer(s) run after
     * the producer.
     */
    mainOutput.versionCode.set(versionCodeTask.map { it.outputFile.get().asFile.readText().toInt() })
}

बिल्ड में जनरेट किए गए सोर्स का योगदान दें

आपका प्लग इन, जनरेट किए गए कुछ तरह के सोर्स का योगदान दे सकता है. जैसे:

जोड़े जा सकने वाले सोर्स की पूरी सूची के लिए, सोर्स एपीआई देखें.

इस कोड स्निपेट में, addStaticSourceDirectory() फ़ंक्शन का इस्तेमाल करके, Java सोर्स सेट में ${variant.name} नाम का कस्टम सोर्स फ़ोल्डर जोड़ने का तरीका बताया गया है. इसके बाद, Android टूलचेन इस फ़ोल्डर को प्रोसेस करता है.

onVariants { variant ->
    variant.sources.java?.let { java ->
        java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
    }
}

ज़्यादा जानकारी के लिए, addJavaSource रेसिपी देखें.

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

onVariants(selector().withBuildType("release")) { variant ->
    // Step 1. Register the task.
    val resCreationTask =
       project.tasks.register<ResCreatorTask>("create${variant.name}Res")

    // Step 2. Register the task output to the variant-generated source directory.
    variant.sources.res?.addGeneratedSourceDirectory(
       resCreationTask,
       ResCreatorTask::outputDirectory)
    }

...

// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
   @get:OutputFiles
   abstract val outputDirectory: DirectoryProperty

   @TaskAction
   fun taskAction() {
      // Step 4. Generate your resources.
      ...
   }
}

ज़्यादा जानकारी के लिए, addCustomAsset recipe देखें.

आर्टफ़ैक्ट ऐक्सेस करना और उनमें बदलाव करना

एजीपी में, Variant ऑब्जेक्ट पर सामान्य प्रॉपर्टी में बदलाव करने के साथ-साथ एक एक्सटेंशन सिस्टम भी होता है. इससे बिल्ड के दौरान बनाए गए इंटरमीडिएट और फ़ाइनल आर्टफ़ैक्ट को पढ़ा जा सकता है या उन्हें बदला जा सकता है. उदाहरण के लिए, AndroidManifest.xml फ़ाइल के कॉन्टेंट को मर्ज करके, उसे कस्टम Task में पढ़ा जा सकता है, ताकि उसका विश्लेषण किया जा सके. इसके अलावा, कस्टम Task से जनरेट की गई मेनिफ़ेस्ट फ़ाइल के कॉन्टेंट से, AndroidManifest.xml फ़ाइल के कॉन्टेंट को पूरी तरह से बदला जा सकता है.

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

एलिमेंट की संख्या

Artifact के एलिमेंट की संख्या, इसके FileSystemLocation इंस्टेंस की संख्या या आर्टफ़ैक्ट टाइप की फ़ाइलों या डायरेक्ट्री की संख्या दिखाती है. किसी आर्टफ़ैक्ट की पैरंट क्लास की जांच करके, उसके एलिमेंट की संख्या के बारे में जानकारी पाई जा सकती है: एक FileSystemLocation वाले आर्टफ़ैक्ट, Artifact.Single की सब-क्लास होगी. एक से ज़्यादा FileSystemLocation इंस्टेंस वाले आर्टफ़ैक्ट, Artifact.Multiple की सब-क्लास होंगे.

FileSystemLocation तरह का

Artifact के पैरामीटर वाले FileSystemLocation टाइप को देखकर यह पता लगाया जा सकता है कि Artifact, फ़ाइलों या डायरेक्ट्री को दिखाता है या नहीं. यह टाइप, RegularFile या Directory हो सकता है.

ये ऑपरेशन किए जा सकते हैं

हर Artifact क्लास, इनमें से किसी भी इंटरफ़ेस को लागू कर सकती है, ताकि यह पता चल सके कि वह किन ऑपरेशन के साथ काम करती है:

  • Transformable: इसकी मदद से, Artifact को Task के इनपुट के तौर पर इस्तेमाल किया जा सकता है. Task, Artifact में अपनी मर्ज़ी के मुताबिक बदलाव करता है और उसका नया वर्शन दिखाता है.
  • Appendable: सिर्फ़ उन आर्टफ़ैक्ट पर लागू होता है जो Artifact.Multiple की सब-क्लास हैं. इसका मतलब है कि Artifact को जोड़ा जा सकता है. इसका मतलब है कि कस्टम Task, इस Artifact टाइप के नए इंस्टेंस बना सकता है, जिन्हें मौजूदा सूची में जोड़ा जाएगा.
  • Replaceable: सिर्फ़ उन आर्टफ़ैक्ट पर लागू होता है जो Artifact.Single के सबक्लास हैं. बदले जा सकने वाले Artifact को पूरी तरह से नए इंस्टेंस से बदला जा सकता है. यह इंस्टेंस, Task के आउटपुट के तौर पर जनरेट होता है.

आर्टफ़ैक्ट में बदलाव करने वाली तीन कार्रवाइयों के अलावा, हर आर्टफ़ैक्ट एक get() (या getAll()) ऑपरेशन की सुविधा देता है, जो आर्टफ़ैक्ट के फ़ाइनल वर्शन के साथ Provider दिखाता है (इस पर सभी कार्रवाइयां पूरी हो जाने के बाद).

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

रजिस्टर करने के ऑपरेशन का एंट्री पॉइंट, Artifacts क्लास है. यहां दिए गए कोड स्निपेट में बताया गया है कि onVariants() callback में, Variant ऑब्जेक्ट की प्रॉपर्टी से Artifacts के किसी इंस्टेंस का ऐक्सेस कैसे पाया जा सकता है.

इसके बाद, TaskBasedOperation ऑब्जेक्ट (1) पाने के लिए, अपने कस्टम TaskProvider को पास किया जा सकता है. साथ ही, wiredWith* के किसी एक तरीके (2) का इस्तेमाल करके, इसके इनपुट और आउटपुट को कनेक्ट करने के लिए इसका इस्तेमाल किया जा सकता है.

आपको जो तरीका चुनना है वह इस बात पर निर्भर करता है कि आपको जिस Artifact को ट्रांसफ़ॉर्म करना है उसके लागू किए गए FileSystemLocation टाइप और एलिमेंट की संख्या क्या है.

आखिर में, Artifact टाइप को उस तरीके में पास किया जाता है जो चुने गए ऑपरेशन को दिखाता है. यह ऑपरेशन, आपको *OperationRequest ऑब्जेक्ट पर दिखता है. उदाहरण के लिए, toAppendTo(), toTransform() या toCreate() (3).

androidComponents.onVariants { variant ->
    val manifestUpdater = // Custom task that will be used for the transform.
            project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
                it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
            }
    // (1) Register the TaskProvider w.
    val variant.artifacts.use(manifestUpdater)
         // (2) Connect the input and output files.
        .wiredWithFiles(
            ManifestTransformerTask::mergedManifest,
            ManifestTransformerTask::updatedManifest)
        // (3) Indicate the artifact and operation type.
        .toTransform(SingleArtifact.MERGED_MANIFEST)
}

इस उदाहरण में, MERGED_MANIFEST एक SingleArtifact है और यह एक RegularFile है. इस वजह से, हमें wiredWithFiles वाले तरीके का इस्तेमाल करना होगा. इसमें इनपुट के लिए एक RegularFileProperty रेफ़रंस और आउटपुट के लिए एकRegularFileProperty रेफ़रंस स्वीकार किया जाता है. TaskBasedOperation क्लास में wiredWith* के अन्य तरीके भी मौजूद हैं. ये Artifact एलिमेंट की संख्या और FileSystemLocation टाइप के अन्य कॉम्बिनेशन के लिए काम करेंगे.

AGP को एक्सटेंड करने के बारे में ज़्यादा जानने के लिए, हमारा सुझाव है कि आप Gradle बिल्ड सिस्टम के मैन्युअल के ये सेक्शन पढ़ें: