अगर आपके ऐप्लिकेशन का 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 एक्ज़िक्यूटेबल (DEX) फ़ाइल के तौर पर एक्ज़ीक्यूटेबल बाइटकोड फ़ाइलें होती हैं. इन फ़ाइलों में, आपके ऐप्लिकेशन को चलाने के लिए इस्तेमाल किए जाने वाले कंपाइल किए गए कोड होते हैं. Delvik एक DEX फ़ाइल के तहत, एक ही DEX फ़ाइल के लिए इस्तेमाल किए जा सकने वाले तरीकों की कुल संख्या को 65,536 तक सीमित करता है. इसमें Android फ़्रेमवर्क के तरीके, लाइब्रेरी के तरीके, और मेथड भी शामिल हैं.
कंप्यूटर साइंस के संदर्भ में, किलो या K का मतलब 1024 (या 2^10) होता है. 65,536, 64x1024 के बराबर है, इस सीमा को _64K पहचान सीमा_ कहा जाता है.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
फ़ाइलों को स्कैन करता है और Android डिवाइस से एक्ज़ीक्यूशन करने के लिए, उन्हें एक OAT फ़ाइल में कंपाइल करता है. इसलिए, अगर आपके minSdkVersion
में
21 या उससे ज़्यादा है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू रहता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं है.
Android 5.0 के रनटाइम के बारे में ज़्यादा जानकारी के लिए, Android Runtime (ART) और Dalvik लेख पढ़ें.
ध्यान दें: Android Studio का इस्तेमाल करके ऐप्लिकेशन चलाते समय, बाइन्ड किए गए उन टारगेट डिवाइसों के लिए बाइन्ड किया जाता है जिन पर ऐप्लिकेशन डिप्लॉय किया जाता है. इसमें, टारगेट किए गए डिवाइस में Android 5.0 और उसके बाद का वर्शन होने पर मल्टीडेक्स को चालू करना भी शामिल है. यह ऑप्टिमाइज़ेशन सिर्फ़ तब लागू होता है, जब Android Studio का इस्तेमाल करके ऐप्लिकेशन को डिप्लॉय किया जाता है. इसलिए, 64K की सीमा से बचने के लिए, आपको अब भी मल्टीडेक्स के लिए अपनी रिलीज़ बिल्ड को कॉन्फ़िगर करना पड़ सकता है.
64K की सीमा से बचना
64 हज़ार या उससे ज़्यादा मेथड रेफ़रंस का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन को कॉन्फ़िगर करने से पहले, अपने ऐप्लिकेशन कोड से कॉल किए गए रेफ़रंस की कुल संख्या कम करें. इनमें, आपके ऐप्लिकेशन कोड या शामिल की गई लाइब्रेरी से तय किए गए मेथड भी शामिल हैं.
इन रणनीतियों की मदद से, DEX रेफ़रंस की सीमा से बचने में मदद मिल सकती है:
- अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
- देखें कि आपने अपने ऐप्लिकेशन में जो बड़ी लाइब्रेरी डिपेंडेंसी शामिल की है उसकी वैल्यू, ऐप्लिकेशन में जोड़े जा रहे कोड की वैल्यू से ज़्यादा है या नहीं. बहुत बड़ी लाइब्रेरी को शामिल करना एक आम लेकिन समस्या पैदा करने वाला पैटर्न है. ऐसा इसलिए किया जाता है, क्योंकि कुछ काम के तरीके काम के होते हैं. अपने ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, अक्सर आपको 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()
पूरा होने से पहले, रिफ़्लेक्शन या जेएनआई की मदद से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); ... }