अगर आपके ऐप्लिकेशन का minSdk एपीआई लेवल 20 या इससे कम है और आपके ऐप्लिकेशन और उससे जुड़ी
लाइब्रेरी में 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) बाइटकोड की किसी एक फ़ाइल में मौजूद कोड से लागू किया जा सकता है. इस पेज पर, इस सीमा को पार करने का तरीका बताया गया है. इसके लिए, मल्टीडेक्स नाम का ऐप्लिकेशन कॉन्फ़िगरेशन चालू करना होगा. इससे आपका ऐप्लिकेशन एक से ज़्यादा 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
फ़ाइल में, मल्टीडेक्स लाइब्रेरी जोड़ें:
शानदार
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 फ़ाइलों और उनमें मौजूद कोड के ऐक्सेस को मैनेज करती है. इस लाइब्रेरी के मौजूदा वर्शन देखने के लिए, मल्टीडेक्स के वर्शन देखें.
ज़्यादा जानकारी के लिए, अपने ऐप्लिकेशन को मल्टीडेक्स के लिए कॉन्फ़िगर करने का तरीका सेक्शन देखें.Android 5.0 और इसके बाद के वर्शन में मल्टीडेक्स की सुविधा
Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन, ART नाम के रनटाइम का इस्तेमाल करते हैं.
यह रनटाइम, APK फ़ाइलों से एक से ज़्यादा DEX फ़ाइलें लोड करने की सुविधा देता है. ART
ऐप्लिकेशन इंस्टॉल करते समय, पहले से कंपाइल करने की प्रोसेस करता है. इसमें
classesN.dex फ़ाइलों को स्कैन किया जाता है और उन्हें Android डिवाइस पर लागू करने के लिए, एक
OAT फ़ाइल में कंपाइल किया जाता है.
इसलिए, अगर आपका minSdkVersion
21 या इससे ज़्यादा है, तो मल्टीडेक्स की सुविधा डिफ़ॉल्ट रूप से चालू होती है. साथ ही, आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
Android 5.0 के रनटाइम के बारे में ज़्यादा जानने के लिए, Android रनटाइम (ART) और Dalvik लेख पढ़ें.
ध्यान दें: Android Studio का इस्तेमाल करके, अपने ऐप्लिकेशन को चलाने पर, बिल्ड को उन टारगेट डिवाइसों के लिए ऑप्टिमाइज़ किया जाता है जिन पर इसे डिप्लॉय किया जाता है. इसमें, टारगेट डिवाइसों पर Android 5.0 और इसके बाद के वर्शन इस्तेमाल किए जाने पर, मल्टीडेक्स की सुविधा चालू करना शामिल है. यह ऑप्टिमाइज़ेशन सिर्फ़ Android Studio का इस्तेमाल करके, अपने ऐप्लिकेशन को डिप्लॉय करने पर लागू होता है. इसलिए, हो सकता है कि आपको 64 हज़ार रेफ़रंस की सीमा से बचने के लिए, अपने रिलीज़ बिल्ड को मल्टीडेक्स के लिए कॉन्फ़िगर करना पड़े .
64 हज़ार रेफ़रंस की सीमा से बचना
अपने ऐप्लिकेशन को 64 हज़ार या इससे ज़्यादा मेथड रेफ़रंस के इस्तेमाल के लिए कॉन्फ़िगर करने से पहले, अपने ऐप्लिकेशन कोड से कॉल किए जाने वाले रेफ़रंस की कुल संख्या कम करने के लिए कदम उठाएं . इसमें, आपके ऐप्लिकेशन कोड या शामिल की गई लाइब्रेरी से तय किए गए मेथड शामिल हैं .
DEX रेफ़रंस की सीमा तक पहुंचने से बचने के लिए, ये रणनीतियां अपनाई जा सकती हैं:
- अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
- इस बारे में सोचें कि आपके ऐप्लिकेशन में शामिल की गई किसी बड़ी लाइब्रेरी डिपेंडेंसी की वैल्यू, ऐप्लिकेशन में जोड़े जा रहे कोड की मात्रा से ज़्यादा है या नहीं. एक आम, लेकिन समस्या पैदा करने वाला पैटर्न यह है कि कुछ यूटिलिटी मेथड काम के होने की वजह से, बहुत बड़ी लाइब्रेरी शामिल कर ली जाती है. अपने ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, अक्सर DEX रेफ़रंस की सीमा तक पहुंचने से बचा जा सकता है.
- R8 की मदद से, इस्तेमाल न होने वाले कोड को हटाना
- अपने रिलीज़ बिल्ड के लिए R8 चलाने के लिए, कोड छोटा करने की सुविधा चालू करें. कोड छोटा करने की सुविधा चालू करें, ताकि यह पक्का किया जा सके कि आपके APK में इस्तेमाल न होने वाला कोड शामिल न हो. अगर कोड छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो यह कोड छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो यह आपकी डिपेंडेंसी से इस्तेमाल न होने वाले कोड और संसाधनों को भी हटा सकती है.
इन तकनीकों का इस्तेमाल करके, अपने APK का कुल साइज़ कम किया जा सकता है. साथ ही, अपने ऐप्लिकेशन में मल्टीडेक्स की ज़रूरत से बचा जा सकता है.
अपने ऐप्लिकेशन को मल्टीडेक्स के लिए कॉन्फ़िगर करना
ध्यान दें: अगर आपकाminSdkVersion 21 या इससे ज़्यादा है, तो मल्टीडेक्स की सुविधा डिफ़ॉल्ट रूप से चालू होती है. साथ ही, आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
अगर आपका minSdkVersion 20 या इससे कम है, तो आपको
मल्टीडेक्स लाइब्रेरी का इस्तेमाल करना होगा. साथ ही, अपने ऐप्लिकेशन प्रोजेक्ट में ये बदलाव करने होंगे:
-
मल्टीडेक्स की सुविधा चालू करने और मल्टीडेक्स लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ने के लिए, मॉड्यूल-लेवल की
build.gradleफ़ाइल में बदलाव करें. जैसा कि यहां दिखाया गया है:शानदार
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क्लास को ओवरराइड नहीं किया जाता है, तोandroid:nameको<application>टैग में इस तरह सेट करने के लिए, अपनी मेनिफ़ेस्ट फ़ाइल में बदलाव करें:<?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); } }
चेतावनी: या किसी अन्य कोड को रिफ़्लेक्शन या JNI के ज़रिए तब तक लागू न करें, जब तक
MultiDex.install()की प्रोसेस पूरी न हो जाए.MultiDex.install()मल्टीडेक्स ट्रेसिंग, उन कॉल को फ़ॉलो नहीं करेगी. इससे, DEX फ़ाइलों के बीच क्लास के गलत पार्टीशन की वजह से,ClassNotFoundExceptionया पुष्टि करने से जुड़ी गड़बड़ियां हो सकती हैं.
अब अपने ऐप्लिकेशन को बिल्ड करने पर, Android बिल्ड टूल, ज़रूरत के हिसाब से प्राइमरी DEX फ़ाइल (classes.dex) और साथ में इस्तेमाल होने वाली DEX फ़ाइलें (classes2.dex, classes3.dex वगैरह) बनाते हैं.
इसके बाद, बिल्ड सिस्टम, सभी DEX फ़ाइलों को आपके APK में पैकेज करता है.
रनटाइम के दौरान, मल्टीडेक्स एपीआई, सिर्फ़ मुख्य
classes.dex फ़ाइल में खोजने के बजाय, आपके मेथड के लिए उपलब्ध सभी
DEX फ़ाइलों को खोजने के लिए, खास क्लास लोडर का इस्तेमाल करते हैं.
मल्टीडेक्स लाइब्रेरी की सीमाएं
मल्टीडेक्स लाइब्रेरी की कुछ तय सीमाएं हैं. अपने ऐप्लिकेशन के बिल्ड कॉन्फ़िगरेशन में लाइब्रेरी शामिल करते समय, इन बातों का ध्यान रखें:
- स्टार्टअप के दौरान, किसी डिवाइस के डेटा पार्टीशन पर DEX फ़ाइलें इंस्टॉल करना मुश्किल होता है और अगर सेकंडरी DEX फ़ाइलें बड़ी हैं, तो इससे ऐप्लिकेशन के काम न करने (ANR) से जुड़ी गड़बड़ियां हो सकती हैं. इस समस्या से बचने के लिए, कोड छोटा करने की सुविधा चालू करें, ताकि DEX फ़ाइलों का साइज़ कम किया जा सके और कोड के इस्तेमाल न होने वाले हिस्सों को हटाया जा सके.
- Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन पर चलाने के दौरान, linearalloc की सीमा (समस्या 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 में बताई गई फ़ाइल में, -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
इसके बाद, किसी बिल्ड टाइप के लिए उस फ़ाइल का एलान किया जा सकता है. जैसे:
शानदार
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
डेवलपमेंट बिल्ड में मल्टीडेक्स को ऑप्टिमाइज़ करना
मल्टीडेक्स कॉन्फ़िगरेशन के लिए, बिल्ड प्रोसेस करने में ज़्यादा समय लगता है, क्योंकि बिल्ड सिस्टम को यह तय करने के लिए जटिल फ़ैसले लेने होते हैं कि प्राइमरी DEX फ़ाइल में किन क्लास को शामिल किया जाना चाहिए और सेकंडरी DEX फ़ाइलों में किन क्लास को शामिल किया जा सकता है. इसका मतलब है कि मल्टीडेक्स का इस्तेमाल करके, इंक्रीमेंटल बिल्ड में आम तौर पर ज़्यादा समय लगता है. साथ ही, इससे आपकी डेवलपमेंट प्रोसेस धीमी हो सकती है.
इंक्रीमेंटल बिल्ड में लगने वाले ज़्यादा समय को कम करने के लिए,
प्री-डेक्सिंग का इस्तेमाल करें, ताकि बिल्ड के बीच मल्टीडेक्स आउटपुट को फिर से इस्तेमाल किया जा सके.
प्री-डेक्सिंग, ART फ़ॉर्मैट पर काम करती है. यह फ़ॉर्मैट, सिर्फ़ Android 5.0
(एपीआई लेवल 21) और इसके बाद के वर्शन पर उपलब्ध है. अगर Android Studio का इस्तेमाल किया जा रहा है, तो IDE, Android 5.0 (एपीआई लेवल 21) या इसके बाद के वर्शन पर चलने वाले डिवाइस पर, आपके ऐप्लिकेशन को डिप्लॉय करते समय, अपने-आप प्री-डेक्सिंग
का इस्तेमाल करता है.
हालांकि, अगर कमांड लाइन से Gradle बिल्ड चलाए जा रहे हैं, तो प्री-डेक्सिंग की सुविधा चालू करने के लिए,
minSdkVersion को 21 या इससे ज़्यादा पर सेट करना होगा.
minSdkVersion के लिए अलग-अलग वैल्यू सेट की जा सकती हैं. जैसा कि यहां दिखाया गया है:
शानदार
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); ... }