अगर आपके ऐप्लिकेशन में 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
गड़बड़ी की इन स्थितियों में एक सामान्य संख्या दिखती है: 65,536. यह संख्या, उन रेफ़रंस की कुल संख्या दिखाती है जिन्हें किसी एक Dalvik Executable (DEX) बाइटकोड फ़ाइल में कोड से शुरू किया जा सकता है. इस पेज पर, मल्टीडेक्स नाम के ऐप्लिकेशन कॉन्फ़िगरेशन को चालू करके, इस सीमा को पार करने का तरीका बताया गया है. इससे आपके ऐप्लिकेशन को एक से ज़्यादा DEX फ़ाइलें बनाने और पढ़ने की अनुमति मिलती है.
64 केबी के रेफ़रंस की सीमा के बारे में जानकारी
Android ऐप्लिकेशन (APK) फ़ाइलों में, Dalvik एक्ज़ीक्यूटेबल (DEX) फ़ाइलों के तौर पर, एक्ज़ीक्यूटेबल बाइटकोड फ़ाइलें होती हैं. इनमें आपके ऐप्लिकेशन को चलाने के लिए इस्तेमाल किया गया कोड होता है. एक DEX फ़ाइल में, 65,536 तरीकों का रेफ़रंस दिया जा सकता है. इसमें Android फ़्रेमवर्क के तरीके, लाइब्रेरी के तरीके, और आपके कोड में मौजूद तरीके शामिल हैं.
कंप्यूटर साइंस के संदर्भ में, किलो या K का मतलब 1024 (या 2^10) होता है. 65,536, 64x1024 के बराबर है. इसलिए, इस सीमा को _64 केबी रेफ़रंस सीमा_ कहा जाता है.Android 5.0 से पहले के वर्शन के लिए, Multidex की सुविधा
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) और उसके बाद के वर्शन, ART नाम के रनटाइम का इस्तेमाल करते हैं. यह रनटाइम,
APK फ़ाइलों से कई DEX फ़ाइलों को लोड करने की सुविधा देता है. ART, ऐप्लिकेशन इंस्टॉल होने के समय पहले से कंपाइल करने की सुविधा देता है. यह classesN.dex
फ़ाइलों को स्कैन करता है और उन्हें एक OAT फ़ाइल में कंपाइल करता है, ताकि Android डिवाइस पर उन्हें चलाया जा सके. इसलिए, अगर आपका minSdkVersion
21 या उसके बाद का है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
Android 5.0 के रनटाइम के बारे में ज़्यादा जानकारी के लिए, Android Runtime (ART) और Dalvik लेख पढ़ें.
ध्यान दें: Android Studio का इस्तेमाल करके ऐप्लिकेशन चलाते समय, बाइन्ड किए गए उन टारगेट डिवाइसों के लिए बाइन्ड किया जाता है जिन पर ऐप्लिकेशन डिप्लॉय किया जाता है. इसमें, टारगेट डिवाइसों पर Android 5.0 और उसके बाद के वर्शन का इस्तेमाल करने पर, मल्टीडेक्स को चालू करना भी शामिल है. यह ऑप्टिमाइज़ेशन सिर्फ़ तब लागू होता है, जब Android Studio का इस्तेमाल करके ऐप्लिकेशन को डिप्लॉय किया जाता है. इसलिए, 64K की सीमा से बचने के लिए, आपको अब भी मल्टीडेक्स के लिए अपनी रिलीज़ बिल्ड को कॉन्फ़िगर करना पड़ सकता है.
64K की सीमा से बचना
64 हज़ार या उससे ज़्यादा मेथड रेफ़रंस का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन को कॉन्फ़िगर करने से पहले, अपने ऐप्लिकेशन कोड से कॉल किए गए रेफ़रंस की कुल संख्या कम करें. इनमें, आपके ऐप्लिकेशन कोड या शामिल की गई लाइब्रेरी से तय किए गए मेथड भी शामिल हैं.
इन रणनीतियों की मदद से, डीईएक्स रेफ़रंस की सीमा से बचने में मदद मिल सकती है:
- अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
- देखें कि आपने अपने ऐप्लिकेशन में जो बड़ी लाइब्रेरी डिपेंडेंसी शामिल की है उसकी वैल्यू, ऐप्लिकेशन में जोड़े जा रहे कोड की वैल्यू से ज़्यादा है या नहीं. बहुत बड़ी लाइब्रेरी को शामिल करना एक आम लेकिन समस्या पैदा करने वाला पैटर्न है. ऐसा इसलिए किया जाता है, क्योंकि कुछ काम के तरीके काम के होते हैं. अपने ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, अक्सर आपको DEX रेफ़रंस की सीमा से बचने में मदद मिल सकती है.
- R8 की मदद से, इस्तेमाल न होने वाले कोड हटाना अपने रिलीज़ बिल्ड के लिए R8 को चलाने के लिए,
- कोड छोटा करने की सुविधा चालू करें. कोड छोटा करने की सुविधा चालू करें, ताकि यह पक्का किया जा सके कि आपके APKs में, इस्तेमाल न होने वाला कोड शिप न किया जा रहा हो. अगर कोड छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो यह आपकी डिपेंडेंसी से, इस्तेमाल न होने वाले कोड और संसाधनों को भी हटा सकती है.
इन तकनीकों का इस्तेमाल करके, अपने APK का कुल साइज़ कम किया जा सकता है. साथ ही, अपने ऐप्लिकेशन में मल्टीडेक्स की ज़रूरत से बचा जा सकता है.
अपने ऐप्लिकेशन को मल्टीडेक्स के लिए कॉन्फ़िगर करना
ध्यान दें: अगर आपकाminSdkVersion
21 या उससे ज़्यादा पर सेट है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.
अगर आपका minSdkVersion
20 या उससे कम पर सेट है, तो आपको मल्टीडेक्स लाइब्रेरी का इस्तेमाल करना होगा. साथ ही, अपने ऐप्लिकेशन प्रोजेक्ट में ये बदलाव करने होंगे:
-
मॉड्यूल-लेवल की
build.gradle
फ़ाइल में बदलाव करके, मल्टीडेक्स को चालू करें और मल्टीडेक्स लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ें, जैसा कि यहां दिखाया गया है:Groovy
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 33 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 33 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 के बिल्ड टूल ज़रूरत के हिसाब से एक मुख्य DEX फ़ाइल (classes.dex
) और सहायक DEX फ़ाइलें (classes2.dex
, classes3.dex
वगैरह) बनाते हैं.
इसके बाद, बिल्ड सिस्टम सभी DEX फ़ाइलों को आपके APK में पैकेज कर देता है.
रनटाइम के दौरान, मल्टीडेक्स एपीआई सिर्फ़ मुख्य classes.dex
फ़ाइल में खोजने के बजाय, आपके तरीकों के लिए उपलब्ध सभी DEX फ़ाइलों को खोजने के लिए, एक खास क्लास लोडर का इस्तेमाल करते हैं.
मल्टीडेक्स लाइब्रेरी की सीमाएं
मल्टीडेक्स लाइब्रेरी की कुछ सीमाएं हैं. लाइब्रेरी को अपने ऐप्लिकेशन के बिल्ड कॉन्फ़िगरेशन में शामिल करते समय, इन बातों का ध्यान रखें:
- डिवाइस के डेटा पार्टीशन पर स्टार्टअप के दौरान DEX फ़ाइलों को इंस्टॉल करना मुश्किल होता है. साथ ही, अगर सेकंडरी DEX फ़ाइलें बड़ी हैं, तो 'ऐप्लिकेशन में कोई समस्या है' (ANR) वाली गड़बड़ियां हो सकती हैं. इस समस्या से बचने के लिए, कोड छोटा करने की सुविधा चालू करें, ताकि DEX फ़ाइलों का साइज़ कम किया जा सके और इस्तेमाल न किए गए कोड के हिस्सों को हटाया जा सके.
- Android 5.0 (एपीआई लेवल 21) से पहले के वर्शन पर काम करते समय, लाइनरऐलोक की सीमा (समस्या 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 फ़ाइलों में शामिल की जा सकती हैं. इसका मतलब है कि मल्टीडेक्स का इस्तेमाल करके इंक्रीमेंटल बिल्ड बनाने में आम तौर पर ज़्यादा समय लगता है. साथ ही, इससे डेवलपमेंट की प्रोसेस भी धीमी हो सकती है.
इंक्रीमेंटल बिल्ड के लंबे समय को कम करने के लिए, पहले से डेक्स करने की सुविधा का इस्तेमाल करें. इससे, बिल्ड के बीच में मल्टीडेक्स आउटपुट का फिर से इस्तेमाल किया जा सकता है.
ऐप्लिकेशन को पहले से डीक्स करने की सुविधा, ART फ़ॉर्मैट पर निर्भर करती है. यह फ़ॉर्मैट सिर्फ़ 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); ... }