अगर आपके ऐप्लिकेशन का 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 फ़ाइलें बना सकता है और उन्हें पढ़ सकता है.
64K रेफ़रंस की सीमा के बारे में जानकारी
Android ऐप्लिकेशन (APK) फ़ाइलों में, Dalvik Executable (DEX) फ़ाइलों के तौर पर एक्ज़ीक्यूटेबल बाइट कोड फ़ाइलें होती हैं. इनमें कंपाइल किया गया कोड होता है, जिसका इस्तेमाल आपके ऐप्लिकेशन को चलाने के लिए किया जाता है. Dalvik Executable की खास जानकारी के मुताबिक, एक DEX फ़ाइल में ज़्यादा से ज़्यादा 65,536 तरीकों का रेफ़रंस दिया जा सकता है. इनमें Android फ़्रेमवर्क के तरीके, लाइब्रेरी के तरीके, और आपके कोड में मौजूद तरीके शामिल हैं.
कंप्यूटर साइंस में, kilo या K का मतलब 1024 (या 2^10) होता है. 65,536, 64x1024 के बराबर होता है. इसलिए, इस सीमा को _64K रेफ़रंस सीमा_ कहा जाता है.Android 5.0 से पहले के वर्शन के लिए मल्टीडेक्स की सुविधा
Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन वाले प्लैटफ़ॉर्म, ऐप्लिकेशन कोड को लागू करने के लिए Dalvik रनटाइम का इस्तेमाल करते हैं. डिफ़ॉल्ट रूप से, Dalvik हर APK के लिए ऐप्लिकेशन को एक classes.dex बाइट कोड फ़ाइल तक सीमित करता है. इस सीमा से बचने के लिए, मॉड्यूल-लेवल की build.gradle या build.gradle.kts फ़ाइल में मल्टीडेक्स लाइब्रेरी जोड़ें:
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 फ़ाइलों और उनमें मौजूद कोड के ऐक्सेस को मैनेज करती है. इस लाइब्रेरी के मौजूदा वर्शन देखने के लिए, मल्टीडेक्स के वर्शन देखें.
ज़्यादा जानकारी के लिए, मल्टीडेक्स के लिए अपने ऐप्लिकेशन को कॉन्फ़िगर करने का तरीका बताने वाला सेक्शन देखें.Android 5.0 और इसके बाद के वर्शन के लिए मल्टीडेक्स की सुविधा
Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन, एआरटी नाम के रनटाइम का इस्तेमाल करते हैं. यह रनटाइम, APK फ़ाइलों से कई DEX फ़ाइलों को लोड करने की सुविधा देता है. ART, ऐप्लिकेशन इंस्टॉल करते समय प्री-कंपाइलेशन करता है. यह classesN.dex फ़ाइलों को स्कैन करता है और उन्हें एक OAT फ़ाइल में कंपाइल करता है, ताकि Android डिवाइस उन्हें एक्ज़ीक्यूट कर सके. इसलिए, अगर आपका minSdkVersion
21 या इससे ज़्यादा है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
Android 5.0 के रनटाइम के बारे में ज़्यादा जानकारी के लिए, Android रनटाइम (ART) और Dalvik लेख पढ़ें.
ध्यान दें: Android Studio का इस्तेमाल करके ऐप्लिकेशन चलाते समय, बिल्ड को उन टारगेट डिवाइसों के लिए ऑप्टिमाइज़ किया जाता है जिन पर ऐप्लिकेशन को डिप्लॉय किया जाता है. इसमें टारगेट डिवाइसों पर Android 5.0 और इसके बाद के वर्शन चलने पर, मल्टीडेक्स को चालू करना शामिल है. यह ऑप्टिमाइज़ेशन सिर्फ़ तब लागू होता है, जब Android Studio का इस्तेमाल करके ऐप्लिकेशन को डिप्लॉय किया जाता है. इसलिए, आपको 64K की सीमा से बचने के लिए, मल्टीडेक्स के लिए रिलीज़ के लिए तैयार बिल्ड को कॉन्फ़िगर करना पड़ सकता है.
64K की सीमा से बचना
अपने ऐप्लिकेशन को 64K या उससे ज़्यादा मेथड रेफ़रंस इस्तेमाल करने के लिए कॉन्फ़िगर करने से पहले, ऐप्लिकेशन कोड से कॉल किए जाने वाले रेफ़रंस की कुल संख्या को कम करने के लिए ज़रूरी कदम उठाएं. इसमें आपके ऐप्लिकेशन कोड या शामिल की गई लाइब्रेरी से तय किए गए तरीके शामिल हैं.
यहां दी गई रणनीतियों की मदद से, DEX रेफ़रंस की सीमा को पार करने से बचा जा सकता है:
- अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
- यह देखें कि आपके ऐप्लिकेशन में शामिल की गई किसी बड़ी लाइब्रेरी डिपेंडेंसी की वैल्यू, ऐप्लिकेशन में जोड़े जा रहे कोड की संख्या से ज़्यादा है या नहीं. एक आम लेकिन समस्या पैदा करने वाला पैटर्न यह है कि बहुत बड़ी लाइब्रेरी को शामिल किया जाए, क्योंकि कुछ यूटिलिटी तरीके काम के थे. अपने ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, DEX रेफ़रंस की सीमा से बचा जा सकता है.
- R8 की मदद से, इस्तेमाल न होने वाले कोड हटाना
- रिलीज़ के लिए तैयार बिल्ड के लिए R8 चलाने के लिए, कोड छोटा करने की सुविधा चालू करें. श्रिंकिंग की सुविधा चालू करें, ताकि यह पक्का किया जा सके कि आपके APKs के साथ इस्तेमाल न किया गया कोड न भेजा जाए. अगर कोड छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो यह आपकी डिपेंडेंसी से, इस्तेमाल न किए गए कोड और संसाधनों को भी हटा सकती है.
इन तकनीकों का इस्तेमाल करके, APK का साइज़ कम किया जा सकता है. साथ ही, ऐप्लिकेशन में मल्टीडेक्स की ज़रूरत से बचा जा सकता है.
मल्टीडेक्स के लिए अपना ऐप्लिकेशन कॉन्फ़िगर करना
ध्यान दें: अगर आपकाminSdkVersion 21 या इससे ज़्यादा पर सेट है, तो मल्टीडेक्स की सुविधा डिफ़ॉल्ट रूप से चालू होती है. साथ ही, आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
अगर आपका minSdkVersion, 20 या इससे कम पर सेट है, तो आपको मल्टीडेक्स लाइब्रेरी का इस्तेमाल करना होगा. साथ ही, अपने ऐप्लिकेशन प्रोजेक्ट में ये बदलाव करने होंगे:
-
मल्टीडेक्स को चालू करने और मल्टीडेक्स लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ने के लिए, मॉड्यूल-लेवल की
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()के पूरा होने से पहले, रिफ़्लेक्शन या जेएनआई के ज़रिएMultiDex.install()या कोई अन्य कोड न चलाएं. मल्टीडेक्स ट्रेसिंग, उन कॉल को फ़ॉलो नहीं करेगी. इससे DEX फ़ाइलों के बीच क्लास के गलत पार्टिशन की वजह से,ClassNotFoundExceptionया पुष्टि करने से जुड़ी गड़बड़ियां हो सकती हैं.
अब जब ऐप्लिकेशन बनाया जाता है, तो Android बिल्ड टूल ज़रूरत के हिसाब से एक प्राइमरी DEX फ़ाइल (classes.dex) और सहायक DEX फ़ाइलें (classes2.dex, classes3.dex वगैरह) बनाते हैं.
इसके बाद, बिल्ड सिस्टम सभी DEX फ़ाइलों को आपके APK में पैकेज करता है.
रनटाइम के दौरान, मल्टीडेक्स एपीआई सिर्फ़ मुख्य classes.dex फ़ाइल में खोजने के बजाय, एक खास क्लास लोडर का इस्तेमाल करते हैं. इससे, आपके तरीकों के लिए उपलब्ध सभी DEX फ़ाइलों को खोजा जाता है.
मल्टीडेक्स लाइब्रेरी की सीमाएं
मल्टीडेक्स लाइब्रेरी की कुछ सीमाएं हैं. अपने ऐप्लिकेशन के बिल्ड कॉन्फ़िगरेशन में लाइब्रेरी को शामिल करते समय, इन बातों का ध्यान रखें:
- डिवाइस के डेटा पार्टिशन पर स्टार्टअप के दौरान, DEX फ़ाइलों को इंस्टॉल करना मुश्किल होता है. साथ ही, अगर सेकंडरी DEX फ़ाइलें बड़ी हैं, तो ऐप्लिकेशन काम नहीं कर रहा है (एएनआर) वाली गड़बड़ियां हो सकती हैं. इस समस्या से बचने के लिए, कोड छोटा करने की सुविधा चालू करें. इससे DEX फ़ाइलों का साइज़ कम हो जाएगा और कोड के ऐसे हिस्से हट जाएंगे जिनका इस्तेमाल नहीं किया जा रहा है.
- Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन पर, मल्टीडेक्स का इस्तेमाल करना linearalloc की सीमा (समस्या 37008143) को हल करने के लिए काफ़ी नहीं है. Android 4.0 (एपीआई लेवल 14) में इस सीमा को बढ़ाया गया था. हालांकि, इससे समस्या पूरी तरह से हल नहीं हुई.
Android 4.0 से पहले के वर्शन पर, DEX इंडेक्स की सीमा तक पहुंचने से पहले, लीनियरऐलोक की सीमा तक पहुंचा जा सकता है. इसलिए, अगर आपको 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 रनटाइम फ़ॉर्मैट पर निर्भर करती है. यह फ़ॉर्मैट सिर्फ़ Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन पर उपलब्ध है. अगर Android Studio का इस्तेमाल किया जा रहा है, तो Android 5.0 (एपीआई लेवल 21) या इसके बाद के वर्शन वाले डिवाइस पर ऐप्लिकेशन को डिप्लॉय करते समय, आईडीई अपने-आप प्री-डेक्सिंग का इस्तेमाल करता है.
हालांकि, अगर कमांड लाइन से 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); ... }