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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Provider<T>
T टाइप की वैल्यू देता है (जहां "T" का मतलब किसी भी तरह का है). इसे get() का इस्तेमाल करके, एक्ज़ीक्यूशन के दौरान पढ़ा जा सकता है. इसके अलावा, map(), flatMap(), और zip() तरीकों का इस्तेमाल करके, एक नए Provider<S> (जहां "S" का मतलब किसी और तरह का है) में बदला जा सकता है. ध्यान दें कि कॉन्फ़िगरेशन के दौरान, get() को कभी भी कॉल नहीं किया जाना चाहिए.
  • map(): इसमें लैम्डा को स्वीकार किया जाता है और S,Provider<S> टाइप का Provider जनरेट किया जाता है. map() का लैम्डा आर्ग्युमेंट 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 अपने हिसाब से ऐसे टास्क लिखें जो इनपुट और/या आउटपुट के हिसाब से हों. हालांकि, AGP अपने टास्क का ऐक्सेस सीधे तौर पर सार्वजनिक नहीं करता. ये लागू करने से जुड़ी जानकारी होती है. यह हर वर्शन में अलग-अलग हो सकती है. इसके बजाय, AGP, वैरिएंट एपीआई को इसके टास्क या आर्टफ़ैक्ट के आउटपुट का ऐक्सेस देता है, ताकि आप उन्हें पढ़ सकें और उनमें बदलाव कर सकें. ज़्यादा जानकारी के लिए, इस दस्तावेज़ में वैरिएंट एपीआई, आर्टफ़ैक्ट, और टास्क देखें.

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

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

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

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

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

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

प्लान लागू करने का चरण

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

Variant API, आर्टफ़ैक्ट, और टास्क

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

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

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

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

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

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

  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)

इस कॉलबैक में, उन DSL ऑब्जेक्ट को ऐक्सेस और उनमें बदलाव किया जा सकता है जिन्हें बिल्ड फ़ाइलों में मौजूद 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 रेसिपी देखें.

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

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

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

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

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

FileSystemLocation तरह का

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

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

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

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

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

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

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

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

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

आखिर में, 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 बिल्ड सिस्टम के मैन्युअल के ये सेक्शन पढ़ें: