अगर आपके ऐप्लिकेशन में एपीआई 20 या इससे पुराना minSdk है और आपके ऐप्लिकेशन और उससे जुड़ी लाइब्रेरी में 65,536 से ज़्यादा तरीके हैं, तो आपको यह बिल्ड गड़बड़ी दिखेगी. इससे पता चलता है कि आपका ऐप्लिकेशन, Android बिल्ड आर्किटेक्चर की सीमा तक पहुंच गया है:
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
बिल्ड सिस्टम के पुराने वर्शन में, अलग गड़बड़ी की सूचना मिलती है. इससे इसी समस्या का पता चलता है:
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
गड़बड़ी की इन स्थितियों में, एक ही नंबर दिखता है: 65536. यह संख्या, उन रेफ़रंस की कुल संख्या दिखाती है जिन्हें एक ही Dalvik Executable (DEX) बाइटकोड फ़ाइल में मौजूद कोड से शुरू किया जा सकता है. इस पेज पर बताया गया है कि इस सीमा को कैसे पार किया जा सकता है. इसके लिए, multidex नाम का ऐप्लिकेशन कॉन्फ़िगरेशन चालू करें. इससे आपका ऐप्लिकेशन, एक से ज़्यादा DEX फ़ाइलें बना और पढ़ सकता है.
रेफ़रंस की 64 हज़ार की सीमा के बारे में जानकारी
Android ऐप्लिकेशन (APK) फ़ाइलों में, एक्ज़ीक्यूटेबल बाइटकोड फ़ाइलें होती हैं. ये फ़ाइलें, Dalvik Executable (DEX) फ़ाइलों के तौर पर होती हैं. इनमें कंपाइल किया गया कोड होता है, जिसका इस्तेमाल आपके ऐप्लिकेशन को चलाने के लिए किया जाता है. Dalvik Executable स्पेसिफ़िकेशन के मुताबिक, किसी एक DEX फ़ाइल में ज़्यादा से ज़्यादा 65,536 तरीकों का रेफ़रंस दिया जा सकता है. इनमें Android फ़्रेमवर्क के तरीके, लाइब्रेरी के तरीके, और आपके कोड में मौजूद तरीके शामिल हैं.
कंप्यूटर साइंस के हिसाब से, किलो या K का मतलब 1024 (या 2^10) होता है. 65,536, 64x1024 के बराबर होता है. इसलिए, इस सीमा को _64 हज़ार रेफ़रंस की सीमा_ कहा जाता है.Android 5.0 से पहले के वर्शन में मल्टीडेक्स की सुविधा
Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन वाले प्लैटफ़ॉर्म, ऐप्लिकेशन कोड को लागू करने के लिए Dalvik रनटाइम का इस्तेमाल करते हैं. डिफ़ॉल्ट रूप से, Dalvik हर APK के लिए ऐप्लिकेशन को सिर्फ़ एक classes.dex बाइटकोड फ़ाइल इस्तेमाल करने की अनुमति देता है. इस सीमा से बचने के लिए, मॉड्यूल-लेवल की build.gradle या build.gradle.kts फ़ाइल में multidex लाइब्रेरी जोड़ें:
Groovy
dependencies { def multidex_version = "2.0.1" implementation "androidx.multidex:multidex:$multidex_version" }
Kotlin
dependencies { val multidex_version = "2.0.1" implementation("androidx.multidex:multidex:$multidex_version") }
यह लाइब्रेरी, आपके ऐप्लिकेशन की प्राइमरी DEX फ़ाइल का हिस्सा बन जाती है. इसके बाद, यह अतिरिक्त DEX फ़ाइलों और उनमें मौजूद कोड के ऐक्सेस को मैनेज करती है. इस लाइब्रेरी के मौजूदा वर्शन देखने के लिए, multidex के वर्शन देखें.
ज़्यादा जानकारी के लिए, अपने ऐप्लिकेशन को मल्टीडेक्स के लिए कॉन्फ़िगर करने के तरीके के बारे में सेक्शन देखें.Android 5.0 और इसके बाद के वर्शन के लिए मल्टीडेक्स की सुविधा
Android 5.0 (एपीआई लेवल 21) और उसके बाद के वर्शन में, एआरटी नाम का रनटाइम इस्तेमाल किया जाता है. यह APK फ़ाइलों से कई DEX फ़ाइलों को लोड करने की सुविधा देता है. ART, ऐप्लिकेशन इंस्टॉल करने के दौरान प्री-कंपाइलेशन करता है. यह classesN.dex फ़ाइलों को स्कैन करता है और उन्हें Android डिवाइस पर एक्ज़ीक्यूट करने के लिए, एक OAT फ़ाइल में कंपाइल करता है. इसलिए, अगर आपका minSdkVersion
21 या इससे ज़्यादा है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है. इसके लिए, आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
Android 5.0 के रनटाइम के बारे में ज़्यादा जानने के लिए, Android Runtime (ART) और Dalvik लेख पढ़ें.
ध्यान दें: Android Studio का इस्तेमाल करके ऐप्लिकेशन चलाने पर, बिल्ड को उन टारगेट डिवाइसों के लिए ऑप्टिमाइज़ किया जाता है जिन पर ऐप्लिकेशन को डिप्लॉय किया जाता है. इसमें, टारगेट डिवाइसों पर Android 5.0 और उसके बाद के वर्शन का इस्तेमाल करने पर मल्टीडेक्स को चालू करना शामिल है. यह ऑप्टिमाइज़ेशन सिर्फ़ तब लागू होता है, जब Android Studio का इस्तेमाल करके ऐप्लिकेशन डिप्लॉय किया जाता है. इसलिए, आपको अब भी मल्टीडेक्स के लिए रिलीज़ बिल्ड कॉन्फ़िगर करना पड़ सकता है, ताकि 64K की सीमा से बचा जा सके.
64 केबी की सीमा से बचें
अपने ऐप्लिकेशन को 64 हज़ार या उससे ज़्यादा मेथड रेफ़रंस का इस्तेमाल करने के लिए कॉन्फ़िगर करने से पहले, अपने ऐप्लिकेशन के कोड से कॉल किए गए रेफ़रंस की कुल संख्या कम करें. इनमें आपके ऐप्लिकेशन के कोड या शामिल की गई लाइब्रेरी से तय की गई मेथड शामिल हैं.
नीचे दी गई रणनीतियों से, आपको DEX रेफ़रंस की सीमा से बचने में मदद मिल सकती है:
- अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
- देखें कि आपके ऐप्लिकेशन में शामिल की गई किसी बड़ी लाइब्रेरी डिपेंडेंसी की वैल्यू, ऐप्लिकेशन में जोड़े जा रहे कोड की मात्रा से ज़्यादा तो नहीं है. एक सामान्य लेकिन समस्या पैदा करने वाला पैटर्न यह है कि कुछ यूटिलिटी मेथड काम के थे, इसलिए बहुत बड़ी लाइब्रेरी को शामिल कर लिया गया. ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, आपको अक्सर DEX रेफ़रंस की सीमा से बचने में मदद मिल सकती है.
- R8 की मदद से, इस्तेमाल न किए गए कोड को हटाना
- रिलीज़ के लिए तैयार बिल्ड के लिए R8 चलाने के लिए, कोड छोटा करने की सुविधा चालू करें. कोड छोटा करने की सुविधा चालू करें, ताकि यह पक्का किया जा सके कि आपके APK में इस्तेमाल न होने वाला कोड शामिल न हो. अगर कोड छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो यह आपकी डिपेंडेंसी से इस्तेमाल न होने वाले कोड और संसाधनों को भी हटा सकती है.
इन तकनीकों का इस्तेमाल करके, अपने APK का कुल साइज़ कम किया जा सकता है. साथ ही, अपने ऐप्लिकेशन में मल्टीडेक्स की ज़रूरत से बचा जा सकता है.
मल्टीडेक्स के लिए अपने ऐप्लिकेशन को कॉन्फ़िगर करना
ध्यान दें: अगर आपकाminSdkVersion 21 या इससे ज़्यादा पर सेट है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है. ऐसे में, आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
अगर आपके ऐप्लिकेशन का minSdkVersion 20 या इससे कम पर सेट है, तो आपको multidex लाइब्रेरी का इस्तेमाल करना होगा. साथ ही, अपने ऐप्लिकेशन प्रोजेक्ट में ये बदलाव करने होंगे:
-
मॉड्यूल-लेवल की
build.gradleफ़ाइल में बदलाव करके, मल्टीडेक्स की सुविधा चालू करें. साथ ही, मल्टीडेक्स लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ें. इसके लिए, यहां दिया गया तरीका अपनाएं:Groovy
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 36 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 36 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
Applicationक्लास को बदलने के आधार पर, इनमें से कोई एक काम करें:अगर आपको
Applicationक्लास को नहीं बदलना है, तो अपनी मेनिफ़ेस्ट फ़ाइल में बदलाव करके,<application>टैग मेंandroid:nameको इस तरह सेट करें:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="androidx.multidex.MultiDexApplication" > ... </application> </manifest>
अगर आपको
Applicationक्लास को बदलना है, तो इसेMultiDexApplicationमें बदलें. इसके लिए, यह तरीका अपनाएं:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
अगर आपने
Applicationक्लास को ओवरराइड किया है, लेकिन बेस क्लास को बदला नहीं जा सकता, तोattachBaseContext()तरीके को ओवरराइड करें और मल्टीडेक्स को चालू करने के लिएMultiDex.install(this)को कॉल करें:Kotlin
class MyApplication : SomeOtherApplication() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } }
Java
public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
चेतावनी:
MultiDex.install()पूरा होने से पहले, रिफ़्लेक्शन या JNI के ज़रिएMultiDex.install()या कोई अन्य कोड न चलाएं. मल्टीडेक्स ट्रेसिंग, उन कॉल को फ़ॉलो नहीं करेगी. इस वजह से, DEX फ़ाइलों के बीच क्लास का गलत बंटवारा होने की वजह से,ClassNotFoundExceptionया पुष्टि करने से जुड़ी गड़बड़ियां होंगी.
अब जब ऐप्लिकेशन बनाया जाता है, तो Android Build Tools, ज़रूरत के मुताबिक एक प्राइमरी DEX फ़ाइल (classes.dex) और सपोर्ट करने वाली DEX फ़ाइलें (classes2.dex, classes3.dex वगैरह) बनाते हैं.
इसके बाद, बिल्ड सिस्टम सभी DEX फ़ाइलों को आपके APK में पैकेज करता है.
रनटाइम के दौरान, मल्टीडेक्स एपीआई सिर्फ़ मुख्य classes.dex फ़ाइल में खोजने के बजाय, एक खास क्लास लोडर का इस्तेमाल करते हैं. इससे वे आपके तरीकों के लिए, उपलब्ध सभी DEX फ़ाइलों को खोज पाते हैं.
multidex लाइब्रेरी की सीमाएं
मल्टीडेक्स लाइब्रेरी की कुछ सीमाएं हैं. लाइब्रेरी को अपने ऐप्लिकेशन के बिल्ड कॉन्फ़िगरेशन में शामिल करते समय, इन बातों का ध्यान रखें:
- स्टार्टअप के दौरान, डिवाइस के डेटा पार्टीशन पर DEX फ़ाइलों को इंस्टॉल करना मुश्किल होता है. साथ ही, अगर सेकंडरी DEX फ़ाइलें बड़ी हैं, तो इससे ऐप्लिकेशन में कोई समस्या होने (ANR) की गड़बड़ियां हो सकती हैं. इस समस्या से बचने के लिए, कोड छोटा करने की सुविधा चालू करें. इससे DEX फ़ाइलों का साइज़ कम हो जाएगा और कोड के इस्तेमाल न होने वाले हिस्से हट जाएंगे.
- Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन पर, multidex का इस्तेमाल करने से लीनियरएलॉक की सीमा (समस्या 37008143) को ठीक नहीं किया जा सकता. Android 4.0 (एपीआई लेवल 14) में इस सीमा को बढ़ा दिया गया था. हालांकि, इससे समस्या पूरी तरह से हल नहीं हुई.
Android 4.0 से पहले के वर्शन में, DEX इंडेक्स की सीमा तक पहुंचने से पहले ही, LinearAlloc की सीमा पूरी हो सकती है. इसलिए, अगर आपको 14 से कम एपीआई लेवल को टारगेट करना है, तो प्लैटफ़ॉर्म के उन वर्शन पर अच्छी तरह से जांच करें. ऐसा इसलिए, क्योंकि हो सकता है कि आपके ऐप्लिकेशन को स्टार्टअप के समय या क्लास के कुछ ग्रुप लोड करते समय समस्याएं आएं.
कोड श्रिंकिंग से इन समस्याओं को कम किया जा सकता है या हो सकता है कि ये समस्याएं पूरी तरह से खत्म हो जाएं.
मुख्य DEX फ़ाइल में ज़रूरी क्लास का एलान करना
मल्टीडेक्स ऐप्लिकेशन के लिए हर DEX फ़ाइल बनाते समय, बिल्ड टूल यह तय करने के लिए जटिल फ़ैसले लेते हैं कि प्राइमरी DEX फ़ाइल में किन क्लास की ज़रूरत है, ताकि आपका ऐप्लिकेशन सही तरीके से शुरू हो सके. अगर स्टार्टअप के दौरान ज़रूरी कोई क्लास, प्राइमरी DEX फ़ाइल में नहीं दी गई है, तो आपका ऐप्लिकेशन java.lang.NoClassDefFoundError गड़बड़ी के साथ क्रैश हो जाएगा.
बिल्ड टूल, ऐसे कोड के पाथ को पहचानते हैं जिसे सीधे आपके ऐप्लिकेशन कोड से ऐक्सेस किया जाता है. हालांकि, यह समस्या तब हो सकती है, जब कोड पाथ कम दिखते हों. जैसे, जब इस्तेमाल की जा रही लाइब्रेरी में जटिल डिपेंडेंसी हों. उदाहरण के लिए, अगर कोड में नेटिव कोड से Java के तरीकों के इंट्रोस्पेक्शन या इनवोकेशन का इस्तेमाल किया जाता है, तो हो सकता है कि उन क्लास को मुख्य DEX फ़ाइल में ज़रूरी के तौर पर न पहचाना जाए.
अगर आपको java.lang.NoClassDefFoundError मिलता है, तो आपको मुख्य DEX फ़ाइल में ज़रूरी अतिरिक्त क्लास के बारे में मैन्युअल तरीके से जानकारी देनी होगी. इसके लिए, आपको अपने बिल्ड टाइप में multiDexKeepProguard प्रॉपर्टी का इस्तेमाल करके उन्हें एलान करना होगा. अगर कोई क्लास multiDexKeepProguard फ़ाइल में मैच होती है, तो उस क्लास को प्राइमरी DEX फ़ाइल में जोड़ दिया जाता है.
multiDexKeepProguard प्रॉपर्टी
multiDexKeepProguard फ़ाइल, ProGuard के फ़ॉर्मैट का इस्तेमाल करती है. साथ ही, यह ProGuard के पूरे व्याकरण के साथ काम करती है. अपने ऐप्लिकेशन में कौनसी चीज़ें रखनी हैं, इस बारे में ज़्यादा जानने के लिए, यह तय करना कि कौनसे कोड को रखना है लेख पढ़ें.
multiDexKeepProguard में बताई गई फ़ाइल में, ProGuard के किसी भी मान्य सिंटैक्स में -keep विकल्प होने चाहिए. उदाहरण के लिए,
-keep com.example.MyClass.class. multidex-config.pro नाम की फ़ाइल बनाई जा सकती है. यह ऐसी दिखती है:
-keep class com.example.MyClass -keep class com.example.MyClassToo
अगर आपको किसी पैकेज में मौजूद सभी क्लास के बारे में बताना है, तो फ़ाइल इस तरह दिखेगी:
-keep class com.example.** { *; } // All classes in the com.example package
इसके बाद, किसी बिल्ड टाइप के लिए उस फ़ाइल का एलान किया जा सकता है. इसके लिए, यह तरीका अपनाएं:
Groovy
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
डेवलपमेंट बिल्ड में मल्टीडेक्स को ऑप्टिमाइज़ करना
मल्टीडेक्स कॉन्फ़िगरेशन के लिए, बिल्ड प्रोसेसिंग का समय काफ़ी बढ़ जाता है. ऐसा इसलिए होता है, क्योंकि बिल्ड सिस्टम को यह तय करने के लिए मुश्किल फ़ैसले लेने होते हैं कि प्राइमरी DEX फ़ाइल में कौनसी क्लास शामिल की जानी चाहिए और सेकंडरी DEX फ़ाइलों में कौनसी क्लास शामिल की जा सकती हैं. इसका मतलब है कि मल्टीडेक्स का इस्तेमाल करके इंक्रीमेंटल बिल्ड बनाने में आम तौर पर ज़्यादा समय लगता है. इससे डेवलपमेंट की प्रोसेस धीमी हो सकती है.
इंक्रीमेंटल बिल्ड में ज़्यादा समय लगने की समस्या को कम करने के लिए, प्री-डेक्सिंग का इस्तेमाल करें. इससे बिल्ड के बीच मल्टीडेक्स आउटपुट को फिर से इस्तेमाल किया जा सकता है.
प्री-डेक्सिंग, एआरटी फ़ॉर्मैट पर काम करती है. यह फ़ॉर्मैट सिर्फ़ Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन पर उपलब्ध है. अगर Android Studio का इस्तेमाल किया जा रहा है, तो Android 5.0 (एपीआई लेवल 21) या इसके बाद के वर्शन वाले डिवाइस पर ऐप्लिकेशन डिप्लॉय करते समय, IDE अपने-आप प्री-डेक्सिंग का इस्तेमाल करता है.
हालांकि, अगर कमांड लाइन से Gradle बिल्ड चलाए जा रहे हैं, तो प्री-डेक्सिंग की सुविधा चालू करने के लिए, आपको minSdkVersion को 21 या उससे ज़्यादा पर सेट करना होगा.
minSdkVersion के लिए अलग-अलग वैल्यू होंगी, जैसा कि दिखाया गया है:
Groovy
android { defaultConfig { ... multiDexEnabled true // The default minimum API level you want to support. minSdkVersion 15 } productFlavors { // Includes settings you want to keep only while developing your app. dev { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdkVersion 21 } prod { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... multiDexEnabled = true // The default minimum API level you want to support. minSdk = 15 } productFlavors { // Includes settings you want to keep only while developing your app. create("dev") { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdk = 21 } create("prod") { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
Android Studio या कमांड लाइन से, बिल्ड की स्पीड को बेहतर बनाने में मदद करने वाली अन्य रणनीतियों के बारे में जानने के लिए, बिल्ड की स्पीड को ऑप्टिमाइज़ करना लेख पढ़ें. बिल्ड वैरिएंट इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, बिल्ड वैरिएंट कॉन्फ़िगर करना लेख पढ़ें.
अहम जानकारी: अगर आपको मल्टीडेक्स की अलग-अलग ज़रूरतों के लिए, अलग-अलग बिल्ड वैरिएंट चाहिए, तो हर वैरिएंट के लिए अलग मेनिफ़ेस्ट फ़ाइल दी जा सकती है. इससे, एपीआई लेवल 20 और इससे कम लेवल के लिए सिर्फ़ फ़ाइल, <application> टैग का नाम बदलती है. आपके पास हर वैरिएंट के लिए, अलग Application सबक्लास बनाने का विकल्प भी है. इससे, एपीआई लेवल 20 और उससे कम के लिए सिर्फ़ सबक्लास, MultiDexApplication क्लास को बढ़ाता है या MultiDex.install(this) को कॉल करता है.
मल्टीडेक्स ऐप्लिकेशन की जांच करना
मल्टीडेक्स ऐप्लिकेशन के लिए इंस्ट्रुमेंटेशन टेस्ट लिखते समय, अगर
MonitoringInstrumentation या
AndroidJUnitRunner इंस्ट्रुमेंटेशन का इस्तेमाल किया जाता है, तो किसी और कॉन्फ़िगरेशन की ज़रूरत नहीं होती. अगर किसी दूसरे Instrumentation का इस्तेमाल किया जाता है, तो आपको उसके onCreate() तरीके को इस कोड से बदलना होगा:
Kotlin
fun onCreate(arguments: Bundle) { MultiDex.install(targetContext) super.onCreate(arguments) ... }
Java
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }