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 प्लगिन रेसिपी देखें. इनमें, सामान्य तौर पर किए जाने वाले बिल्ड कस्टमाइज़ेशन के उदाहरण दिए गए हैं. हमारे रेफ़रंस दस्तावेज़ में, नए एपीआई के बारे में ज़्यादा जानकारी भी देखी जा सकती है.
AGP API आर्टफ़ैक्ट के हिसाब से कंपाइल करना
AGP को एक्सटेंड करने वाले कस्टम Gradle प्लग इन या बिल्ड लॉजिक (जैसे कि buildSrc डायरेक्ट्री, कन्वेंशन प्लग इन मॉड्यूल या स्टैंडअलोन प्लग इन प्रोजेक्ट में) को लिखने के लिए, पूरे प्लग इन को लागू करने वाले आर्टफ़ैक्ट के बजाय, AGP के gradle-api आर्टफ़ैक्ट के साथ कंपाइल करें.
gradle-api आर्टफ़ैक्ट के ख़िलाफ़ कंपाइल क्यों करें?
AGP, दो मुख्य आर्टफ़ैक्ट पब्लिश करता है:
com.android.tools.build:gradle-api: इसमें AGP के आधिकारिक एपीआई शामिल हैं. इनमें स्टेबल एपीआई (जैसे किAndroidComponentsExtension,Variant, और सार्वजनिक डीएसएल इंटरफ़ेस), इनक्यूबेटिंग एपीआई (@Incubatingके तौर पर मार्क किए गए), और बंद किए गए एपीआई (@Deprecatedके तौर पर मार्क किए गए) शामिल हैं.com.android.tools.build:gradle: इसमें एजीपी के पूरे रनटाइम का इंपलीमेंटेशन शामिल होता है. जैसे, इंटरनल क्लास, बिल्ड टास्क, और कंपाइलर इंटिग्रेशन.
gradle-api आर्टफ़ैक्ट के ख़िलाफ़ कंपाइल करने के कई फ़ायदे हैं:
- इंटरनल एपीआई का इस्तेमाल न करें: इससे यह पक्का होता है कि आपके प्लगिन का कोड सिर्फ़ आधिकारिक एपीआई को ऐक्सेस करता है. इससे इंटरनल इंप्लीमेंटेशन क्लास (
com.android.build.gradle.internal.*) पर अनजाने में होने वाली डिपेंडेंसी को रोका जा सकता है. ये क्लास, बिना किसी चेतावनी के AGP के अलग-अलग वर्शन में बदली या हटाई जा सकती हैं. - तेज़ी से बिल्ड होता है और कम जगह लेता है:
gradle-apiआर्टफ़ैक्ट में, पूरेgradleआर्टफ़ैक्ट की तुलना में डिपेंडेंसी ट्री बहुत छोटा होता है. इससे डाउनलोड का साइज़ कम हो जाता है, क्लासपाथ पॉल्यूशन से बचा जा सकता है, और बिल्ड लॉजिक को तेज़ी से कंपाइल किया जा सकता है. - रनटाइम आइसोलेशन: कस्टम प्लगिन को सिर्फ़ कंपाइलेशन के दौरान एपीआई परिभाषाओं की ज़रूरत होती है. रनटाइम के दौरान, Android ऐप्लिकेशन या लाइब्रेरी प्लगिन लागू होने पर, Android बिल्ड से AGP का पूरा वर्शन मिलता है.
डिपेंडेंसी जोड़ना
अपने प्लगिन प्रोजेक्ट की बिल्ड फ़ाइल या buildSrc डायरेक्ट्री में, gradle-api आर्टफ़ैक्ट को डिपेंडेंसी के तौर पर जोड़ें. gradle-api आर्टफ़ैक्ट को Google की Maven रिपॉज़िटरी पर पब्लिश किया गया है. आर्टफ़ैक्ट की समस्या हल करने के लिए, पक्का करें कि आपके बिल्ड में google() रिपॉज़िटरी शामिल हो. उदाहरण के लिए, इसे dependencyResolutionManagement.repositories ब्लॉक में कॉन्फ़िगर करें. यह settings.gradle या settings.gradle.kts फ़ाइल में होता है. इसके अलावा, इसे अपने प्लगिन की बिल्ड फ़ाइल के repositories ब्लॉक या buildSrc डायरेक्ट्री की बिल्ड फ़ाइल में भी कॉन्फ़िगर किया जा सकता है.
वर्शन कैटलॉग के साथ
अगर आपका प्रोजेक्ट वर्शन कैटलॉग का इस्तेमाल करता है, तो gradle/libs.versions.toml फ़ाइल में gradle-api डिपेंडेंसी तय करें:
[versions]
androidGradlePlugin = "9.4.0"
[libraries]
android-gradle-plugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }
android-gradle-plugin-api कैटलॉग एलियास, टाइप-सेफ़ ऐक्सेसर libs.android.gradle.plugin.api जनरेट करता है.
इसके बाद, अपने प्लगिन की बिल्ड फ़ाइल के dependencies ब्लॉक में libs.android.gradle.plugin.api जोड़ें:
Kotlin
dependencies { compileOnly(libs.android.gradle.plugin.api) }
Groovy
dependencies { compileOnly libs.android.gradle.plugin.api }
वर्शन कैटलॉग के बिना
अगर आपके प्रोजेक्ट में वर्शन कैटलॉग का इस्तेमाल नहीं किया जाता है, तो अपने प्लगिन की बिल्ड फ़ाइल में सीधे तौर पर डिपेंडेंसी का एलान करें:
Kotlin
dependencies { compileOnly("com.android.tools.build:gradle-api:9.4.0") }
Groovy
dependencies { compileOnly 'com.android.tools.build:gradle-api:9.4.0' }
पब्लिश करने के लिए बनाए गए स्टैंडअलोन प्लगिन के लिए, compileOnly कॉन्फ़िगरेशन विकल्प को डिफ़ॉल्ट के तौर पर इस्तेमाल करें. इस्तेमाल करने वाला प्रोजेक्ट, AGP लागू करता है और बिल्डस्क्रिप्ट क्लासपाथ पर अपनी रनटाइम क्लास उपलब्ध कराता है. compileOnly कॉन्फ़िगरेशन का इस्तेमाल करने से, पब्लिश किया गया आपका प्लगिन, किसी खास AGP वर्शन पर रनटाइम डिपेंडेंसी को लीक नहीं करता है. साथ ही, इससे उपभोक्ताओं के लिए वर्शन के टकराव से बचा जा सकता है. अगर आपको इंटरनल बिल्ड लॉजिक डेवलप करना है, जैसे कि buildSrc डायरेक्ट्री में प्लगिन या कंपोज़िट बिल्ड में कन्वेंशन प्लगिन, तो implementation कॉन्फ़िगरेशन विकल्प का इस्तेमाल करें. ये प्लगिन, बिल्ड के दौरान चलते हैं. साथ ही, इन्हें अपने रनटाइम क्लासपाथ पर AGP की ज़रूरत होती है.
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 का इस्तेमाल करके सेट किया जाता है. वैरिएंट एपीआई की मदद से, आपको इंटरमीडिएट और फ़ाइनल आर्टफ़ैक्ट का ऐक्सेस भी मिलता है. ये आर्टफ़ैक्ट, बिल्ड प्रोसेस के दौरान बनाए जाते हैं. जैसे, क्लास फ़ाइलें, मर्ज किया गया मेनिफ़ेस्ट या 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 सैंपल
भी देखे जा सकते हैं. पक्का करें कि आपका प्लगिन, बिल्ड फ़ाइल में gradle-api आर्टफ़ैक्ट के साथ कंपाइल हो. यहां प्लगिन कोड से कॉलबैक रजिस्टर करने का एक उदाहरण दिया गया है:
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() को कॉल किए जाने तक, 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 source set में ${variant.name} नाम का कस्टम सोर्स फ़ोल्डर कैसे जोड़ा जा सकता है. इसके बाद, Android टूलचेन इस फ़ोल्डर को प्रोसेस करता है.
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
ज़्यादा जानकारी के लिए, addJavaSource रेसिपी देखें.
इस कोड स्निपेट में, कस्टम टास्क से जनरेट किए गए Android संसाधनों वाली डायरेक्ट्री को res source set में जोड़ने का तरीका दिखाया गया है. अन्य सोर्स टाइप के लिए भी यही प्रोसेस अपनाई जाती है.
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 से जनरेट की गई मेनिफ़ेस्ट फ़ाइल के कॉन्टेंट से, फ़ाइल के कॉन्टेंट को पूरी तरह से बदला जा सकता है.
Artifact क्लास के रेफ़रंस दस्तावेज़ में, फ़िलहाल काम करने वाले आर्टफ़ैक्ट की सूची देखी जा सकती है. हर आर्टफ़ैक्ट टाइप की कुछ प्रॉपर्टी होती हैं, जिनके बारे में जानना ज़रूरी होता है:
एलिमेंट की संख्या
किसी Artifact की कार्डिनलिटी से, उसके FileSystemLocation इंस्टेंस की संख्या या आर्टफ़ैक्ट टाइप की फ़ाइलों या डायरेक्ट्री की संख्या का पता चलता है. किसी आर्टफ़ैक्ट की कार्डिनलिटी के बारे में जानकारी पाने के लिए, उसकी पैरंट क्लास देखें: एक FileSystemLocation वाले आर्टफ़ैक्ट, Artifact.Single की सबक्लास होंगे; एक से ज़्यादा FileSystemLocation इंस्टेंस वाले आर्टफ़ैक्ट, Artifact.Multiple की सबक्लास होंगे.
FileSystemLocation तरह का
पैरामीटर वाले FileSystemLocation टाइप को देखकर, यह पता लगाया जा सकता है कि कोई Artifact फ़ाइलों या डायरेक्ट्री को दिखाता है या नहीं. यह RegularFile या Directory हो सकता है.
इन कार्रवाइयों के लिए यह सुविधा उपलब्ध है
हर 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 टास्क टाइप डेवलप करना
- लेज़ी कॉन्फ़िगरेशन
- टास्क कॉन्फ़िगरेशन से बचना