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

Uygulamanızın API düzeyi 20 veya daha düşükse ve uygulamanız ile referans verdiği kitaplıklar 65.536 yöntemi aşıyorsa uygulamanızın Android derleme mimarisinin sınırına ulaştığını belirten şu derleme hatasıyla karşılaşırsınız:minSdk

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ı soruna işaret eden farklı bir hata bildirir:

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

Bu hata koşulları ortak bir sayı gösterir: 65536. Bu sayı, tek bir Dalvik Executable (DEX) bayt kodu dosyası içindeki kod tarafından çağrılabilen toplam referans sayısını gösterir. Bu sayfada, uygulamanızın birden fazla DEX dosyası oluşturmasına ve okumasına olanak tanıyan multidex adlı bir uygulama yapılandırmasını etkinleştirerek bu sınırlamayı nasıl aşacağınız açıklanmaktadır.

64.000 referans sınırı hakkında

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

Bilgisayar bilimleri bağlamında kilo veya K terimi 1024'ü (veya 2^10) ifade eder. 65.536, 64x1024'e eşit olduğundan bu sınıra _64K referans sınırı_ adı verilir.

Android 5.0'dan önceki sürümlerde multidex desteği

Platformun Android 5.0 (API düzeyi 21) öncesi sürümlerinde uygulama kodunu yürütmek için Dalvik çalışma zamanı kullanılır. Varsayılan olarak Dalvik, uygulamaları APK başına tek bir classes.dex bayt kodu dosyasıyla sınırlar. Bu sınırlamayı aşmak için multidex kitaplığını modül düzeyindeki build.gradle veya build.gradle.kts dosyasına ekleyin:

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

Bu kitaplık, uygulamanızın birincil DEX dosyasının bir parçası olur ve ardından ek DEX dosyalarına ve bu dosyaları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ümlerine bakın.

Daha fazla bilgi için Uygulamanızı multidex için yapılandırma başlıklı bölümü inceleyin.

Android 5.0 ve sonraki sürümlerde 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 sırasında ön derleme gerçekleştirir, classesN.dex dosyalarını tarar ve bunları Android cihaz tarafından yürütülmek üzere tek bir OAT dosyası olarak derler. Bu nedenle, minSdkVersion 21 veya daha yüksekse multidex varsayılan olarak etkindir ve multidex kitaplığına ihtiyacınız yoktur.

Android 5.0 çalışma zamanı hakkında daha fazla bilgi için Android Çalışma Zamanı (ART) ve Dalvik başlıklı makaleyi inceleyin.

Not: Uygulamanızı Android Studio'yu kullanarak çalıştırdığınızda, derleme, dağıtım yaptığınız hedef cihazlar için optimize edilir. Hedef cihazlarda Android 5.0 ve sonraki sürümler çalışırken multidex'in etkinleştirilmesi de buna dahildir. Bu optimizasyon yalnızca uygulamanızı Android Studio kullanarak dağıtırken uygulandığından 64K sınırını aşmamak için yayın derlemenizi multidex için yapılandırmanız gerekebilir.

64.000 karakter sınırını aşmaktan kaçının

Uygulamanızı 64.000 veya daha fazla yöntem referansının kullanımını etkinleştirecek şekilde yapılandırmadan önce, uygulama kodunuz tarafından tanımlanan veya dahil edilen kitaplıklar da dahil olmak üzere uygulama kodunuz tarafından çağrılan toplam referans sayısını azaltmak için gerekli adımları uygulayın.

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 kitaplık bağımlılıklarının değerinin, uygulamaya eklenen kod miktarından daha fazla olup olmadığını değerlendirin. Birkaç yardımcı yöntemin faydalı olması nedeniyle çok büyük bir kitaplığın eklenmesi yaygın ancak sorunlu bir durumdur. Uygulama kodu bağımlılıklarınızı azaltmak genellikle DEX referans sınırından kaçınmanıza yardımcı olabilir.
R8 ile kullanılmayan kodu kaldırma
Sürüm derlemelerinizde R8'i çalıştırmak için
kod küçültmeyi etkinleştirin. APK'larınızla kullanılmayan kodları göndermediğinizden emin olmak için küçültmeyi etkinleştirin. Kod küçültme doğru şekilde yapılandırılırsa bağımlılıklarınızdaki kullanılmayan kodları ve kaynakları da kaldırabilir.

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

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

Not: minSdkVersion değeriniz 21 veya daha yüksekse multidex varsayılan olarak etkindir ve multidex kitaplığına ihtiyacınız yoktur.

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

  1. Multidex'i etkinleştirmek ve multidex kitaplığını bağımlılık olarak eklemek için modül düzeyindeki build.gradle dosyasını aşağıdaki gibi değiştirin:

    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")
    }
  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ılmıyorsanız manifest dosyanızı düzenleyerek <application> etiketinde android:name değerini aşağıdaki gibi ayarlayın:

      <?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 gibi MultiDexApplication sınıfını genişletecek şekilde 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 olmazsa bunun yerine attachBaseContext() yöntemini geçersiz kılın ve çoklu dex'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() ya da başka bir kodu yürütmeyin. Multidex izleme bu çağrıları takip etmez. Bu durum, DEX dosyaları arasında kötü bir sınıf bölümlemesi nedeniyle ClassNotFoundException veya doğrulama hatalarına neden olur.

Artık uygulamanızı oluşturduğunuzda Android derleme araçları, gerektiğinde 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, yalnızca ana classes.dex dosyasında arama yapmak yerine, çoklu DEX API'leri, yöntemleriniz için mevcut tüm DEX dosyalarında arama yapmak üzere özel bir sınıf yükleyici kullanır.

Multidex kitaplığının sınırlamaları

Çoklu dex kitaplığının bilinen bazı sınırlamaları vardır. Kitaplığı uygulamanızın derleme yapılandırmasına dahil ederken aşağıdakileri göz önünde bulundurun:

  • Başlangıç sırasında DEX dosyalarının cihazın veri bölümüne yüklenmesi karmaşıktır ve ikincil DEX dosyaları büyükse Uygulama Yanıt Vermiyor (ANR) hatalarına neden olabilir. Bu sorunu önlemek için kod küçültmeyi etkinleştirerek DEX dosyalarının boyutunu en aza indirin ve kodun kullanılmayan kısımlarını kaldırın.
  • Android 5.0'dan (API düzeyi 21) önceki sürümlerde çalıştırılırken, linearalloc sınırını (37008143 numaralı sorun) aşmak için multidex kullanmak yeterli değildir. Bu sınır, Android 4.0'da (API düzeyi 14) artırılmış olsa da sorun tamamen çözülmemiştir.

    Android 4.0'dan önceki sürümlerde, DEX dizin sınırına ulaşmadan önce linearalloc sınırına ulaşabilirsiniz. Bu nedenle, 14'ten düşük API seviyelerini hedefliyorsanız platformun bu sürümlerinde kapsamlı bir test yapın. Aksi takdirde, uygulamanız başlatılırken veya belirli sınıf grupları yüklenirken sorun yaşayabilir.

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

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

Çoklu DEX uygulaması için her bir DEX dosyası oluşturulurken derleme araçları, uygulamanızın başarılı bir şekilde başlatılabilmesi için birincil DEX dosyasında hangi sınıfların gerekli olduğunu belirlemek üzere karmaşık kararlar verir. Başlatma sırasında gerekli olan bir sınıf birincil DEX dosyasında sağlanmazsa 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, kod yolları daha az görünür olduğunda (ör. kullandığınız bir kitaplığın karmaşık bağımlılıkları olduğunda) ortaya çıkabilir. Örneğin, kod, Java yöntemlerinin yerel koddan yansıtılmasını veya çağrılmasını kullanıyorsa bu sınıflar birincil DEX dosyasında gerekli olarak tanınmayabilir.

java.lang.NoClassDefFoundError hatası alırsanız birincil DEX dosyasında gereken ek sınıfları, derleme türünüzde multiDexKeepProguard özelliğiyle bildirerek manuel olarak belirtmeniz gerekir. multiDexKeepProguard dosyasında eşleşen bir sınıf varsa bu sınıf birincil DEX dosyasına eklenir.

multiDexKeepProguard özelliği

multiDexKeepProguard dosyası, ProGuard ile aynı biçimi kullanır ve ProGuard gramerinin tamamını destekler. Uygulamanızda nelerin tutulacağını özelleştirme hakkında daha fazla bilgi için Hangi kodun tutulacağını özelleştirme başlıklı makaleyi inceleyin.

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

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

Bir paketteki tüm sınıfları belirtmek istiyorsanız dosya şu şekilde görünür:

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

Ardından, bu dosyayı aşağıdaki gibi bir derleme türü için bildirebilirsiniz:

Groovy

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

Kotlin

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

Geliştirme derlemelerinde multidex'i optimize etme

Çoklu DEX yapılandırması, derleme sistemi 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 vermek zorunda olduğundan derleme işleme süresinin önemli ölçüde artmasını gerektirir. Bu nedenle, multidex kullanan artımlı derlemeler genellikle daha uzun sürer ve geliştirme sürecinizi yavaşlatabilir.

Daha uzun artımlı derleme sürelerini azaltmak için derlemeler arasında çoklu dex çıkışını yeniden kullanmak üzere önceden dexleme özelliğini kullanın. Önceden dex oluşturma, 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 bir sürümü çalıştıran bir cihaza dağıtırken otomatik olarak önceden dexleme kullanır. Ancak Gradle derlemelerini komut satırından çalıştırıyorsanız önceden dexleme özelliğini etkinleştirmek için minSdkVersion değerini 21 veya daha yüksek bir değere ayarlamanız gerekir.

Üretim derlemenizin ayarlarını korumak için uygulamanızın iki sürümünü ürün çeşitlerini kullanarak oluşturabilirsiniz. Bu sürümlerden biri geliştirme çeşidine, diğeri ise yayın çeşidine sahip olur. minSdkVersion için farklı değerler kullanılır.

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 veya komut satırından derleme hızlarını artırmaya yardımcı olacak daha fazla strateji öğrenmek için Derleme hızınızı optimize etme başlıklı makaleyi inceleyin. Derleme varyantlarını kullanma hakkında daha fazla bilgi için Derleme varyantlarını yapılandırma başlıklı makaleyi inceleyin.

İpucu: Farklı çoklu dex 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 daha düşük olan dosya, <application> etiket adını değiştirir. Ayrıca, her varyant için farklı bir Application alt sınıfı da oluşturabilirsiniz. Böylece yalnızca API düzeyi 20 ve önceki sürümler için olan alt sınıf, MultiDexApplication sınıfını genişletir veya MultiDex.install(this)'ı çağırır.

Multidex uygulamalarını test etme

Çoklu dex uygulamaları için enstrümantasyon testleri yazarken MonitoringInstrumentation veya AndroidJUnitRunner enstrümantasyonu kullanıyorsanız ek yapılandırma gerekmez. Başka bir Instrumentation kullanıyorsanız 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);
  ...
}