64 हज़ार से ज़्यादा तरीकों वाले ऐप्लिकेशन के लिए, मल्टीडेक्स की सुविधा चालू करें

अगर आपके ऐप्लिकेशन में 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) फ़ाइलों के तौर पर, एक्ज़ीक्यूटेबल बाइटकोड फ़ाइलें होती हैं. इनमें आपके ऐप्लिकेशन को चलाने के लिए इस्तेमाल किया गया कोड होता है. एक DEX फ़ाइल में, 65,536 तरीकों का रेफ़रंस दिया जा सकता है. इसमें Android फ़्रेमवर्क के तरीके, लाइब्रेरी के तरीके, और आपके कोड में मौजूद तरीके शामिल हैं.

इस कंप्यूटर साइंस के संदर्भ में, किलो या K शब्द 1024 दिखाता है (या 2^10). 65,536 का साइज़ 64x1024 के बराबर होता है. इसलिए, इस सीमा को _64K में रेफ़रंस की सीमा तय की गई_.

Android 5.0 से पहले के वर्शन के लिए मल्टीडेक्स सहायता

Android 5.0 (एपीआई लेवल 21) से पहले के प्लैटफ़ॉर्म के वर्शन में, Delvik का इस्तेमाल किया जाता है किसी ऐप्लिकेशन कोड को चलाने के लिए रनटाइम. डिफ़ॉल्ट रूप से, Dalvik ऐप्लिकेशन को हर APK के लिए एक ही classes.dex बाइटकोड फ़ाइल तक सीमित करता है. इस सीमित से बचने के लिए, मॉड्यूल-लेवल build.gradle या build.gradle.kts फ़ाइल में मल्टीडेक्स लाइब्रेरी जोड़ें:

ग्रूवीKotlin
dependencies {
    def multidex_version = "2.0.1"
    implementation "androidx.multidex:multidex:$multidex_version"
}
dependencies {
    val multidex_version = "2.0.1"
    implementation("androidx.multidex:multidex:$multidex_version")
}

यह लाइब्रेरी आपके ऐप्लिकेशन की मुख्य DEX फ़ाइल का हिस्सा बन जाती है और फिर अतिरिक्त DEX फ़ाइलों और उनमें मौजूद कोड के ऐक्सेस को मैनेज करता है. इस लाइब्रेरी का मौजूदा वर्शन देखने के लिए, यह देखें मल्टीडेक्स वर्शन में उपलब्ध है.

ज़्यादा जानकारी के लिए, मल्टीडेक्स के लिए अपने ऐप्लिकेशन को कॉन्फ़िगर करने का तरीका बताने वाला सेक्शन देखें.

Android 5.0 और इसके बाद के वर्शन के लिए Multidex की सुविधा

Android 5.0 (एपीआई लेवल 21) और उसके बाद के वर्शन, ART नाम के रनटाइम का इस्तेमाल करते हैं मूल रूप से, APK फ़ाइलों से कई DEX फ़ाइलें लोड करने की सुविधा मिलती है. एआरटी ऐप्लिकेशन इंस्टॉल के समय पूर्व-संकलन करता है, classesN.dex फ़ाइलें और उन्हें एक में कंपाइल करना इसके लिए OAT फ़ाइल Android डिवाइस पर लागू होता है. इसलिए, अगर आपका minSdkVersion 21 या उसके बाद का है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू होता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं होती.

Android 5.0 के बारे में ज़्यादा जानकारी पाने के लिए रनटाइम, Android रनटाइम (ART) और Delvik को पढ़ें.

ध्यान दें: Android Studio का इस्तेमाल करके अपना ऐप्लिकेशन चलाते समय, बिल्ड को उन टारगेट डिवाइसों के हिसाब से ऑप्टिमाइज़ किया गया है जिन पर आपका ऐप्लिकेशन काम करता है. इसमें, टारगेट किए गए डिवाइसों के चलने के दौरान मल्टीडेक्स को चालू करना शामिल है Android 5.0 और इसके बाद के वर्शन. यह ऑप्टिमाइज़ेशन सिर्फ़ तब लागू होता है, जब Android Studio का इस्तेमाल करके ऐप्लिकेशन को डिप्लॉय किया जाता है. इसलिए, 64K की सीमा से बचने के लिए, आपको अब भी मल्टीडेक्स के लिए अपनी रिलीज़ बिल्ड को कॉन्फ़िगर करना पड़ सकता है.

64K की सीमा से बचना

64 हज़ार या उससे ज़्यादा मेथड रेफ़रंस का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन को कॉन्फ़िगर करने से पहले, अपने ऐप्लिकेशन कोड से कॉल किए गए रेफ़रंस की कुल संख्या कम करें. इनमें, आपके ऐप्लिकेशन कोड या शामिल की गई लाइब्रेरी से तय किए गए मेथड भी शामिल हैं.

इन रणनीतियों की मदद से, डीईएक्स रेफ़रंस की सीमा से बचने में मदद मिल सकती है:

अपने ऐप्लिकेशन की डायरेक्ट और ट्रांज़िटिव डिपेंडेंसी की समीक्षा करना
ध्यान रखें कि आपने अपने ऐप्लिकेशन में बड़ी लाइब्रेरी डिपेंडेंसी शामिल की है या नहीं, इसकी वैल्यू कोड की तय सीमा से ज़्यादा है को ऐप में जोड़ा जा रहा है. एक बहुत बड़ी लाइब्रेरी को शामिल करना, एक आम लेकिन समस्या वाला पैटर्न है क्योंकि कुछ उपयोगी तरीके उपयोगी थे. अपने ऐप्लिकेशन कोड की डिपेंडेंसी कम करने से, अक्सर आपको DEX रेफ़रंस की सीमा से बचने में मदद मिल सकती है.
R8 की मदद से इस्तेमाल नहीं किए गए कोड को हटाएं
अपने रिलीज़ बिल्ड के लिए R8 को चलाने के लिए,
कोड छोटा करने की सुविधा चालू करें. श्रिंकिंग की सुविधा चालू करें, ताकि यह पक्का किया जा सके कि आपके APK के साथ इस्तेमाल न किया गया कोड नहीं भेज रहे हैं. अगर कोड का आकार छोटा करने की सुविधा को सही तरीके से कॉन्फ़िगर किया गया है, तो आपकी डिपेंडेंसी से इस्तेमाल नहीं होने वाले कोड और रिसॉर्स को भी हटा सकता है.

इन तकनीकों का इस्तेमाल करके आप अपने APK का कुल साइज़ कम कर सकते हैं और अपने ऐप्लिकेशन में मल्टीडेक्स की ज़रूरत से बचें.

अपने ऐप्लिकेशन को मल्टीडेक्स के लिए कॉन्फ़िगर करना

ध्यान दें: अगर आपका minSdkVersion 21 या उससे बाद पर सेट है, तो मल्टीडेक्स डिफ़ॉल्ट रूप से चालू रहता है और आपको मल्टीडेक्स लाइब्रेरी की ज़रूरत नहीं है.

अगर आपके minSdkVersion को 20 या इससे कम पर सेट किया गया है, तो को multidex लाइब्रेरी और आपके ऐप्लिकेशन प्रोजेक्ट में ये बदलाव किए गए हैं:

  1. मॉड्यूल-लेवल build.gradle फ़ाइल को इसमें बदलें मल्टीडेक्स को चालू करें और मल्टीडेक्स लाइब्रेरी को डिपेंडेंसी के तौर पर जोड़ें, जैसा कि यहां दिखाया गया है:

    ग्रूवीKotlin
    android {
        defaultConfig {
            ...
            minSdkVersion 15 
            targetSdkVersion 33
            multiDexEnabled true
        }
        ...
    }
    
    dependencies {
        implementation "androidx.multidex:multidex:2.0.1"
    }
    android {
        defaultConfig {
            ...
            minSdk = 15 
            targetSdk = 33
            multiDexEnabled = true
        }
        ...
    }
    
    dependencies {
        implementation("androidx.multidex:multidex:2.0.1")
    }
  2. यह इस बात पर निर्भर करता है कि आप 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 तक बढ़ाने के लिए नीचे बताए गए तरीके से बदलें:

      KotlinJava
      class MyApplication : MultiDexApplication() {...}
      public class MyApplication extends MultiDexApplication { ... }
    • अगर Application क्लास को बदलने के बाद भी, बेस क्लास में बदलाव नहीं किया जा सकता, तो इसके बजाय attachBaseContext() तरीके को बदलें और मल्टीडेक्स को चालू करने के लिए MultiDex.install(this) को कॉल करें:

      KotlinJava
      class MyApplication : SomeOtherApplication() {
      
          override fun attachBaseContext(base: Context) {
              super.attachBaseContext(base)
              MultiDex.install(this)
          }
      }
      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(base);
           MultiDex.install(this);
        }
      }

      चेतावनी: MultiDex.install() पूरा होने से पहले, रिफ़्लेक्शन या JNI के ज़रिए MultiDex.install() या कोई दूसरा कोड न चलाएं. मल्टीडेक्स ट्रेसिंग की मदद से उन कॉल को फ़ॉलो नहीं कर रहे जिनकी वजह से ClassNotFoundException गड़बड़ी हो रही है या गड़बड़ी की पुष्टि की जा रही है DEX फ़ाइलों के बीच खराब क्लास विभाजन की वजह से.

अब जब आपका ऐप्लिकेशन बनता है, तो 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 गड़बड़ी के साथ क्रैश हो जाएगा.

बिल्ड टूल, ऐसे कोड के कोड पाथ की पहचान करता है जिसे सीधे आपके ऐप्लिकेशन से ऐक्सेस किया जाता है कोड. हालांकि, यह समस्या हो सकती है ये तब होते हैं, जब कोड पाथ कम दिखते हैं, जैसे कि जब इस्तेमाल की जाने वाली लाइब्रेरी में जटिल डिपेंडेंसी. उदाहरण के लिए, अगर कोड में आत्मनिरीक्षण या शुरू करने के लिए कहा गया है का इस्तेमाल किया है, तो हो सकता है कि उन क्लास की पहचान प्राथमिक DEX फ़ाइल में आवश्यक है.

अगर आपको java.lang.NoClassDefFoundError मिलता है, तो आपको प्राइमरी DEX फ़ाइल में ज़रूरी अतिरिक्त क्लास मैन्युअल तरीके से बतानी होंगी. इसके लिए, आपको अपने बिल्ड टाइप में multiDexKeepProguard प्रॉपर्टी का इस्तेमाल करके, उन्हें बताना होगा. अगर किसी क्लास का मिलान multiDexKeepProguard फ़ाइल, फिर वह क्लास को मुख्य DEX फ़ाइल में जोड़ा गया है.

multiDexKeepProguard प्रॉपर्टी

multiDexKeepProguard फ़ाइल, ProGuard के जैसे ही फ़ॉर्मैट का इस्तेमाल करती है और ProGuard के पूरे ग्रामर के साथ काम करती है. अपने ऐप्लिकेशन में क्या रखना है, इसे पसंद के मुताबिक बनाने के तरीके के बारे में ज़्यादा जानने के लिए, पसंद के मुताबिक यह तय करना कि कौनसा कोड रखना है लेख पढ़ें.

multiDexKeepProguard में तय की गई फ़ाइल में -keep होना चाहिए किसी भी मान्य ProGuard सिंटैक्स में मौजूद विकल्पों का इस्तेमाल करें. उदाहरण के लिए, -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

इसके बाद, बिल्ड टाइप के लिए उस फ़ाइल का एलान इस तरह किया जा सकता है:

ग्रूवीKotlin
android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
            ...
        }
    }
}
android {
    buildTypes {
        getByName("release") {
            multiDexKeepProguard = file("multidex-config.pro")
            ...
        }
    }
}

डेवलपमेंट बिल्ड में मल्टीडेक्स को ऑप्टिमाइज़ करना

मल्टीडेक्स कॉन्फ़िगरेशन के लिए, बिल्ड प्रोसेसिंग में काफ़ी ज़्यादा समय लगता है. इसकी वजह यह है कि बिल्ड सिस्टम को यह तय करना होता है कि मुख्य DEX फ़ाइल में कौनसी क्लास शामिल की जानी चाहिए और कौनसी क्लास सेकंडरी DEX फ़ाइलों में शामिल की जा सकती हैं. इसका मतलब है कि मल्टीडेक्स का इस्तेमाल करके इंक्रीमेंटल बिल्ड बनाने में आम तौर पर ज़्यादा समय लगता है. साथ ही, इससे ऐप्लिकेशन डेवलप करने की प्रोसेस भी धीमी हो सकती है.

इंक्रीमेंटल बिल्ड के लंबे समय को कम करने के लिए, पहले से डेक्स करने की सुविधा का इस्तेमाल करें. इससे, बिल्ड के बीच में मल्टीडेक्स आउटपुट का फिर से इस्तेमाल किया जा सकता है. प्री-डेक्सिंग, सिर्फ़ Android 5.0 पर उपलब्ध एआरटी फ़ॉर्मैट के हिसाब से होती है (एपीआई लेवल 21) और उसके बाद वाले वर्शन. Android Studio का इस्तेमाल करने पर, आईडीई अपने-आप प्री-डेक्सिंग का इस्तेमाल करता है Android 5.0 (एपीआई लेवल 21) या उसके बाद के वर्शन वाले डिवाइस पर अपना ऐप्लिकेशन डिप्लॉय करते समय. हालांकि, अगर कमांड लाइन से Gradle बिल्ड चलाए जा रहे हैं, तो आपको पहले से डीकंपाइल करने की सुविधा चालू करने के लिए, minSdkVersion को 21 या उसके बाद के वर्शन पर सेट करना होगा.

अपनी सेटिंग को बनाए रखने के लिए प्रोडक्शन बिल्ड के साथ, अपने ऐप्लिकेशन के दो वर्शन बनाए जा सकते हैं उत्पादों के फ़्लेवर का इस्तेमाल करना—एक वर्शन जिसमें डेवलपमेंट फ़्लेवर और रिलीज़ फ़्लेवर वाला एक वर्शन शामिल है. minSdkVersion के लिए अलग-अलग मान, जैसा कि दिखाया गया है:

ग्रूवीKotlin
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"
}
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() तरीके को, नीचे दिए गए कोड से बदलना होगा:

KotlinJava
fun onCreate(arguments: Bundle) {
  MultiDex.install(targetContext)
  super.onCreate(arguments)
  ...
}
public void onCreate(Bundle arguments) {
  MultiDex.install(getTargetContext());
  super.onCreate(arguments);
  ...
}