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

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

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

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

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

Gradle बिल्ड फ़ेज़

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

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

कॉन्फ़िगरेशन फ़ेज़

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

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

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

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

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

Variant API, Android Gradle प्लगिन में एक्सटेंशन का एक तरीका है. इसकी मदद से, Android बिल्ड पर असर डालने वाले अलग-अलग विकल्पों में बदलाव किया जा सकता है. आम तौर पर, इन विकल्पों को बिल्ड कॉन्फ़िगरेशन फ़ाइलों में DSL का इस्तेमाल करके सेट किया जाता है. Variant API की मदद से, आपको इंटरमीडिएट और फ़ाइनल आर्टफ़ैक्ट का ऐक्सेस भी मिलता है. ये आर्टफ़ैक्ट, बिल्ड प्रोसेस के दौरान बनाए जाते हैं. जैसे, क्लास फ़ाइलें, मर्ज किया गया मेनिफ़ेस्ट या 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() की तरह, आपकी दी गई सभी वैल्यू को कॉन्फ़िगरेशन के समय हल किया जाना चाहिए. साथ ही, ये वैल्यू बाहरी इनपुट पर निर्भर नहीं होनी चाहिए. VariantBuilder कॉलबैक का इस्तेमाल पूरा होने के बाद, VariantBuilder ऑब्जेक्ट में बदलाव नहीं किया जाना चाहिए.beforeVariants()

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 वैल्यू सिर्फ़ तब हल होंगी, जब AGP के टास्क पूरे हो जाएंगे. इसलिए, इन्हें अपने कस्टम टास्क के प्रोवाइडर से सुरक्षित तरीके से कनेक्ट किया जा सकता है. ये प्रोवाइडर, ज़रूरी कैलकुलेशन करेंगे. इनमें फ़ाइलों या नेटवर्क जैसे बाहरी इनपुट से डेटा पढ़ना भी शामिल है.

// 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() })
}

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

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

  • java डायरेक्ट्री में मौजूद ऐप्लिकेशन कोड
  • res डायरेक्ट्री में मौजूद Android रिसॉर्स
  • resources डायरेक्ट्री में मौजूद Java संसाधन
  • assets डायरेक्ट्री में मौजूद Android ऐसेट

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

इस कोड स्निपेट में, 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 रेसिपी देखें.

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

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

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

कार्डिनलिटी

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

FileSystemLocation तरह का

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

इन कार्रवाइयों के लिए यह सुविधा उपलब्ध है

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

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

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

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

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

इसके बाद, अपने कस्टम TaskProvider को पास करके, TaskBasedOperation ऑब्जेक्ट (1) पाएं. साथ ही, इसका इस्तेमाल करके, 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 बिल्ड सिस्टम के मैन्युअल में दिए गए ये सेक्शन पढ़ें: