หากแอปมี minSdk
ของ API ระดับ 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) ไฟล์เดียว หน้านี้อธิบายวิธีก้าวข้ามข้อจำกัดนี้โดยเปิดใช้การกำหนดค่าแอปที่เรียกว่า multidex ซึ่งช่วยให้แอปสร้างและอ่านไฟล์ DEX หลายไฟล์ได้
เกี่ยวกับขีดจํากัดข้อมูลอ้างอิง 64 KB
ไฟล์แอป Android (APK) มีไฟล์ไบต์โค้ดที่เรียกใช้งานได้ในรูปแบบไฟล์ Dalvik Executable (DEX) ซึ่งมีโค้ดที่คอมไพล์แล้วซึ่งใช้เรียกใช้แอป ข้อกำหนดของ Dalvik Executable จำกัดจำนวนเมธอดทั้งหมดที่อ้างอิงได้ภายในไฟล์ DEX ไฟล์เดียวไว้ที่ 65,536 เมธอด ซึ่งรวมถึงเมธอดเฟรมเวิร์ก Android, เมธอดไลบรารี และเธอดในโค้ดของคุณเอง
ในบริบทของวิทยาการคอมพิวเตอร์ คําว่า กิโล หรือ K หมายถึง 1024 (หรือ 2^10) เนื่องจาก 65,536 เท่ากับ 64x1024 ขีดจํากัดนี้จึงเรียกว่า _ขีดจํากัดข้อมูลอ้างอิง 64K_การรองรับ Multidex ก่อน Android 5.0
แพลตฟอร์มเวอร์ชันก่อน Android 5.0 (API ระดับ 21) ใช้รันไทม์ Dalvik ในการเรียกใช้โค้ดแอป โดยค่าเริ่มต้น Dalvik จะจำกัดแอปให้เป็นไฟล์ไบต์โค้ด classes.dex
ไฟล์เดียวต่อ APK หากต้องการหลีกเลี่ยงข้อจำกัดนี้ ให้เพิ่มไลบรารี Multidex ลงในไฟล์ 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 เพิ่มเติมและโค้ดที่อยู่ในไฟล์เหล่านั้น หากต้องการดูเวอร์ชันปัจจุบันของไลบรารีนี้ โปรดดู เวอร์ชัน Multidex
ดูรายละเอียดเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธีกำหนดค่าแอปสำหรับ MultiDexการรองรับ Multidex สำหรับ Android 5.0 ขึ้นไป
Android 5.0 (API ระดับ 21) ขึ้นไปใช้รันไทม์ที่เรียกว่า ART ซึ่งรองรับการโหลดไฟล์ DEX หลายไฟล์จากไฟล์ APK โดยกำเนิด ART จะทำการคอมไพล์ล่วงหน้าเมื่อติดตั้งแอป โดยสแกนหาไฟล์ classesN.dex
และคอมไพล์เป็นไฟล์ OAT ไฟล์เดียวเพื่อให้อุปกรณ์ Android เรียกใช้ได้ ดังนั้น หาก minSdkVersion
ของคุณคือ 21 ขึ้นไป ระบบจะเปิดใช้ multidex โดยค่าเริ่มต้นและคุณไม่จำเป็นต้องใช้ไลบรารี multidex
ดูข้อมูลเพิ่มเติมเกี่ยวกับรันไทม์ของ Android 5.0 ได้ที่ Android Runtime (ART) และ Dalvik
หมายเหตุ: เมื่อเรียกใช้แอปโดยใช้ Android Studio ระบบจะเพิ่มประสิทธิภาพบิลด์สำหรับอุปกรณ์เป้าหมายที่คุณนำไปใช้งาน ซึ่งรวมถึงการเปิดใช้ MultiDex เมื่ออุปกรณ์เป้าหมายใช้ Android 5.0 ขึ้นไป เนื่องจากการเพิ่มประสิทธิภาพนี้จะมีผลเมื่อคุณติดตั้งใช้งานแอปโดยใช้ Android Studio เท่านั้น คุณจึงอาจยังต้องกําหนดค่าบิวด์รุ่นสําหรับ MultiDex เพื่อหลีกเลี่ยงขีดจํากัด 64 KB
หลีกเลี่ยงขีดจำกัด 64K
ก่อนกำหนดค่าแอปเพื่อเปิดใช้การอ้างอิงเมธอด 64,000 รายการขึ้นไป ให้ทำตามขั้นตอนเพื่อลดจํานวนการอ้างอิงทั้งหมดที่โค้ดแอปเรียกใช้ ซึ่งรวมถึงเมธอดที่กําหนดโดยโค้ดแอปหรือไลบรารีที่รวมไว้
กลยุทธ์ต่อไปนี้จะช่วยคุณหลีกเลี่ยงไม่ให้เกินขีดจํากัดการอ้างอิงของ DEX
- ตรวจสอบการพึ่งพาโดยตรงและแบบสื่อกลางของแอป
- พิจารณาว่าค่าของไลบรารีขนาดใหญ่ที่คุณรวมไว้ในแอปมีมากกว่าปริมาณโค้ดที่เพิ่มลงในแอปหรือไม่ รูปแบบที่พบบ่อยแต่มีปัญหาคือการรวมไลบรารีขนาดใหญ่มากเนื่องจากมีเมธอดยูทิลิตีเพียงไม่กี่รายการที่มีประโยชน์ การลดการพึ่งพาโค้ดแอปมักจะช่วยหลีกเลี่ยงขีดจํากัดการอ้างอิง DEX ได้
- นำโค้ดที่ไม่ได้ใช้งานออกด้วย R8
- เปิดใช้การลดขนาดโค้ดเพื่อเรียกใช้ R8 สำหรับบิลด์ที่เผยแพร่ เปิดใช้การลดขนาดเพื่อช่วยตรวจสอบว่าคุณไม่ได้จัดส่งโค้ดที่ไม่ได้ใช้ไปกับ APK หากกําหนดค่าการลดขนาดโค้ดอย่างถูกต้อง ก็จะนําโค้ดและทรัพยากรที่ไม่ได้ใช้งานออกจากทรัพยากร Dependency ได้ด้วย
การใช้เทคนิคเหล่านี้จะช่วยคุณลดขนาดโดยรวมของ APK และหลีกเลี่ยงความจำเป็นในการใช้ MultiDex ในแอป
กำหนดค่าแอปสำหรับ Multidex
หมายเหตุ: หากตั้งค่าminSdkVersion
เป็น 21 ขึ้นไป ระบบจะเปิดใช้ MultiDex โดยค่าเริ่มต้นและคุณไม่จำเป็นต้องใช้ไลบรารี MultiDex
หากตั้งค่า minSdkVersion
เป็น 20 หรือต่ำกว่า คุณต้องใช้ไลบรารี MultiDex และทำการแก้ไขต่อไปนี้ในโปรเจ็กต์แอป
-
แก้ไขไฟล์
build.gradle
ระดับโมดูลเพื่อเปิดใช้ multidex และเพิ่มไลบรารี multidex เป็น Dependency ดังที่แสดงที่นี่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
ให้แก้ไขไฟล์ Manifest เพื่อตั้งค่าandroid:name
ในแท็ก<application>
ดังนี้<?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)
เพื่อเปิดใช้ multidex แทน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()
จะเสร็จสมบูรณ์ การติดตาม Multidex จะไม่ติดตามการเรียกเหล่านั้น ซึ่งจะทำให้เกิดClassNotFoundException
หรือข้อผิดพลาดในการยืนยันเนื่องจากมีการแบ่งพาร์ติชันคลาสที่ไม่ถูกต้องระหว่างไฟล์ DEX
เมื่อคุณสร้างแอป เครื่องมือสร้างของ Android จะสร้างไฟล์ DEX หลัก (classes.dex
) และไฟล์ DEX ที่รองรับ (classes2.dex
, classes3.dex
และอื่นๆ) ตามที่จำเป็น
จากนั้นระบบบิลด์จะจัดแพ็กเกจไฟล์ DEX ทั้งหมดลงใน APK
ขณะรันไทม์ MultiDex API จะใช้ตัวโหลดคลาสพิเศษเพื่อค้นหาเมธอดของคุณในไฟล์ DEX ทั้งหมดที่มีอยู่แทนที่จะค้นหาเฉพาะในไฟล์ classes.dex
หลัก
ข้อจำกัดของคลัง MultiDex
ไลบรารี MultiDex มีข้อจำกัดบางประการที่ทราบ เมื่อรวมไลบรารีไว้ในการกำหนดค่าการสร้างแอป ให้พิจารณาสิ่งต่อไปนี้
- การติดตั้งไฟล์ DEX ในระหว่างการเริ่มต้นลงในพาร์ติชันข้อมูลของอุปกรณ์มีความซับซ้อนและอาจทำให้เกิดข้อผิดพลาด "แอปพลิเคชันไม่ตอบสนอง" (ANR) หากไฟล์ DEX รองมีขนาดใหญ่ หากต้องการหลีกเลี่ยงปัญหานี้ ให้เปิดใช้การลดขนาดโค้ดเพื่อลดขนาดไฟล์ DEX และนำโค้ดส่วนที่ไม่ได้ใช้ออก
- เมื่อทำงานในเวอร์ชันก่อน Android 5.0 (API ระดับ 21) การใช้ multidex ไม่เพียงพอที่จะหลีกเลี่ยงข้อจำกัด Linearalloc (ปัญหา 37008143) ขีดจํากัดนี้เพิ่มขึ้นใน Android 4.0 (API ระดับ 14) แต่ก็ยังแก้ปัญหาได้ไม่สมบูรณ์
ในเวอร์ชันที่ต่ำกว่า Android 4.0 คุณอาจถึงขีดจำกัดของ linearalloc ก่อนถึงขีดจำกัดของดัชนี DEX ดังนั้น หากคุณกําหนดเป้าหมาย API ระดับต่ำกว่า 14 ให้ทดสอบอย่างละเอียดในแพลตฟอร์มเวอร์ชันเหล่านั้น เนื่องจากแอปอาจมีปัญหาเมื่อเริ่มต้นหรือเมื่อโหลดคลาสบางกลุ่ม
การลดขนาดโค้ดสามารถลดหรืออาจขจัดปัญหาเหล่านี้ได้
ประกาศคลาสที่จำเป็นในไฟล์ DEX หลัก
เมื่อสร้างไฟล์ DEX แต่ละไฟล์สําหรับแอปแบบ MultiDex เครื่องมือสร้างจะทําการตัดสินใจที่ซับซ้อนเพื่อระบุคลาสที่จําเป็นในไฟล์ DEX หลักเพื่อให้แอปเริ่มทํางานได้สําเร็จ หากไม่ได้ระบุคลาสที่จําเป็นในระหว่างการเริ่มต้นในไฟล์ DEX หลัก แอปจะขัดข้องพร้อมข้อผิดพลาด java.lang.NoClassDefFoundError
เครื่องมือสร้างจะจดจำเส้นทางโค้ดสําหรับโค้ดที่เข้าถึงจากโค้ดแอปโดยตรง อย่างไรก็ตาม ปัญหานี้อาจเกิดขึ้นเมื่อเส้นทางโค้ดมองเห็นได้น้อยลง เช่น เมื่อไลบรารีที่คุณใช้มีความซับซ้อน เช่น หากโค้ดใช้การตรวจสอบหรือการเรียกเมธอด Java จากโค้ดแบบเนทีฟ คลาสเหล่านั้นอาจไม่ได้รับการจดจำว่าจำเป็นในไฟล์ 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
จากนั้นคุณสามารถประกาศไฟล์นั้นสําหรับประเภทบิลด์ ดังนี้
Groovy
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
เพิ่มประสิทธิภาพ MultiDex ในบิลด์สำหรับการพัฒนา
การกําหนดค่า MultiDex จะเพิ่มเวลาการประมวลผลการบิลด์อย่างมาก เนื่องจากระบบบิลด์ต้องตัดสินใจอย่างซับซ้อนว่าคลาสใดต้องรวมอยู่ในไฟล์ DEX หลักและคลาสใดสามารถรวมอยู่ในไฟล์ DEX รองได้ ซึ่งหมายความว่าบิลด์แบบเพิ่มโดยใช้ MultiDex มักจะใช้เวลานานขึ้นและอาจทำให้กระบวนการพัฒนาช้าลง
หากต้องการลดเวลาในการสร้างที่เพิ่มขึ้น ให้ใช้การแยกไฟล์ Dex ล่วงหน้าเพื่อนำเอาเอาต์พุต MultiDex มาใช้ซ้ำระหว่างการสร้าง
การแปลงไฟล์ก่อนการจัดทําไฟล์ DEX จะใช้รูปแบบ ART ที่มีให้ใช้งานใน Android 5.0 (API ระดับ 21) ขึ้นไปเท่านั้น หากคุณใช้ Android Studio IDE จะใช้การแยกไฟล์ก่อนการคอมไพล์โดยอัตโนมัติเมื่อคุณติดตั้งแอปในอุปกรณ์ที่ใช้ Android 5.0 (API ระดับ 21) ขึ้นไป
อย่างไรก็ตาม หากเรียกใช้บิลด์ 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 หรือบรรทัดคำสั่ง โปรดอ่านเพิ่มประสิทธิภาพความเร็วของบิลด์ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ตัวแปรของบิลด์ได้ที่กำหนดค่าตัวแปรของบิลด์
เคล็ดลับ: หากมีตัวแปรของบิลด์ที่แตกต่างกันตามความต้องการของ Multidex ที่แตกต่างกัน คุณอาจระบุไฟล์ Manifest ที่แตกต่างกันสำหรับแต่ละตัวแปรเพื่อให้เฉพาะไฟล์สำหรับ API ระดับ 20 และต่ำกว่าเปลี่ยนชื่อแท็ก <application>
ได้ คุณยังสร้างคลาสย่อย Application
ที่แตกต่างกันสำหรับแต่ละตัวแปรได้ด้วย เพื่อให้เฉพาะคลาสย่อยสำหรับ API ระดับ 20 และต่ำกว่าเท่านั้นขยายคลาส MultiDexApplication
หรือการเรียกใช้ MultiDex.install(this)
ทดสอบแอป Multidex
เมื่อเขียนการทดสอบที่ใช้เครื่องมือวัดผลสําหรับแอป MultiDex คุณไม่จําเป็นต้องกําหนดค่าเพิ่มเติมหากใช้เครื่องมือวัดผล
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); ... }