Android Gradle प्लग इन (AGP), 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<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 बिल्ड पर असर डालते हैं. वैरिएंट एपीआई से, आपको इंटरमीडिएट और फ़ाइनल आर्टफ़ैक्ट का भी ऐक्सेस मिलता है. ये आर्टफ़ैक्ट, बिल्ड के ज़रिए बनाए जाते हैं. जैसे, क्लास फ़ाइलें, मर्ज की गई मेनिफ़ेस्ट या APK/AAB फ़ाइलें.
Android बिल्ड फ़्लो और एक्सटेंशन पॉइंट
AGP के साथ इंटरैक्ट करते समय, Gradle के लाइफ़साइकल कॉलबैक (जैसे, afterEvaluate()
) को रजिस्टर करने या साफ़ तौर पर Task
डिपेंडेंसी सेट अप करने के बजाय, खास तौर पर बनाए गए एक्सटेंशन पॉइंट का इस्तेमाल करें. AGP की मदद से बनाए गए टास्क को लागू करने की जानकारी माना जाता है. इन्हें सार्वजनिक एपीआई के तौर पर नहीं दिखाया जाता. आपको Task
ऑब्जेक्ट के इंस्टेंस पाने या Task
के नामों का अनुमान लगाने से बचना चाहिए. साथ ही, उन Task
ऑब्जेक्ट में सीधे तौर पर कॉलबैक या डिपेंडेंसी जोड़ने से भी बचना चाहिए.
AGP, अपने Task
इंस्टेंस बनाने और उन्हें लागू करने के लिए, यहां दिया गया तरीका अपनाता है. इससे बिल्ड आर्टफ़ैक्ट बनते हैं. Variant
ऑब्जेक्ट बनाने के मुख्य चरणों के बाद, कॉलबैक होते हैं. इनकी मदद से, किसी बिल्ड के हिस्से के तौर पर बनाए गए कुछ ऑब्जेक्ट में बदलाव किए जा सकते हैं. ध्यान दें कि सभी कॉलबैक, कॉन्फ़िगरेशन फ़ेज़ (इस पेज पर बताया गया है) के दौरान होते हैं. साथ ही, इन्हें तेज़ी से चलाया जाना चाहिए. इसके बजाय, किसी भी मुश्किल काम को लागू करने के फ़ेज़ के दौरान, सही Task
इंस्टेंस पर भेजा जाना चाहिए.
- डीएसएल पार्स करना: इस दौरान, बिल्ड स्क्रिप्ट का आकलन किया जाता है. साथ ही,
android
ब्लॉक से Android डीएसएल ऑब्जेक्ट की अलग-अलग प्रॉपर्टी बनाई और सेट की जाती हैं. इस चरण के दौरान, नीचे दिए गए सेक्शन में बताए गए वैरिएंट एपीआई कॉलबैक भी रजिस्टर किए जाते हैं. finalizeDsl()
: ऐसा कॉलबैक जिसकी मदद से, कॉम्पोनेंट (वैरिएंट) बनाने के लिए, डीएसएल ऑब्जेक्ट को लॉक किए जाने से पहले उन्हें बदला जा सकता है.VariantBuilder
ऑब्जेक्ट, डीएसएल ऑब्जेक्ट में मौजूद डेटा के आधार पर बनाए जाते हैं.डीएसएल लॉक करना: डीएसएल अब लॉक हो गया है और इसमें बदलाव नहीं किया जा सकता.
beforeVariants()
: इस कॉलबैक से यह तय हो सकता है कि कौनसे कॉम्पोनेंट बनाए जाएं औरVariantBuilder
की मदद से उनकी कुछ प्रॉपर्टी क्या हों. हालांकि, इससे अब भी बाइल्ड फ़्लो और जनरेट किए गए आर्टफ़ैक्ट में बदलाव किए जा सकते हैं.वैरिएंट बनाना: बनाए जाने वाले कॉम्पोनेंट और आर्टफ़ैक्ट की सूची अब तय हो गई है और इसमें बदलाव नहीं किया जा सकता.
onVariants()
: इस कॉलबैक में, आपको बनाए गएVariant
ऑब्जेक्ट का ऐक्सेस मिलता है. साथ ही, इनमें मौजूदProperty
वैल्यू के लिए वैल्यू या प्रोवाइडर सेट किए जा सकते हैं, ताकि इन्हें धीरे-धीरे कैलकुलेट किया जा सके.वैरिएंट लॉक करना: वैरिएंट ऑब्जेक्ट अब लॉक हो गए हैं और उनमें बदलाव नहीं किया जा सकता.
बनाए गए टास्क:
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() })
}
जनरेट किए गए सोर्स को बिल्ड में जोड़ना
आपका प्लग इन, जनरेट किए गए कुछ तरह के सोर्स का योगदान दे सकता है. जैसे:
java
डायरेक्ट्री में मौजूद ऐप्लिकेशन कोडres
डायरेक्ट्री में Android रिसॉर्सresources
डायरेक्ट्री में Java रिसॉर्सassets
डायरेक्ट्री में Android ऐसेट
जोड़े जा सकने वाले सोर्स की पूरी सूची के लिए, सोर्स एपीआई देखें.
इस कोड स्निपेट में, 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
में पढ़ा जा सकता है, ताकि उसका विश्लेषण किया जा सके. इसके अलावा, कस्टम 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 बिल्ड सिस्टम के मैन्युअल के ये सेक्शन पढ़ें:
- पसंद के मुताबिक़ ग्रेडल प्लग इन बनाना
- Gradle प्लग इन लागू करना
- Gradle के कस्टम टास्क टाइप बनाना
- लेज़ी कॉन्फ़िगरेशन
- टास्क कॉन्फ़िगरेशन से बचना