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

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

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

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

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

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

बंद किए जाने की नीति

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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