หากแอปมี 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,000 รายการ
ไฟล์แอป Android (APK) มีไฟล์ไบต์โค้ดปฏิบัติการในรูปแบบ ของดัลวิก ไฟล์ปฏิบัติการ (DEX) ซึ่งมีโค้ดที่คอมไพล์แล้วซึ่งใช้เพื่อเรียกใช้แอป ข้อกำหนดเฉพาะของ Dalvik Executable จะจำกัดจำนวนเมธอดทั้งหมดที่ อ้างอิงได้ภายในไฟล์ DEX ไฟล์เดียวถึง 65,536 ไฟล์ ซึ่งรวมถึง Android ด้วย เมธอดของเฟรมเวิร์ก เมธอดของไลบรารี และเมธอดในโค้ดของคุณเอง
ใน บริบทของวิทยาการคอมพิวเตอร์ คำว่า kilo หรือ K หมายถึง 1024 (หรือ 2^10) เนื่องจาก 65,536 เท่ากับ 64x1024 ขีดจำกัดนี้เรียกว่า _ขีดจำกัดการอ้างอิง 64Kการรองรับ Multidex ก่อน Android 5.0
แพลตฟอร์มเวอร์ชันก่อน Android 5.0 (API ระดับ 21) ใช้ Dalvik
รันไทม์สำหรับการเรียกใช้โค้ดของแอป โดยค่าเริ่มต้น Dalvik จะจำกัดแอปไว้ไม่เกิน 1 รายการ
ไฟล์ไบต์โค้ด classes.dex
ไฟล์ต่อ APK วิธีหลีกเลี่ยง
ให้เพิ่มไลบรารี Multidex ในระดับโมดูล build.gradle
หรือ
วันที่ build.gradle.kts
ไฟล์:
ดึงดูด
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 ตั้งแต่แรก ศิลปะ
ทำการคอมไพล์ล่วงหน้า ณ เวลาที่ติดตั้งแอป โดยสแกนหา
classesN.dex
ไฟล์และรวมเป็น 1 ไฟล์
ไฟล์ OAT สำหรับ
ปฏิบัติการโดยอุปกรณ์ Android ดังนั้นหากminSdkVersion
21 ขึ้นไป ระบบจะเปิดใช้ Multidex โดยค่าเริ่มต้น แต่คุณไม่จำเป็นต้องใช้ไลบรารี multidex
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Android 5.0 ให้อ่าน Android Runtime (ART) และ Dalvik
หมายเหตุ: เมื่อเรียกใช้แอปโดยใช้ Android Studio บิลด์นี้ได้รับการปรับให้เหมาะกับอุปกรณ์เป้าหมายที่คุณทำให้ใช้งานได้ ซึ่งรวมถึงการเปิดใช้ Multidex เมื่ออุปกรณ์เป้าหมายทำงานอยู่ Android 5.0 ขึ้นไป เนื่องจากการเพิ่มประสิทธิภาพนี้จะใช้เฉพาะเมื่อทำให้แอปใช้งานได้โดยใช้ คุณอาจยังต้องกำหนดค่าบิลด์ของรุ่นสำหรับ Android Studio สำหรับ multidex เพื่อหลีกเลี่ยงขีดจำกัด 64K
หลีกเลี่ยงขีดจำกัด 64K
ทำตามขั้นตอนก่อนกำหนดค่าแอปให้เปิดใช้การอ้างอิงเมธอด 64K ขึ้นไป เพื่อลดจำนวนการอ้างอิงทั้งหมดที่โค้ดของแอปเรียกใช้ ซึ่งรวมถึงวิธีการที่กำหนดโดย โค้ดของแอปหรือไลบรารีที่รวมอยู่
กลยุทธ์ต่อไปนี้จะช่วยให้คุณหลีกเลี่ยงไม่ให้ถึงขีดจำกัดการอ้างอิงของ DEX
- ตรวจสอบทรัพยากร Dependency แบบโดยตรงและแบบทรานซิทีฟของแอป
- พิจารณาว่ามูลค่าของทรัพยากร Dependency ของไลบรารีขนาดใหญ่ที่คุณใส่ในแอปมีน้ำหนักมากกว่าโค้ดหรือไม่ กำลังเพิ่มลงในแอป รูปแบบที่พบบ่อยแต่เป็นปัญหาคือการรวมไลบรารีขนาดใหญ่มาก เพราะวิธีการยูทิลิตีที่มีประโยชน์ไม่กี่วิธี การลดทรัพยากร Dependency ของโค้ดแอปมักจะช่วยแก้ปัญหานี้ได้ เพื่อหลีกเลี่ยงขีดจำกัดการอ้างอิง DEX
- นำโค้ดที่ไม่ได้ใช้ออกด้วย R8
- เปิดใช้การย่อโค้ดเพื่อเรียกใช้ R8 สำหรับบิลด์ที่เผยแพร่ เปิดใช้การย่อขนาดลงเพื่อช่วยให้มั่นใจได้ว่า จะไม่จัดส่งโค้ดที่ไม่ได้ใช้งานด้วย APK ของคุณ หากมีการกำหนดค่าการย่อโค้ดอย่างถูกต้อง สามารถนำโค้ดและทรัพยากรที่ไม่ได้ใช้ออกจากทรัพยากร Dependency ได้ด้วย
การใช้เทคนิคเหล่านี้จะช่วยคุณลดขนาดโดยรวมของ APK และ ทำให้ไม่จำเป็นต้องใช้แบบ Multidex ในแอป
กำหนดค่าแอปสำหรับ Multidex
หมายเหตุ: หากตั้งค่าminSdkVersion
เป็น 21 ขึ้นไป ระบบจะเปิดใช้ Multidex โดยค่าเริ่มต้น
แต่ไม่ต้องใช้ไลบรารี Multidex
หากตั้งค่า minSdkVersion
เป็น 20 หรือต่ำกว่า
ต้องใช้เมธอด
ไลบรารี Multidex และสร้าง
การแก้ไขต่อไปนี้ในโปรเจ็กต์แอปของคุณ:
-
แก้ไขไฟล์
build.gradle
ระดับโมดูลเป็น เปิดใช้ multidex และเพิ่มไลบรารี multidex เป็น Dependency ตามที่แสดงอยู่ที่นี่ดึงดูด
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
ขณะรันไทม์ แทนที่จะค้นหาเฉพาะใน
classes.dex
API ของ Multidex จะใช้ตัวโหลดคลาสพิเศษเพื่อค้นหา
ไฟล์ DEX ที่พร้อมใช้งานสำหรับเมธอดของคุณ
ข้อจำกัดของไลบรารี Multidex
ไลบรารี Multidex มีข้อจำกัดบางอย่างที่ทราบ เมื่อรวมไลบรารีไว้ในการกำหนดค่าบิลด์ของแอป โปรดพิจารณาสิ่งต่อไปนี้
- การติดตั้งไฟล์ DEX ระหว่างการเริ่มต้นบนพาร์ติชันข้อมูลของอุปกรณ์มีความซับซ้อนและ อาจส่งผลให้เกิดข้อผิดพลาด "แอปพลิเคชันไม่ตอบสนอง (ANR)" หากไฟล์ DEX รองมีขนาดใหญ่ ถึง ให้หลีกเลี่ยงปัญหานี้ เปิดใช้การย่อโค้ดเพื่อลดขนาด ขนาดของไฟล์ DEX และนำโค้ดส่วนที่ไม่ได้ใช้ออก
- เมื่อใช้เวอร์ชันก่อน Android 5.0 (API ระดับ 21) ให้ใช้
Multidex ไม่เพียงพอที่จะหลีกเลี่ยงค่า Linear Alloc (ปัญหา 37008143) ขีดจำกัดนี้เพิ่มขึ้นในเดือน
Android 4.0 (API ระดับ 14) แต่ยังแก้ปัญหาได้ไม่สมบูรณ์
ในเวอร์ชันที่ต่ำกว่า Android 4.0 คุณอาจใช้งานถึงขีดจำกัด Linearalloc ถึงขีดจำกัดดัชนี DEX ดังนั้น หากคุณกำหนดเป้าหมายระดับ API ต่ำกว่า 14 ทดสอบแพลตฟอร์มเวอร์ชันดังกล่าวอย่างละเอียดถี่ถ้วน เพราะแอปของคุณอาจ เกิดปัญหาเมื่อเริ่มต้นระบบหรือเมื่อโหลดชั้นเรียนบางกลุ่ม
การย่อโค้ดสามารถลดหรือ อาจกำจัดปัญหาเหล่านี้ได้
ประกาศคลาสที่จำเป็นในไฟล์ DEX หลัก
เมื่อสร้างไฟล์ DEX แต่ละไฟล์สำหรับแอป Multidex เครื่องมือสร้างจะทำงาน
การตัดสินใจที่ซับซ้อนเพื่อกำหนดว่าชั้นเรียนใดที่จำเป็นใน DEX หลัก
เพื่อให้แอปของคุณเริ่มทำงานได้สำเร็จ หากมีชั้นเรียนที่จำเป็น
ในช่วงเริ่มต้นไม่ได้ระบุไว้ในไฟล์ DEX หลัก แอปของคุณขัดข้อง
มีข้อผิดพลาด java.lang.NoClassDefFoundError
เครื่องมือสร้างจะจดจำเส้นทางโค้ดสำหรับโค้ดที่เข้าถึงจากแอปของคุณโดยตรง โค้ด อย่างไรก็ตาม ปัญหานี้อาจ เกิดขึ้นเมื่อเส้นทางโค้ดปรากฏน้อยลง เช่น เมื่อไลบรารีที่คุณใช้มี ทรัพยากร Dependency ที่ซับซ้อน ตัวอย่างเช่น หากโค้ดใช้การพิจารณาภายในหรือการเรียกใช้ ของเมธอด 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
จากนั้นคุณจะประกาศไฟล์นั้นสำหรับประเภทบิลด์ได้ดังนี้
ดึงดูด
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
เพิ่มประสิทธิภาพ Multidex ในรุ่นการพัฒนา
การกำหนดค่า Multidex ต้องใช้การประมวลผลบิลด์ที่เพิ่มขึ้นอย่างมาก เนื่องจากระบบการสร้างต้องทำการตัดสินใจที่ซับซ้อนว่าชั้นเรียนใด ต้องอยู่ในไฟล์ DEX หลักและคลาสที่รวมไว้ในไฟล์ได้ ไฟล์ DEX รอง ซึ่งหมายความว่าโดยทั่วไปแล้วบิลด์ที่เพิ่มขึ้นโดยใช้ Multidex ใช้เวลานานกว่าปกติและอาจทำให้ขั้นตอนการพัฒนาช้าลง
หากต้องการลดเวลาในการสร้างที่นานขึ้น ให้ใช้
pre-dexing เพื่อนำเอาต์พุต Multidex มาใช้ซ้ำระหว่างบิลด์
ไฟล์ Pre-dexing จะใช้รูปแบบ ART ซึ่งมีอยู่ใน Android 5.0 เท่านั้น
(API ระดับ 21) ขึ้นไป หากคุณใช้ Android Studio อยู่ IDE จะใช้การเข้ารหัสล่วงหน้าโดยอัตโนมัติ
เมื่อทำให้แอปใช้งานได้ในอุปกรณ์ที่ใช้ Android 5.0 (API ระดับ 21) ขึ้นไป
แต่หากคุณเรียกใช้บิลด์ Gradle จากบรรทัดคำสั่ง คุณต้องตั้งค่าพารามิเตอร์
minSdkVersion
เป็น 21 ขึ้นไปเพื่อเปิดใช้การเข้ารหัสล่วงหน้า
minSdkVersion
ดังต่อไปนี้
ดึงดูด
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); ... }