64K'dan fazla yöntemler içeren uygulamalar için multidex'i etkinleştirin

Uygulamanızda API 20 veya daha düşük bir minSdk sürümü varsa ve uygulamanızda referans verilen kitaplıklar 65.536 yöntemi aşıyorsa uygulamanızın Android derleme mimarisi sınırına ulaştığını belirten aşağıdaki derleme hatasıyla karşılaşırsınız:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

Derleme sisteminin eski sürümleri, aynı sorunun göstergesi olan farklı bir hata bildirir:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

Bu hata koşullarında ortak bir sayı gösterilir: 65536. Bu sayı, tek bir Dalvik Yürütülebilir (DEX) bayt kodu dosyasındaki kod tarafından çağrılabilecek toplam referans sayısını temsil eder. Bu sayfada, uygulamanızın birden fazla DEX dosyası oluşturup okumasına olanak tanıyan multidex adlı bir uygulama yapılandırmasını etkinleştirerek bu sınırlamayı nasıl aşabileceğiniz açıklanmaktadır.

64K referans sınırı hakkında

Android uygulama (APK) dosyaları, Dalvik Yürütülebilir (DEX) dosyaları biçiminde yürütülebilir bayt kodu dosyaları içerir. Bu dosyalar,uygulamanızı çalıştırmak için kullanılan derlenmiş kodu içerir. Dalvik Yürütülebilir teknik spesifikasyonu, tek bir DEX dosyasında referans verilebilecek toplam yöntem sayısını 65.536 ile (Android çerçeve yöntemleri, kitaplık yöntemleri ve yöntemler dahil) sınırlandırır.

Bilgisayar bilimi bağlamında kilo veya K terimi 1024 (veya 2^10) anlamına gelir. 65.536, 64x1.024'e eşit olduğundan bu sınıra _64K referans sınırı_ denir.

Android 5.0'dan önceki Multidex desteği

Platformun Android 5.0'dan (API düzeyi 21) önceki sürümleri, uygulama kodunu çalıştırmak için Dalvik çalışma zamanını kullanır. Dalvik, uygulamaları varsayılan olarak APK başına tek bir classes.dex bayt kod dosyasıyla sınırlandırır. Bu sınırlamayı aşmak için çokludex kitaplığını modül düzeyindeki build.gradle veya build.gradle.kts dosyasına ekleyin:

Eski

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")
}

Bu kitaplık, uygulamanızın birincil DEX dosyasının bir parçası haline gelir ve ardından ek DEX dosyalarına ve bunların içerdiği koda erişimi yönetir. Bu kitaplığın mevcut sürümlerini görüntülemek için multidex sürümlerini inceleyin.

Daha fazla ayrıntı için uygulamanızı multidex için yapılandırma ile ilgili bölüme bakın.

Android 5.0 ve sonraki sürümler için Multidex desteği

Android 5.0 (API düzeyi 21) ve sonraki sürümlerde, APK dosyalarından birden fazla DEX dosyasının yüklenmesini yerel olarak destekleyen ART adlı bir çalışma zamanı kullanılır. ART, uygulama yükleme anında önceden derleme gerçekleştirir, classesN.dex dosyasını tarar ve Android cihaz tarafından yürütülmesi için tek bir OAT dosyasında derler. Bu nedenle, minSdkVersion değeriniz 21 veya daha yüksekse multidex varsayılan olarak etkinleştirilir ve multidex kitaplığına ihtiyacınız yoktur.

Android 5.0 çalışma zamanı hakkında daha fazla bilgi edinmek için Android Runtime (ART) ve Dalvik sayfalarını okuyun.

Not: Uygulamanızı Android Studio kullanarak çalıştırırken derleme, dağıtım yaptığınız hedef cihazlara göre optimize edilir. Buna, hedef cihazlarda Android 5.0 ve sonraki sürümler çalışırken multidex'in etkinleştirilmesi de dahildir. Bu optimizasyon yalnızca Android Studio kullanarak uygulamanızı dağıtırken uygulandığından, 64K sınırından kaçınmak için yine de multidex için sürüm derlemenizi yapılandırmanız gerekebilir.

64K sınırından kaçının.

Uygulamanızı 64K veya daha fazla yöntem referansının kullanılmasını sağlayacak şekilde yapılandırmadan önce, uygulama kodunuz tarafından çağrılan toplam referans sayısını azaltmak için gerekli adımları atın (uygulama kodunuz veya dahil ettiğiniz kitaplıklar tarafından tanımlanan yöntemler dahil).

Aşağıdaki stratejiler, DEX referans sınırına ulaşmaktan kaçınmanıza yardımcı olabilir:

Uygulamanızın doğrudan ve geçişli bağımlılıklarını inceleme
Uygulamanıza eklediğiniz büyük bir kitaplık bağımlılığının değerinin, uygulamaya eklenen kod miktarından ağır bastığını düşünün. Yaygın ancak sorunlu bir model, birkaç yardımcı yöntemi faydalı olduğu için çok büyük bir kitaplık eklemektir. Uygulama kodu bağımlılıklarını azaltmak genellikle DEX referans sınırından kaçınmanıza yardımcı olabilir.
Kullanılmayan kodları R8 ile kaldırın
Sürüm derlemelerinizde R8'i çalıştırmak için kod küçültmeyi etkinleştirin. Kullanılmayan kodları APK'larınızla birlikte göndermediğinizden emin olmak için küçültmeyi etkinleştirin. Kod daraltma doğru şekilde yapılandırılırsa kullanılmayan kod ve kaynakları da bağımlılıklarınızdan kaldırabilir.

Bu teknikleri kullanarak APK'nızın genel boyutunu küçültebilir ve uygulamanızda multimedya kullanımı ihtiyacını ortadan kaldırabilirsiniz.

Uygulamanızı multidex için yapılandırma

Not: minSdkVersion değeriniz 21 veya daha yüksek bir değere ayarlanırsa multidex varsayılan olarak etkinleştirilir ve multidex kitaplığına ihtiyacınız olmaz.

minSdkVersion değeriniz 20 veya daha düşük bir değere ayarlanmışsa multidex kitaplığını kullanmanız ve uygulama projenizde aşağıdaki değişiklikleri yapmanız gerekir:

  1. Multidex'i etkinleştirmek için modül düzeyindeki build.gradle dosyasını değiştirin ve çokludex kitaplığını aşağıda gösterildiği gibi bağımlılık olarak ekleyin:

    Eski

    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")
    }
    
  2. Application sınıfını geçersiz kılıp kılmadığınıza bağlı olarak aşağıdakilerden birini yapın:
    • Application sınıfını geçersiz kılmazsanız manifest dosyanızı, <application> etiketinde android:name değerini ayarlayacak şekilde aşağıdaki gibi düzenleyin:

      <?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 sınıfını geçersiz kılarsanız aşağıdaki şekilde MultiDexApplication öğesini genişletmek için değiştirin:

      Kotlin

      class MyApplication : MultiDexApplication() {...}
      

      Java

      public class MyApplication extends MultiDexApplication { ... }
      
    • Application sınıfını geçersiz kılarsanız ancak temel sınıfı değiştirmek mümkün değilse bunun yerine, attachBaseContext() yöntemini geçersiz kılın ve multidex'i etkinleştirmek için MultiDex.install(this) yöntemini çağırın:

      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);
        }
      }
      

      Dikkat: MultiDex.install() tamamlanmadan önce yansıtma veya JNI aracılığıyla MultiDex.install() veya başka bir kodu yürütmeyin. Multidex izleme, bu çağrıları takip etmez. Bu nedenle, ClassNotFoundException veya DEX dosyaları arasındaki hatalı sınıf bölümü nedeniyle hataları doğrular.

Şimdi uygulamanızı oluştururken Android derleme araçları, birincil DEX dosyası (classes.dex) ve destekleyici DEX dosyaları (classes2.dex, classes3.dex vb.) oluşturur. Ardından, derleme sistemi tüm DEX dosyalarını APK'nıza paketler.

Çalışma zamanında, multidex API'ler yalnızca ana classes.dex dosyasında arama yapmak yerine, kullanılabilir tüm DEX dosyalarında yöntemleriniz için arama yapmak üzere özel bir sınıf yükleyici kullanır.

Multidex kitaplığıyla ilgili sınırlamalar

Multidex kitaplığının bilinen bazı sınırlamaları vardır. Kitaplığı, uygulama derleme yapılandırmanıza eklerken aşağıdakileri göz önünde bulundurun:

  • Başlatma sırasında DEX dosyalarının bir cihazın veri bölümüne yüklenmesi karmaşık bir işlemdir ve ikincil DEX dosyaları büyükse Uygulama Yanıt Vermiyor (ANR) hatalarına neden olabilir. Bu sorunu önlemek için DEX dosyalarının boyutunu en aza indirmek ve kullanılmayan kod bölümlerini kaldırmak için kod küçültmeyi etkinleştirin.
  • Android 5.0'dan (API düzeyi 21) önceki sürümlerde çalışırken multidex kullanmak, doğrusal sınırı aşmak için yeterli değildir (sorun 37008143). Bu sınır, Android 4.0'da (API düzeyi 14) artırılsa da sorunu tamamen çözmedi.

    Android 4.0'dan düşük sürümlerde, DEX dizin sınırına ulaşmadan önce doğrusal sınırlama sınırına ulaşabilirsiniz. Bu nedenle, 14'ün altındaki API düzeylerini hedefliyorsanız uygulamanızın başlatma sırasında veya belirli sınıf grupları yüklenirken sorun yaşayabileceğinden platformun bu sürümlerinde kapsamlı bir şekilde test yapın.

    Kod küçültme, bu sorunları azaltabilir veya muhtemelen ortadan kaldırabilir.

Birincil DEX dosyasında gerekli sınıfları bildirme

Derleme araçları, bir multidex uygulaması için her DEX dosyasını oluştururken, uygulamanızın başarılı bir şekilde başlatılabilmesi amacıyla birincil DEX dosyasında hangi sınıfların gerekli olduğunu belirlemek için karmaşık karar verme süreçleri yürütür. Başlatma sırasında gereken herhangi bir sınıf birincil DEX dosyasında sağlanmamışsa uygulamanız java.lang.NoClassDefFoundError hatasıyla kilitlenir.

Derleme araçları, doğrudan uygulama kodunuzdan erişilen kodun kod yollarını tanır. Ancak bu sorun, kullandığınız bir kitaplıkta karmaşık bağımlılıklar bulunması gibi kod yollarının daha az görünür olduğu durumlarda ortaya çıkabilir. Örneğin, kod yerel koddan Java yöntemlerinin iç gözlemini veya çağrılmasını kullanıyorsa bu sınıflar birincil DEX dosyasında gerekli olarak tanınmayabilir.

java.lang.NoClassDefFoundError alırsanız birincil DEX dosyasında gerekli olan ek sınıfları, derleme türünüzde multiDexKeepProguard mülküyle tanımlayarak manuel olarak belirtmeniz gerekir. multiDexKeepProguard dosyasında eşleşen sınıflar birincil DEX dosyasına eklenir.

multiDexKeepProGuard mülkü

multiDexKeepProguard dosyası, ProGuard ile aynı biçimi kullanır ve ProGuard dil bilgisinin tamamını destekler. Uygulamanızda nelerin depolanacağını özelleştirme hakkında daha fazla bilgi için Hangi kodun saklanacağını özelleştirme bölümüne bakın.

multiDexKeepProguard öğesinde belirttiğiniz dosya, geçerli herhangi bir ProGuard söz diziminde -keep seçenekleri içermelidir. Örneğin, -keep com.example.MyClass.class. multidex-config.pro adında, şu şekilde görünen bir dosya oluşturabilirsiniz:

-keep class com.example.MyClass
-keep class com.example.MyClassToo

Bir paketteki tüm sınıfları belirtmek istiyorsanız dosya aşağıdaki gibi görünür:

-keep class com.example.** { *; } // All classes in the com.example package

Ardından bu dosyayı aşağıdaki şekilde bir derleme türü için tanımlayabilirsiniz:

Eski

android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
            ...
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            multiDexKeepProguard = file("multidex-config.pro")
            ...
        }
    }
}

Geliştirme derlemelerinde multidex'i optimize edin

Derleme sisteminin birincil DEX dosyasına hangi sınıfların dahil edilmesi gerektiği ve ikincil DEX dosyalarına hangi sınıfların dahil edilebileceği konusunda karmaşık kararlar alması gerektiğinden, multidex yapılandırmasında derleme işleme süresi önemli ölçüde artar. Yani, multidex kullanan artımlı derlemeler genelde daha uzun sürer ve geliştirme sürecinizi yavaşlatabilir.

Artımlı derleme sürelerini azaltmak için önceden ekleme özelliğini kullanarak derlemeler arasında multidex çıktılarını yeniden kullanın. Önceden ekleme işlemi, yalnızca Android 5.0 (API düzeyi 21) ve sonraki sürümlerde kullanılabilen bir ART biçimine dayanır. Android Studio kullanıyorsanız IDE, uygulamanızı Android 5.0 (API düzeyi 21) veya sonraki sürümleri çalıştıran bir cihaza dağıtırken otomatik olarak önceden ekleme özelliğini kullanır. Ancak Gradle derlemelerini komut satırından çalıştırıyorsanız önceden sıralamayı etkinleştirmek için minSdkVersion değerini 21 veya daha yüksek bir değere ayarlamanız gerekir.

Üretim derlemenizin ayarlarını korumak amacıyla ürün aromalarını kullanarak uygulamanızın iki sürümünü (biri geliştirme aromasına, diğeri sürüm aromasına sahip) aşağıda gösterildiği gibi minSdkVersion için farklı değerlerle oluşturabilirsiniz:

Eski

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")
}

Derleme hızlarını Android Studio'dan veya komut satırından iyileştirmeye yardımcı olacak daha fazla strateji öğrenmek için Derleme hızınızı optimize etme bölümünü okuyun. Derleme varyantlarını kullanma hakkında daha fazla bilgi için Derleme varyantlarını yapılandırma bölümüne bakın.

İpucu: Farklı multidex ihtiyaçları için farklı derleme varyantlarınız varsa her varyant için farklı bir manifest dosyası sağlayabilirsiniz. Böylece, yalnızca API düzeyi 20 ve önceki sürümlerdeki dosya, <application> etiket adını değiştirir. Ayrıca her varyant için farklı bir Application alt sınıfı oluşturabilirsiniz. Böylece yalnızca API düzeyi 20 ve altındaki alt sınıf, MultiDexApplication sınıfını veya MultiDex.install(this) çağrılarını genişletir.

Multidex uygulamalarını test etme

Multidex uygulamaları için araç testleri yazdığınızda, MonitoringInstrumentation veya AndroidJUnitRunner enstrümantasyonu kullanıyorsanız ek yapılandırma gerekmez. Başka bir Instrumentation kullanıyorsanız bu seçeneğin onCreate() yöntemini aşağıdaki kodla geçersiz kılmanız gerekir:

Kotlin

fun onCreate(arguments: Bundle) {
  MultiDex.install(targetContext)
  super.onCreate(arguments)
  ...
}

Java

public void onCreate(Bundle arguments) {
  MultiDex.install(getTargetContext());
  super.onCreate(arguments);
  ...
}