Android Gradle प्लगिन (AGP), Android ऐप्लिकेशन के लिए आधिकारिक बिल्ड सिस्टम है. इसमें कई तरह के सोर्स को कंपाइल करने और उन्हें एक साथ लिंक करने की सुविधा शामिल है. इससे आपको एक ऐसा ऐप्लिकेशन मिलता है जिसे किसी Android डिवाइस या एम्युलेटर पर चलाया जा सकता है.
AGP में प्लगिन के लिए एक्सटेंशन पॉइंट होते हैं. इनकी मदद से, बिल्ड इनपुट को कंट्रोल किया जा सकता है. साथ ही, नए चरणों के ज़रिए इसकी सुविधाओं को बढ़ाया जा सकता है. इन चरणों को स्टैंडर्ड बिल्ड टास्क के साथ इंटिग्रेट किया जा सकता है. AGP के पिछले वर्शन में, आधिकारिक एपीआई को इंटरनल इंप्लीमेंटेशन से अलग नहीं किया गया था. AGP के 7.0 वर्शन से, इसमें आधिकारिक और स्टेबल एपीआई का एक सेट है. इन पर भरोसा किया जा सकता है.
AGP API का लाइफ़साइकल
AGP, अपने एपीआई की स्थिति तय करने के लिए Gradle की सुविधा के लाइफ़साइकल का पालन करता है:
- इंटरनल: इसका इस्तेमाल सार्वजनिक तौर पर नहीं किया जा सकता
- इनक्यूबेटिंग: सार्वजनिक तौर पर इस्तेमाल के लिए उपलब्ध है, लेकिन फ़ाइनल नहीं है. इसका मतलब है कि ये फ़ाइनल वर्शन में बैकवर्ड कंपैटिबल नहीं हो सकती हैं
- सार्वजनिक तौर पर उपलब्ध: सार्वजनिक तौर पर इस्तेमाल के लिए उपलब्ध और स्थिर
- अब इस्तेमाल नहीं किया जाता: अब काम नहीं करता. इसकी जगह नए एपीआई का इस्तेमाल किया जाता है
समर्थन नहीं होने की नीति
AGP में बदलाव हो रहा है. पुराने एपीआई बंद किए जा रहे हैं और उनकी जगह नए, स्टेबल एपीआई और नई डोमेन स्पेसिफ़िक लैंग्वेज (डीएसएल) का इस्तेमाल किया जा रहा है. यह बदलाव, 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बनाता है,Provider<S>.map()के लिए Lambda आर्ग्युमेंट,Tवैल्यू लेता है औरSवैल्यू जनरेट करता है. लैम्ब्डा को तुरंत लागू नहीं किया जाता. इसके बजाय, इसे तब लागू किया जाता है, जब नतीजे के तौर पर मिलेget()परget()को कॉल किया जाता है. इससे पूरी चेन लेज़ी हो जाती है.Provider<S>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() कॉल की एक सीरीज़ शुरू हो जाती है. यह सीरीज़, प्रोवाइडर में मौजूद टास्क डिपेंडेंसी की जानकारी के हिसाब से काम करती है. ज़रूरी वैल्यू को लागू करने के लिए, टास्क को तुरंत नहीं चलाया जाता.
वैरिएंट एपीआई, आर्टफ़ैक्ट, और टास्क
वैरिएंट एपीआई, Android Gradle प्लगिन में एक्सटेंशन का एक तरीका है. इससे आपको Android बिल्ड पर असर डालने वाले अलग-अलग विकल्पों में बदलाव करने की सुविधा मिलती है. आम तौर पर, इन विकल्पों को बिल्ड कॉन्फ़िगरेशन फ़ाइलों में DSL का इस्तेमाल करके सेट किया जाता है. Variant API की मदद से, आपको इंटरमीडिएट और फ़ाइनल आर्टफ़ैक्ट का ऐक्सेस भी मिलता है. ये आर्टफ़ैक्ट, बिल्ड प्रोसेस के दौरान बनाए जाते हैं. जैसे, क्लास फ़ाइलें, मर्ज किया गया मेनिफ़ेस्ट या APK/AAB फ़ाइलें.
Android बिल्ड फ़्लो और एक्सटेंशन पॉइंट
AGP के साथ इंटरैक्ट करते समय, खास तौर पर बनाए गए एक्सटेंशन पॉइंट का इस्तेमाल करें. इसके बजाय, सामान्य Gradle लाइफ़साइकल कॉलबैक (जैसे कि afterEvaluate()) रजिस्टर न करें या Task डिपेंडेंसी सेट अप न करें. AGP के बनाए गए टास्क को लागू करने की जानकारी माना जाता है. इन्हें सार्वजनिक एपीआई के तौर पर नहीं दिखाया जाता. आपको Task ऑब्जेक्ट के इंस्टेंस पाने की कोशिश नहीं करनी चाहिए. साथ ही, Task के नामों का अनुमान नहीं लगाना चाहिए. इसके अलावा, उन Task ऑब्जेक्ट में सीधे तौर पर कॉलबैक या डिपेंडेंसी नहीं जोड़नी चाहिए.
AGP, Task इंस्टेंस बनाने और उन्हें लागू करने के लिए यह तरीका अपनाता है. इससे बिल्ड आर्टफ़ैक्ट जनरेट होते हैं. ऑब्जेक्ट बनाने के मुख्य चरणों
Variant
के बाद, कॉलबैक फ़ंक्शन का इस्तेमाल किया जाता है. इनकी मदद से, बिल्ड के हिस्से के तौर पर बनाए गए कुछ ऑब्जेक्ट में बदलाव किया जा सकता है. ध्यान दें कि सभी कॉलबैक, कॉन्फ़िगरेशन फ़ेज़ के दौरान होते हैं. इस फ़ेज़ के बारे में इस पेज पर बताया गया है. साथ ही, इन्हें तेज़ी से पूरा होना चाहिए. इसके बजाय, किसी भी मुश्किल काम को एक्ज़ीक्यूशन फ़ेज़ के दौरान सही Task इंस्टेंस पर भेज देना चाहिए.
- डीएसएल पार्सिंग: इस दौरान, बिल्ड स्क्रिप्ट का आकलन किया जाता है. साथ ही,
androidब्लॉक से Android डीएसएल ऑब्जेक्ट की अलग-अलग प्रॉपर्टी बनाई जाती हैं और सेट की जाती हैं. नीचे दिए गए सेक्शन में बताए गए Variant API के कॉलबैक भी इस फ़ेज़ के दौरान रजिस्टर किए जाते हैं. 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() की तरह ही, आपकी दी गई सभी वैल्यू को कॉन्फ़िगरेशन के समय हल किया जाना चाहिए. साथ ही, ये बाहरी इनपुट पर निर्भर नहीं होनी चाहिए. 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 के बिल्ड सिस्टम के मैन्युअल में दिए गए ये सेक्शन पढ़ें:
- कस्टम Gradle प्लगिन डेवलप करना
- Gradle प्लगिन लागू करना
- कस्टम Gradle टास्क टाइप डेवलप करना
- लेज़ी कॉन्फ़िगरेशन
- टास्क कॉन्फ़िगरेशन से बचना