ลดขนาด ปรับให้ยากต่อการอ่าน (Obfuscate) และเพิ่มประสิทธิภาพแอปของคุณ

คุณควรเพิ่มประสิทธิภาพและลดขนาดบิลด์รุ่นด้วย isMinifyEnabled = true เพื่อให้แอปมีขนาดเล็กและรวดเร็วที่สุด

การทำเช่นนี้จะทำให้ลดขนาด ซึ่งจะนำโค้ดที่ไม่ได้ใช้ออก การสร้างความสับสน ซึ่งจะย่อชื่อคลาสและสมาชิกของแอป และการเพิ่มประสิทธิภาพ ซึ่งเป็นกลยุทธ์การเพิ่มประสิทธิภาพโค้ดที่ได้รับการปรับปรุงเพื่อลดขนาดและปรับปรุงประสิทธิภาพของแอปให้มากยิ่งขึ้น หน้านี้อธิบายวิธีที่ R8 ดำเนินงานเวลาคอมไพล์เหล่านี้สำหรับโปรเจ็กต์ของคุณ และวิธีปรับแต่ง

เมื่อสร้างโปรเจ็กต์โดยใช้ปลั๊กอิน Android Gradle 3.4.0 ขึ้นไป ปลั๊กอินจะไม่ใช้ ProGuard ในการเพิ่มประสิทธิภาพโค้ดเวลาคอมไพล์อีกต่อไป แต่จะใช้ร่วมกับคอมไพเลอร์ R8 เพื่อจัดการงานต่อไปนี้ในรันไทม์

  • การย่อโค้ด (หรือการสั่นต้นไม้): ตรวจหาและนำคลาส ช่อง วิธีการ และแอตทริบิวต์ที่ไม่ได้ใช้ออกจากแอปและการอ้างอิงของไลบรารีได้อย่างปลอดภัย (ทำให้เป็นเครื่องมือที่มีประโยชน์สำหรับการแก้ปัญหาขีดจำกัดการอ้างอิง 64,000 รายการ) เช่น หากคุณใช้ API เพียงไม่กี่รายการของไลบรารี เครื่องมือจะระบุโค้ดไลบรารีที่แอปไม่ได้ใช้และนำเฉพาะโค้ดนั้นออกจากแอปได้ ดูข้อมูลเพิ่มเติมได้ในส่วนวิธีลดขนาดโค้ด
  • การลดขนาดทรัพยากร: นำทรัพยากรที่ไม่ได้ใช้ออกจากแอปที่แพ็ก รวมถึงทรัพยากรที่ไม่ได้ใช้ใน Dependency ของไลบรารีของแอป ซึ่งทำงานร่วมกับการลดขนาดโค้ด เช่น เมื่อนำโค้ดที่ไม่ได้ใช้งานออกแล้ว ระบบจะนำทรัพยากรที่ไม่ได้อ้างอิงอีกต่อไปออกอย่างปลอดภัยด้วย ดูข้อมูลเพิ่มเติมได้ที่ส่วนวิธีลดขนาดทรัพยากร
  • การเพิ่มประสิทธิภาพ: ตรวจสอบและเขียนโค้ดใหม่เพื่อปรับปรุงประสิทธิภาพรันไทม์และลดขนาดไฟล์ DEX ของแอปให้น้อยลง ซึ่งจะปรับปรุงประสิทธิภาพรันไทม์ของโค้ดได้สูงสุด 30% ซึ่งจะปรับปรุงเวลาเริ่มต้นและเฟรมได้อย่างมาก ตัวอย่างเช่น หาก R8 ตรวจพบว่าไม่มีการใช้สาขา else {} ของคำสั่ง if/else หนึ่งๆ เลย R8 จะนําโค้ดสําหรับสาขา else {} ออก ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับการเพิ่มประสิทธิภาพโค้ด
  • การสร้างความสับสน (หรือการลดตัวระบุ): ย่อชื่อคลาสและสมาชิก ซึ่งส่งผลให้ไฟล์ DEX มีขนาดเล็กลง ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธีสร้างความสับสนให้กับโค้ด

เมื่อสร้างแอปเวอร์ชันที่เผยแพร่ คุณสามารถกําหนดค่า R8 ให้ทํางานต่างๆ ในเวลาคอมไพล์ตามที่อธิบายไว้ข้างต้นให้คุณได้ คุณยังปิดงานบางอย่างหรือปรับแต่งลักษณะการทำงานของ R8 ผ่านไฟล์กฎ ProGuard ได้ด้วย อันที่จริงแล้ว R8 ใช้งานได้กับไฟล์กฎ ProGuard ที่มีอยู่ทั้งหมด ดังนั้นการอัปเดตปลั๊กอิน Android Gradle เพื่อใช้ R8 จึงไม่จําเป็นต้องเปลี่ยนกฎที่มีอยู่

เปิดใช้การย่อ การสร้างความสับสน และการเพิ่มประสิทธิภาพ

เมื่อใช้ Android Studio 3.4 หรือปลั๊กอิน Android Gradle 3.4.0 ขึ้นไป R8 จะเป็นคอมไพเลอร์เริ่มต้นที่จะแปลงไบต์โค้ด Java ของโปรเจ็กต์เป็นรูปแบบ DEX ที่ทำงานบนแพลตฟอร์ม Android อย่างไรก็ตาม เมื่อคุณสร้างโปรเจ็กต์ใหม่โดยใช้ Android Studio ระบบจะไม่เปิดใช้การบีบอัด การสร้างความสับสน และการเพิ่มประสิทธิภาพโค้ดโดยค่าเริ่มต้น เนื่องจากการเพิ่มประสิทธิภาพเวลาคอมไพล์เหล่านี้จะเพิ่มเวลาสร้างโปรเจ็กต์และอาจทำให้เกิดข้อบกพร่องหากคุณปรับแต่งโค้ดที่จะเก็บไว้ไม่เพียงพอ

ดังนั้น คุณควรเปิดใช้งานเหล่านี้เมื่อคอมไพล์แอปเวอร์ชันสุดท้ายที่จะทดสอบก่อนเผยแพร่ หากต้องการเปิดใช้การย่อ การสร้างความสับสน และการเพิ่มประสิทธิภาพ ให้ใส่ข้อมูลต่อไปนี้ในสคริปต์บิลด์ระดับโปรเจ็กต์

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Groovy

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

ไฟล์การกําหนดค่า R8

R8 ใช้ไฟล์กฎ ProGuard เพื่อแก้ไขลักษณะการทำงานเริ่มต้นและทําความเข้าใจโครงสร้างของแอปได้ดียิ่งขึ้น เช่น คลาสที่ทำหน้าที่เป็นจุดแรกเข้าของโค้ดแอป แม้ว่าคุณจะแก้ไขไฟล์กฎบางไฟล์ได้ แต่เครื่องมือเวลาคอมไพล์ เช่น AAPT2 อาจสร้างกฎบางรายการโดยอัตโนมัติ หรืออาจรับค่ามาจากไลบรารีที่แอปของคุณใช้ร่วมกัน ตารางด้านล่างอธิบายแหล่งที่มาของไฟล์กฎ ProGuard ที่ R8 ใช้

แหล่งที่มา ตำแหน่ง คำอธิบาย
Android Studio <module-dir>/proguard-rules.pro เมื่อคุณสร้างโมดูลใหม่โดยใช้ Android Studio IDE จะสร้างไฟล์ proguard-rules.pro ในไดเรกทอรีรูทของโมดูลนั้น

โดยค่าเริ่มต้น ไฟล์นี้จะไม่มีการใช้กฎใดๆ ดังนั้น ให้ใส่กฎ ProGuard ของคุณเองที่นี่ เช่น กฎการเก็บรักษาที่กําหนดเอง

ปลั๊กอิน Android Gradle สร้างโดยปลั๊กอิน Android Gradle ในเวลาคอมไพล์ ปลั๊กอิน Android Gradle จะสร้าง proguard-android-optimize.txt ซึ่งมีกฎที่เป็นประโยชน์ต่อโปรเจ็กต์ Android ส่วนใหญ่ และเปิดใช้@Keep*คำอธิบายประกอบ

โดยค่าเริ่มต้น เมื่อสร้างโมดูลใหม่โดยใช้ Android Studio สคริปต์การสร้างระดับโมดูลจะรวมไฟล์กฎนี้ไว้ในบิลด์รุ่นให้คุณ

หมายเหตุ: ปลั๊กอิน Android Gradle มีไฟล์กฎ ProGuard ที่กำหนดไว้ล่วงหน้าเพิ่มเติม แต่เราขอแนะนำให้ใช้proguard-android-optimize.txt

ทรัพยากร Dependency ของไลบรารี

ในคลัง AAR
proguard.txt

ในไลบรารี JAR
META-INF/proguard/<ProGuard-rules-file>

นอกเหนือจากประเทศเหล่านี้แล้ว ปลั๊กอิน Android Gradle 3.6 ขึ้นไปยังรองรับกฎการย่อเป้าหมายด้วย

หากเผยแพร่ไลบรารี AAR หรือ JAR ด้วยไฟล์กฎของตนเอง และคุณรวมไลบรารีนั้นไว้เป็น Dependency ของเวลาคอมไพล์ R8 จะใช้กฎเหล่านั้นโดยอัตโนมัติเมื่อคอมไพล์โปรเจ็กต์

นอกจากกฎ ProGuard แบบดั้งเดิมแล้ว ปลั๊กอิน Android Gradle เวอร์ชัน 3.6 ขึ้นไปยังรองรับกฎการบีบอัดแบบกำหนดเป้าหมายด้วย กฎเหล่านี้กำหนดเป้าหมายไปยังเครื่องมือบีบอัดที่เฉพาะเจาะจง (R8 หรือ ProGuard) รวมถึงเวอร์ชันเครื่องมือบีบอัดที่เฉพาะเจาะจง

การใช้ไฟล์กฎที่รวมอยู่ในไลบรารีจะมีประโยชน์หากต้องใช้กฎบางอย่างเพื่อให้ไลบรารีทํางานได้อย่างถูกต้อง กล่าวคือ นักพัฒนาไลบรารีได้ดําเนินการตามขั้นตอนการแก้ปัญหาให้คุณแล้ว

อย่างไรก็ตาม โปรดทราบว่าเนื่องจากกฎเป็นการเพิ่ม คุณจึงนำกฎบางอย่างที่ไลบรารี Dependency รวมไว้ออกไม่ได้ และอาจส่งผลต่อการคอมไพล์ส่วนอื่นๆ ของแอป เช่น หากไลบรารีมีกฎในการปิดใช้การเพิ่มประสิทธิภาพโค้ด กฎนั้นจะปิดใช้การเพิ่มประสิทธิภาพสำหรับทั้งโปรเจ็กต์

เครื่องมือแพ็กเกจเนื้อหา Android 2 (AAPT2) หลังจากสร้างโปรเจ็กต์ด้วย minifyEnabled true แล้ว ให้ทำดังนี้ <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt AAPT2 สร้างกฎ Keep โดยอิงตามการอ้างอิงไปยังคลาสในไฟล์ Manifest, เลย์เอาต์ และทรัพยากรอื่นๆ ของแอป ตัวอย่างเช่น AAPT2 มีกฎการเก็บไว้สําหรับกิจกรรมแต่ละรายการที่คุณลงทะเบียนในไฟล์ Manifest ของแอปเป็นจุดแรกเข้า
ไฟล์การกำหนดค่าที่กำหนดเอง โดยค่าเริ่มต้น เมื่อคุณสร้างโมดูลใหม่โดยใช้ Android Studio IDE จะสร้าง <module-dir>/proguard-rules.pro ให้คุณเพื่อเพิ่มกฎของคุณเอง คุณสามารถรวมการกําหนดค่าเพิ่มเติมได้ และ R8 จะใช้การกําหนดค่าเหล่านั้นเมื่อคอมไพล์

เมื่อคุณตั้งค่าพร็อพเพอร์ตี้ minifyEnabled เป็น true R8 จะรวมกฎจากแหล่งที่มาที่มีอยู่ทั้งหมดตามรายการข้างต้น โปรดคำนึงถึงเรื่องนี้เมื่อแก้ปัญหาเกี่ยวกับ R8 เนื่องจากไลบรารีอื่นๆ ที่ใช้ร่วมกันในรันไทม์ เช่น ไลบรารีที่ใช้ร่วมกัน อาจทำให้เกิดการเปลี่ยนแปลงลักษณะการทำงานของ R8 ที่คุณไม่ทราบ

หากต้องการแสดงรายงานฉบับเต็มของกฎทั้งหมดที่ R8 นำไปใช้เมื่อสร้างโปรเจ็กต์ ให้ใส่ข้อมูลต่อไปนี้ในไฟล์ proguard-rules.pro ของโมดูล

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

กฎการย่อเป้าหมาย

ปลั๊กอิน Android Gradle 3.6 ขึ้นไปรองรับกฎของไลบรารีที่กำหนดเป้าหมายไปยัง S{/7}{/7}{/7}ing เวอร์ชันที่เจาะจงโดยเฉพาะ (R8 หรือ ProGuard) วิธีนี้ช่วยให้นักพัฒนาซอฟต์แวร์ไลบรารีสามารถปรับแต่งกฎให้ทำงานได้ดีที่สุดในโปรเจ็กต์ที่ใช้เวอร์ชันย่อขนาดใหม่ ในขณะเดียวกันก็อนุญาตให้ใช้กฎที่มีอยู่ในโปรเจ็กต์ที่มีแบบย่อส่วนต่อไปได้

ในการระบุกฎการย่อเป้าหมาย นักพัฒนาไลบรารีจะต้องรวมกฎดังกล่าวไว้ในตำแหน่งที่เจาะจงภายในไลบรารี AAR หรือ JAR ตามที่อธิบายไว้ด้านล่าง

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

ซึ่งหมายความว่าระบบจะจัดเก็บกฎการบีบอัดที่กําหนดเป้าหมายไว้ในไดเรกทอรี META-INF/com.android.tools ของ JAR หรือในไดเรกทอรี META-INF/com.android.tools ภายในclasses.jar ของ AAR

ในไดเรกทอรีดังกล่าว อาจมีหลายไดเรกทอรีที่มีชื่อในรูปแบบ r8-from-<X>-upto-<Y> หรือ proguard-from-<X>-upto-<Y> เพื่อระบุว่าเวอร์ชันใดที่ลดขนาดกฎภายในไดเรกทอรีที่เขียนขึ้นมา โปรดทราบว่าส่วน -from-<X> และ -upto-<Y> ไม่บังคับ ส่วนเวอร์ชัน <Y> ต้องระบุ และช่วงเวอร์ชันต้องต่อเนื่องกัน

เช่น r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 และ r8-from-8.2.0 ประกอบกันเป็นชุดกฎการลดขนาดที่กำหนดเป้าหมายที่ถูกต้อง R8 จะใช้กฎที่อยู่ในไดเรกทอรี r8-from-8.0.0-upto-8.2.0 ตั้งแต่เวอร์ชัน 8.0.0 ขึ้นไปแต่ไม่รวมเวอร์ชัน 8.2.0

เมื่อทราบข้อมูลดังกล่าวแล้ว ปลั๊กอิน Android Gradle 3.6 ขึ้นไปจะเลือกกฎจากไดเรกทอรี R8 ที่ตรงกัน หากไลบรารีไม่ได้ระบุกฎการบีบอัดเป้าหมาย ปลั๊กอิน Gradle ของ Android จะเลือกกฎจากตำแหน่งเดิม (proguard.txt สำหรับ AAR หรือ META-INF/proguard/<ProGuard-rules-file> สำหรับ JAR)

นักพัฒนาไลบรารีสามารถเลือกที่จะรวมกฎการบีบอัดแบบกำหนดเป้าหมายหรือกฎ ProGuard รุ่นเดิมไว้ในไลบรารี หรือจะรวมทั้ง 2 ประเภทก็ได้หากต้องการคงความเข้ากันได้กับปลั๊กอิน Gradle ของ Android ที่เก่ากว่า 3.6 หรือเครื่องมืออื่นๆ

รวมการกําหนดค่าเพิ่มเติม

เมื่อคุณสร้างโปรเจ็กต์หรือโมดูลใหม่โดยใช้ Android Studio IDE จะสร้างไฟล์ <module-dir>/proguard-rules.pro ให้คุณเพื่อใส่กฎของคุณเอง นอกจากนี้ คุณยังรวมกฎเพิ่มเติมจากไฟล์อื่นๆ ได้โดยเพิ่มลงในพร็อพเพอร์ตี้ proguardFiles ในสคริปต์บิลด์ของโมดูล

ตัวอย่างเช่น คุณอาจเพิ่มกฎที่เฉพาะเจาะจงสำหรับตัวแปรของบิลด์แต่ละรายการได้โดยการเพิ่มพร็อพเพอร์ตี้ proguardFiles อีกรายการในบล็อก productFlavor ที่เกี่ยวข้อง ไฟล์ Gradle ต่อไปนี้จะเพิ่ม flavor2-rules.pro ลงในตัวแปรผลิตภัณฑ์ flavor2 ตอนนี้ flavor2 ใช้กฎ ProGuard ทั้ง 3 ข้อ เนื่องจากมีการใช้กฎจากบล็อก release ด้วย

นอกจากนี้ คุณยังเพิ่มพร็อพเพอร์ตี้ testProguardFiles ซึ่งระบุรายการไฟล์ ProGuard ที่รวมอยู่ใน APK ทดสอบเท่านั้นได้ ดังนี้

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

ย่อโค้ดของคุณ

ระบบจะเปิดใช้การย่อโค้ดด้วย R8 โดยค่าเริ่มต้นเมื่อคุณตั้งค่าพร็อพเพอร์ตี้ minifyEnabled เป็น true

การลดขนาดโค้ด (เรียกอีกอย่างว่า Tree Shaking) คือกระบวนการนําโค้ดที่ R8 พิจารณาว่าไม่จําเป็นในรันไทม์ออก กระบวนการนี้สามารถลดขนาดแอปได้อย่างมาก ตัวอย่างเช่น หากแอปของคุณมีทรัพยากร Dependency ของไลบรารีจำนวนมาก แต่ใช้ฟังก์ชันของไลบรารีเพียงบางส่วน

หากต้องการลดขนาดโค้ดของแอป R8 จะกำหนดจุดแรกเข้าทั้งหมดในโค้ดของแอปโดยอิงตามชุดไฟล์การกําหนดค่าที่รวมกัน จุดเข้าเหล่านี้ประกอบด้วยคลาสทั้งหมดที่แพลตฟอร์ม Android อาจใช้เพื่อเปิดกิจกรรมหรือบริการของแอป โดย R8 จะตรวจสอบโค้ดของแอปจากจุดแรกเข้าแต่ละจุดเพื่อสร้างกราฟของเมธอด ตัวแปรสมาชิก และคลาสอื่นๆ ทั้งหมดที่แอปอาจเข้าถึงขณะรันไทม์ โค้ดที่ไม่ได้เชื่อมต่อกับกราฟนั้นจะถือว่าเข้าถึงไม่ได้และอาจถูกนำออกจากแอป

รูปที่ 1 แสดงแอปที่มีทรัพยากร Dependency ของไลบรารีรันไทม์ ขณะตรวจสอบโค้ดของแอป R8 จะระบุว่าเข้าถึงเมธอด foo(), faz() และ bar() ได้จากจุดแรกเข้า MainActivity.class อย่างไรก็ตาม แอปของคุณจะไม่ใช้คลาส OkayApi.class หรือเมธอด baz() ในระหว่างรันไทม์ และ R8 จะนำโค้ดดังกล่าวออกเมื่อลดขนาดแอป

รูปที่ 1 ขณะคอมไพล์ R8 จะสร้างกราฟตามกฎการเก็บรักษาแบบรวมของโปรเจ็กต์เพื่อระบุโค้ดที่เข้าถึงไม่ได้

R8 จะกำหนดจุดแรกเข้าผ่านกฎ -keep ในไฟล์การกำหนดค่า R8 ของโปรเจ็กต์ กล่าวคือ กฎการเก็บรักษาจะระบุคลาสที่ R8 ไม่ควรทิ้งเมื่อลดขนาดแอป และ R8 จะถือว่าคลาสเหล่านั้นเป็นจุดเข้าใช้งานที่เป็นไปได้ของแอป ปลั๊กอิน Android Gradle และ AAPT2 จะสร้างกฎการเก็บรักษาที่จําเป็นสําหรับโปรเจ็กต์แอปส่วนใหญ่ให้คุณโดยอัตโนมัติ เช่น กิจกรรม มุมมอง และบริการของแอป อย่างไรก็ตาม หากต้องการปรับแต่งลักษณะการทํางานเริ่มต้นนี้ด้วยกฎการเก็บรักษาเพิ่มเติม โปรดอ่านส่วนเกี่ยวกับวิธีปรับแต่งโค้ดที่จะเก็บ

แต่หากสนใจเฉพาะการลดขนาดทรัพยากรของแอป ให้ข้ามไปที่หัวข้อย่อทรัพยากร

โปรดทราบว่าหากมีการลดขนาดโปรเจ็กต์ไลบรารี แอปที่ขึ้นกับไลบรารีนั้นจะมีคลาสไลบรารีที่ลดขนาด คุณอาจต้องปรับกฎ Keep ของไลบรารี หากไม่มีคลาสใน APK ของไลบรารี หากคุณกำลังสร้างและเผยแพร่ไลบรารีในรูปแบบ AAR ไฟล์ JAR ในเครื่องที่ไลบรารีของคุณใช้จะไม่ถูกบีบอัดในไฟล์ AAR

ปรับแต่งรหัสที่จะเก็บไว้

ในกรณีส่วนใหญ่ ไฟล์กฎ ProGuard เริ่มต้น (proguard-android-optimize.txt) ก็เพียงพอแล้วสำหรับ R8 ในการนําเฉพาะโค้ดที่ไม่ได้ใช้ออก อย่างไรก็ตาม ในบางสถานการณ์ R8 อาจวิเคราะห์ได้ไม่ถูกต้องและอาจนำโค้ดที่แอปของคุณต้องใช้ออก ตัวอย่างกรณีที่อาจนำโค้ดออกอย่างไม่ถูกต้อง ได้แก่

  • เมื่อแอปเรียกใช้เมธอดจาก Java Native Interface (JNI)
  • เมื่อแอปค้นหาโค้ดขณะรันไทม์ (เช่น กับรีเฟล็กชัน)

การทดสอบแอปควรแสดงข้อผิดพลาดที่เกิดจากโค้ดที่ถูกนำออกอย่างไม่เหมาะสม แต่คุณยังตรวจสอบโค้ดที่ถูกนำออกได้ด้วยการสร้างรายงานโค้ดที่ถูกนำออก

หากต้องการแก้ไขข้อผิดพลาดและบังคับให้ R8 เก็บโค้ดบางรายการไว้ ให้เพิ่มบรรทัด -keep ในไฟล์กฎ ProGuard เช่น

-keep public class MyClass

หรือจะเพิ่มคำอธิบายประกอบ @Keep ลงในโค้ดที่ต้องการเก็บไว้ก็ได้ การเพิ่ม @Keep ในคลาสจะทำให้คลาสทั้งคลาสเหมือนเดิม ส่วนการเพิ่มในเมธอดหรือฟิลด์จะทำให้เมธอด/ฟิลด์ (และชื่อ) รวมถึงชื่อคลาสเหมือนเดิม โปรดทราบว่าคําอธิบายประกอบนี้ใช้ได้เฉพาะเมื่อใช้คลังคําอธิบายประกอบ AndroidX และเมื่อคุณรวมไฟล์กฎ ProGuard ที่รวมอยู่ในแพ็กเกจกับปลั๊กอิน Android Gradle ตามที่อธิบายไว้ในส่วนเกี่ยวกับวิธีเปิดใช้การลดขนาด

มีข้อควรพิจารณามากมายที่คุณควรพิจารณาเมื่อใช้ตัวเลือก -keep โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับการปรับแต่งไฟล์กฎที่คู่มือ ProGuard ส่วนการแก้ปัญหาจะระบุปัญหาทั่วไปอื่นๆ ที่คุณอาจพบเมื่อระบบนำโค้ดออก

ลบไลบรารีที่มาพร้อมเครื่อง

โดยค่าเริ่มต้น ระบบจะถอดไลบรารีโค้ดเนทีฟในบิลด์รุ่นของแอปออก การถอดประกอบด้วยการนำตารางสัญลักษณ์และข้อมูลการแก้ไขข้อบกพร่องที่อยู่ในไลบรารีเนทีฟที่แอปใช้ออก การถอดไลบรารีโค้ดเนทีฟจะทำให้ไฟล์มีขนาดเล็กลงอย่างมาก แต่คุณจะไม่สามารถวินิจฉัยข้อขัดข้องใน Google Play Console ได้เนื่องจากไม่มีข้อมูล (เช่น ชื่อคลาสและฟังก์ชัน)

การสนับสนุนด้านการขัดข้องของระบบ

Google Play Console รายงานการขัดข้องของระบบใน Android Vitals คุณสามารถสร้างและอัปโหลดไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบสำหรับแอปได้ง่ายๆ ในไม่กี่ขั้นตอน ไฟล์นี้เปิดใช้สแต็กเทรซข้อขัดข้องของระบบที่แทนที่ด้วยสัญลักษณ์ (ซึ่งรวมถึงชื่อคลาสและฟังก์ชัน) ใน Android Vitals เพื่อช่วยแก้ไขข้อบกพร่องของแอปในเวอร์ชันที่ใช้งานจริง ขั้นตอนเหล่านี้จะแตกต่างกันไปตามเวอร์ชันของปลั๊กอิน Android Gradle ที่ใช้กับโปรเจ็กต์และเอาต์พุตการสร้างของโปรเจ็กต์

ปลั๊กอิน Android Gradle เวอร์ชัน 4.1 ขึ้นไป

หากเป็นโปรเจ็กต์สร้าง Android App Bundle คุณจะรวมไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องในแอปที่มาพร้อมเครื่องไว้ในนั้นได้โดยอัตโนมัติ หากต้องการรวมไฟล์นี้ไว้ในบิลด์รุ่น ให้เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ build.gradle.kts ของแอป

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

เลือกระดับสัญลักษณ์การแก้ไขข้อบกพร่องจากรายการต่อไปนี้

  • ใช้ SYMBOL_TABLE เพื่อดูชื่อฟังก์ชันในสแต็กเทรซที่แทนที่ด้วยสัญลักษณ์ของ Play Console ระดับนี้รองรับรายการที่ลบไปแล้ว
  • ใช้ FULL เพื่อดูชื่อฟังก์ชัน ไฟล์ และหมายเลขบรรทัดในสแต็กเทรซที่มีสัญลักษณ์ของ Play Console

หากโปรเจ็กต์สร้าง APK ให้ใช้การตั้งค่าบิลด์ build.gradle.kts ที่แสดงก่อนหน้านี้เพื่อสร้างไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบแยกต่างหาก อัปโหลดไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบไปยัง Google Play Console ด้วยตนเอง เนื่องจากเป็นส่วนหนึ่งของกระบวนการบิลด์ ปลั๊กอิน Android Gradle จะเอาต์พุตไฟล์นี้ในตำแหน่งของโปรเจ็กต์ดังต่อไปนี้

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

ปลั๊กอิน Android Gradle เวอร์ชัน 4.0 ลงไป (และระบบบิลด์อื่นๆ)

ในกระบวนการบิลด์ ปลั๊กอิน Android Gradle จะเก็บสำเนาของไลบรารีที่ยังไม่ได้ Strip เอาไว้ในไดเรกทอรีของโปรเจ็กต์ โครงสร้างไดเรกทอรีนี้คล้ายกับตัวอย่างต่อไปนี้

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. บีบอัดเนื้อหาของไดเรกทอรีนี้

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. อัปโหลดไฟล์ symbols.zip ไปยัง Google Play Console ด้วยตนเอง

ย่อขนาดทรัพยากร

การลดขนาดทรัพยากรใช้ได้กับการทำงานร่วมกับการลดขนาดโค้ดเท่านั้น หลังจากที่ตัวย่อโค้ดนำโค้ดทั้งหมดที่ไม่ได้ใช้ออก ตัวย่อทรัพยากรจะระบุทรัพยากรที่แอปยังใช้อยู่ได้ กรณีนี้เกิดขึ้นเมื่อคุณเพิ่มไลบรารีโค้ดที่มีทรัพยากร โดยคุณต้องนำโค้ดไลบรารีที่ไม่ได้ใช้ออกเพื่อให้ระบบยกเลิกการอ้างอิงทรัพยากรของไลบรารี ซึ่งจะทำให้เครื่องมือบีบอัดทรัพยากรนำทรัพยากรดังกล่าวออกได้

หากต้องการเปิดใช้การลดขนาดทรัพยากร ให้ตั้งค่าพร็อพเพอร์ตี้ shrinkResources เป็น true ในสคริปต์บิลด์ (ควบคู่ไปกับ minifyEnabled สำหรับการลดขนาดโค้ด) เช่น

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

หากยังไม่ได้สร้างแอปโดยใช้ minifyEnabled สำหรับการลดขนาดโค้ด ให้ลองใช้ก่อนเปิดใช้ shrinkResources เนื่องจากคุณอาจต้องแก้ไขไฟล์ proguard-rules.pro เพื่อเก็บคลาสหรือเมธอดที่สร้างหรือเรียกใช้แบบไดนามิกไว้ก่อนเริ่มนำทรัพยากรออก

ปรับแต่งทรัพยากรที่จะเก็บไว้

หากมีทรัพยากรที่เฉพาะเจาะจงที่ต้องการเก็บหรือทิ้ง ให้สร้างไฟล์ XML ในโปรเจ็กต์โดยใช้แท็ก <resources> แล้วระบุทรัพยากรแต่ละรายการที่จะเก็บไว้ในแอตทริบิวต์ tools:keep และทรัพยากรแต่ละรายการที่จะทิ้งไว้ในแอตทริบิวต์ tools:discard แอตทริบิวต์ทั้ง 2 รายการยอมรับรายการชื่อทรัพยากรที่คั่นด้วยคอมมา คุณใช้เครื่องหมายดอกจันเป็นไวลด์การ์ดได้

เช่น

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

บันทึกไฟล์นี้ในทรัพยากรของโปรเจ็กต์ เช่น ที่ res/raw/my.package.keep.xml บิลด์จะไม่รวมไฟล์นี้ไว้ในแอป

หมายเหตุ: ตรวจสอบว่าคุณใช้ชื่อที่ไม่ซ้ำกันสำหรับไฟล์ keep เมื่อมีการลิงก์คลังต่างๆ เข้าด้วยกัน กฎการเก็บรักษาของคลังจะขัดแย้งกัน ซึ่งอาจทำให้เกิดปัญหาเกี่ยวกับกฎที่ถูกละเว้นหรือทรัพยากรที่เก็บไว้โดยไม่จำเป็น

การระบุทรัพยากรที่จะทิ้งอาจดูไร้สาระเมื่อคุณลบทรัพยากรเหล่านั้นได้ แต่วิธีนี้มีประโยชน์เมื่อใช้ตัวแปรของบิลด์ ตัวอย่างเช่น คุณอาจใส่ทรัพยากรทั้งหมดไว้ในไดเรกทอรีโปรเจ็กต์ทั่วไป จากนั้นสร้างไฟล์ my.package.build.variant.keep.xml ที่แตกต่างกันสำหรับตัวแปรการสร้างแต่ละรายการเมื่อทราบว่ามีการใช้ทรัพยากรหนึ่งๆ ในโค้ด (และเครื่องมือบีบอัดจึงไม่นำออก) แต่คุณทราบว่าจะไม่ได้ใช้ทรัพยากรนั้นกับตัวแปรการสร้างหนึ่งๆ นอกจากนี้ เครื่องมือสร้างอาจระบุทรัพยากรไม่ถูกต้องตามที่ต้องการ ซึ่งเป็นไปได้เนื่องจากคอมไพเลอร์จะเพิ่มรหัสทรัพยากรในบรรทัดนั้นๆ และเครื่องมือวิเคราะห์ทรัพยากรอาจไม่ทราบว่าความแตกต่างระหว่างทรัพยากรที่อ้างอิงอย่างแท้จริงกับค่าจำนวนเต็มในโค้ดที่บังเอิญมีค่าเดียวกัน

เปิดใช้การตรวจสอบการอ้างอิงอย่างเข้มงวด

โดยปกติแล้ว ตัวลดขนาดทรัพยากรจะระบุได้อย่างแม่นยำว่าทรัพยากรมีการใช้งานหรือไม่ อย่างไรก็ตาม หากโค้ดของคุณเรียกใช้ Resources.getIdentifier() (หรือหากไลบรารีของคุณทำแบบนั้น ไลบรารี AppCompat ก็มี) แสดงว่าโค้ดจะค้นหาชื่อทรัพยากรตามสตริงที่สร้างขึ้นแบบไดนามิก เมื่อดำเนินการดังกล่าว ตัวลดขนาดทรัพยากรจะทำงานแบบป้องกันโดยค่าเริ่มต้น และทําเครื่องหมายทรัพยากรทั้งหมดที่มีรูปแบบชื่อตรงกันว่ามีการใช้งานและนำออกไม่ได้

ตัวอย่างเช่น โค้ดต่อไปนี้จะทำให้ระบบทำเครื่องหมายทรัพยากรทั้งหมดที่มีคำนำหน้า img_ ว่าใช้แล้ว

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

ตัวย่อทรัพยากรยังตรวจสอบค่าคงที่สตริงทั้งหมดในโค้ดของคุณ รวมถึงทรัพยากร res/raw/ ต่างๆ ด้วย เพื่อค้นหา URL ทรัพยากรในรูปแบบที่คล้ายกับ file:///android_res/drawable//ic_plus_anim_016.png หากพบสตริงเช่นนี้หรือสตริงอื่นๆ ที่ดูเหมือนว่าอาจใช้สร้าง URL เช่นนี้ ระบบจะไม่นำสตริงเหล่านั้นออก

ตัวอย่างโหมดการลดขนาดอย่างปลอดภัยที่เปิดใช้โดยค่าเริ่มต้นมีดังนี้ อย่างไรก็ตาม คุณสามารถปิดการจัดการที่ "ปลอดภัยดีกว่าขอโทษ" นี้ แล้วระบุว่าเครื่องมือย่อทรัพยากรเก็บเฉพาะทรัพยากรที่จำเป็นต้องใช้เท่านั้น โดยให้ตั้งค่า shrinkMode เป็น strict ในไฟล์ keep.xml ดังนี้

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

หากคุณเปิดใช้โหมดการบีบอัดแบบเข้มงวดและโค้ดของคุณยังอ้างอิงทรัพยากรด้วยสตริงที่สร้างขึ้นแบบไดนามิกด้วย ดังที่แสดงด้านบน คุณต้องเก็บทรัพยากรเหล่านั้นด้วยตนเองโดยใช้แอตทริบิวต์ tools:keep

นำทรัพยากรทางเลือกที่ไม่ได้ใช้ออก

ตัวลดขนาดทรัพยากร Gradle จะนําเฉพาะทรัพยากรที่ไม่ได้อ้างอิงโดยโค้ดแอปออก ซึ่งหมายความว่าจะไม่นํา ทรัพยากรทางเลือกสําหรับการกําหนดค่าอุปกรณ์ที่แตกต่างกันออก หากจำเป็น คุณสามารถใช้พร็อพเพอร์ตี้ resConfigs ของปลั๊กอิน Android Gradle เพื่อนำไฟล์ทรัพยากรทางเลือกที่แอปไม่จำเป็นต้องใช้ออก

ตัวอย่างเช่น หากคุณใช้ไลบรารีที่มีทรัพยากรภาษา (เช่น AppCompat หรือ Google Play Services) แอปของคุณจะมีสตริงภาษาที่แปลแล้วทั้งหมดสำหรับข้อความในไลบรารีเหล่านั้น ไม่ว่าส่วนที่เหลือของแอปจะแปลเป็นภาษาเดียวกันหรือไม่ก็ตาม หากต้องการเก็บเฉพาะภาษาที่แอปรองรับอย่างเป็นทางการไว้ คุณสามารถระบุภาษาเหล่านั้นได้โดยใช้พร็อพเพอร์ตี้ resConfig ระบบจะนำทรัพยากรสำหรับภาษาที่ไม่ได้ระบุออก

ข้อมูลโค้ดต่อไปนี้แสดงวิธีจำกัดทรัพยากรภาษาให้เหลือเพียงภาษาอังกฤษและฝรั่งเศส

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Groovy

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

เมื่อเผยแพร่แอปโดยใช้รูปแบบ App Bundle ของ Android ระบบจะดาวน์โหลดเฉพาะภาษาที่กําหนดค่าไว้ในอุปกรณ์ของผู้ใช้เมื่อติดตั้งแอป โดยค่าเริ่มต้น ในทํานองเดียวกัน ระบบจะรวมเฉพาะทรัพยากรที่ตรงกับความหนาแน่นของหน้าจอของอุปกรณ์ และไลบรารีแบบเนทีฟที่ตรงกับ ABI ของอุปกรณ์ไว้ในการดาวน์โหลด ดูข้อมูลเพิ่มเติมได้ที่การกำหนดค่า Android App Bundle

สำหรับแอปเดิมที่เผยแพร่ด้วย APK (สร้างขึ้นก่อนเดือนสิงหาคม 2021) คุณสามารถปรับแต่งความละเอียดของหน้าจอหรือทรัพยากร ABI ที่จะรวมไว้ใน APK ได้โดยสร้าง APK หลายรายการที่แต่ละรายการกำหนดเป้าหมายเป็นการกำหนดค่าอุปกรณ์ที่แตกต่างกัน

ผสานทรัพยากรที่ซ้ำกัน

โดยค่าเริ่มต้น Gradle จะผสานทรัพยากรที่มีชื่อเหมือนกันด้วย เช่น ไฟล์ Drawable ที่มีชื่อเดียวกันซึ่งอาจอยู่ในโฟลเดอร์ทรัพยากรที่แตกต่างกัน ลักษณะการทํางานนี้ไม่ได้ควบคุมโดยพร็อพเพอร์ตี้ shrinkResources และปิดใช้ไม่ได้ เนื่องจากจําเป็นต่อการป้องกันข้อผิดพลาดเมื่อแหล่งข้อมูลหลายรายการตรงกับชื่อที่โค้ดของคุณค้นหา

การรวมทรัพยากรจะเกิดขึ้นก็ต่อเมื่อไฟล์อย่างน้อย 2 ไฟล์มีชื่อ ประเภท และตัวระบุทรัพยากรเหมือนกันเท่านั้น Gradle จะเลือกไฟล์ที่พิจารณาว่าดีที่สุดจากไฟล์ที่ซ้ำกัน (ตามลําดับความสําคัญที่อธิบายไว้ด้านล่าง) และส่งทรัพยากรเพียงไฟล์เดียวนั้นไปยัง AAPT เพื่อเผยแพร่ในแอตทริบิวต์สุดท้าย

Gradle จะค้นหาทรัพยากรที่ซ้ำกันในส่วนต่อไปนี้

  • โดยทั่วไปแล้ว ทรัพยากรหลักที่เชื่อมโยงกับชุดแหล่งที่มาหลักจะอยู่ใน src/main/res/
  • การวางซ้อนตัวแปรจากประเภทบิลด์และตัวแปรของบิลด์
  • ทรัพยากร Dependency ของโปรเจ็กต์ห้องสมุด

Gradle รวมทรัพยากรที่ซ้ำกันตามลำดับความสำคัญแบบต่อเรียงต่อไปนี้

Dependency → Main → Build flavor → Build type

ตัวอย่างเช่น หากทรัพยากรที่ซ้ำกันปรากฏทั้งในทรัพยากรหลักและรูปแบบการสร้าง Gradle จะเลือกทรัพยากรในรูปแบบการสร้าง

หากมีทรัพยากรที่เหมือนกันปรากฏในชุดแหล่งที่มาเดียวกัน Gradle จะผสานไม่ได้และแสดงข้อผิดพลาดการผสานทรัพยากร กรณีนี้อาจเกิดขึ้นได้หากคุณกำหนดชุดแหล่งที่มาหลายชุดในพร็อพเพอร์ตี้ sourceSet ของไฟล์ build.gradle.kts เช่น หากทั้ง src/main/res/ และ src/main/res2/ มีทรัพยากรที่เหมือนกัน

สร้างความสับสนให้กับโค้ด

วัตถุประสงค์ของการปรับให้ยากต่อการอ่าน (Obfuscation) คือเพื่อลดขนาดแอปโดยการทำให้ชื่อคลาส เมธอด และช่องของแอปสั้นลง ต่อไปนี้เป็นตัวอย่างการสร้างความสับสนโดยใช้ R8

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

แม้ว่าการปรับให้ยากต่อการอ่านจะไม่นำโค้ดออกจากแอป แต่แอปที่มีไฟล์ DEX ซึ่งจัดทำดัชนีคลาส เมธอด และช่องจำนวนมากจะประหยัดพื้นที่ได้อย่างมาก อย่างไรก็ตาม เนื่องจากการสร้างความสับสนจะเปลี่ยนชื่อส่วนต่างๆ ของโค้ด งานบางอย่าง เช่น การตรวจสอบสแต็กเทรซ จึงต้องใช้เครื่องมือเพิ่มเติม หากต้องการทําความเข้าใจสแต็กเทรซหลังจากการสร้างความสับสน ให้อ่านส่วนเกี่ยวกับวิธีถอดรหัสสแต็กเทรซที่ปรับให้ยากต่อการอ่าน (Obfuscate)

นอกจากนี้ หากโค้ดของคุณใช้การตั้งชื่อที่คาดเดาได้สำหรับเมธอดและคลาสของแอป เช่น เมื่อใช้การสะท้อนกลับ คุณควรถือว่าลายเซ็นเหล่านั้นเป็นจุดแรกเข้าและระบุกฎการเก็บไว้สำหรับลายเซ็นเหล่านั้น ตามที่อธิบายไว้ในส่วนเกี่ยวกับวิธีปรับแต่งโค้ดที่จะเก็บไว้ กฎ keep เหล่านี้บอก R8 ไม่เพียงให้เก็บโค้ดนั้นไว้ใน DEX สุดท้ายของแอปเท่านั้น แต่ยังให้เก็บชื่อเดิมของโค้ดด้วย

ถอดรหัสสแต็กเทรซที่ปรับให้ยากต่อการอ่าน (Obfuscate)

หลังจาก R8 ทำให้โค้ดของคุณยากต่อการอ่าน (Obfuscate) แล้ว การทำความเข้าใจสแต็กเทรซจะทําได้ยาก (หากทำไม่ได้) เนื่องจากอาจมีการเปลี่ยนแปลงชื่อของคลาสและเมธอด หากต้องการดูสแต็กเทรซเดิม คุณควรติดตามสแต็กเทรซอีกครั้ง

การเพิ่มประสิทธิภาพโค้ด

ในการเพิ่มประสิทธิภาพแอปให้ดียิ่งขึ้น R8 จะตรวจสอบโค้ดในระดับที่ลึกขึ้นเพื่อนําโค้ดที่ไม่ได้ใช้ออก หรือเขียนโค้ดใหม่ให้มีรายละเอียดน้อยลงหากเป็นไปได้ ตัวอย่างการเพิ่มประสิทธิภาพดังกล่าวมีดังนี้

  • หากรหัสไม่ได้ใช้ Branch ของ else {} สําหรับคำสั่ง if/else ที่ระบุ R8 อาจนำรหัสของ Branch ของ else {} ออก
  • หากโค้ดเรียกใช้เมธอดในบางตำแหน่งเท่านั้น R8 อาจนําเมธอดออกและฝังไว้ในตำแหน่งการเรียกใช้ไม่กี่แห่ง
  • หาก R8 พิจารณาว่าคลาสมีคลาสย่อยที่ไม่ซ้ำกันเพียงคลาสเดียว และคลาสนั้นไม่ได้สร้างอินสแตนซ์ (เช่น คลาสฐานนามธรรมที่ใช้โดยคลาสการใช้งานที่เฉพาะเจาะจงเพียงคลาสเดียว) R8 จะรวมคลาส 2 คลาสเข้าด้วยกันและนำคลาสออกจากแอปได้
  • หากต้องการดูข้อมูลเพิ่มเติม โปรดอ่านบล็อกโพสต์การเพิ่มประสิทธิภาพ R8 โดย Jake Wharton

R8 ไม่อนุญาตให้คุณปิดหรือเปิดใช้การเพิ่มประสิทธิภาพแบบแยกต่างหาก หรือแก้ไขลักษณะการทํางานของการเพิ่มประสิทธิภาพ อันที่จริง R8 ไม่สนใจกฎ ProGuard ที่พยายามแก้ไขการเพิ่มประสิทธิภาพเริ่มต้น เช่น -optimizations และ -optimizationpasses ข้อจำกัดนี้สำคัญเนื่องจากเมื่อ R8 ได้รับการปรับปรุงอย่างต่อเนื่อง การรักษาลักษณะการทำงานมาตรฐานสำหรับการเพิ่มประสิทธิภาพจะช่วยให้ทีม Android Studio แก้ปัญหาที่คุณอาจพบได้อย่างง่ายดาย

โปรดทราบว่าการเปิดใช้การเพิ่มประสิทธิภาพจะเปลี่ยนสแต็กเทรซสําหรับแอปพลิเคชัน เช่น การฝังโค้ดจะนําเฟรมสแต็กออก ดูส่วนการติดตามซ้ำเพื่อดูวิธีรับสแต็กเทรซเดิม

ผลกระทบต่อประสิทธิภาพของรันไทม์

หากเปิดใช้การบีบอัด การสร้างความสับสน และการเพิ่มประสิทธิภาพทั้งหมด R8 จะปรับปรุงประสิทธิภาพรันไทม์ของโค้ด (รวมถึงเวลาเริ่มต้นและเวลาเฟรมในเธรด UI) สูงสุด 30% การปิดใช้รายการใดรายการหนึ่งเหล่านี้จะจำกัดชุดการเพิ่มประสิทธิภาพที่ R8 ใช้อย่างมาก

หากเปิดใช้ R8 คุณควรสร้างโปรไฟล์การเริ่มต้นด้วยเพื่อให้ประสิทธิภาพการเริ่มต้นดีขึ้น

เปิดใช้การเพิ่มประสิทธิภาพที่ได้รับการปรับปรุง

R8 มีชุดการเพิ่มประสิทธิภาพเพิ่มเติม (เรียกว่า "โหมดเต็มรูปแบบ") ซึ่งทําให้ทํางานแตกต่างจาก ProGuard ระบบจะเปิดใช้การเพิ่มประสิทธิภาพเหล่านี้โดยค่าเริ่มต้นตั้งแต่ปลั๊กอิน Android Gradle เวอร์ชัน 8.0.0

คุณสามารถปิดใช้การเพิ่มประสิทธิภาพเพิ่มเติมเหล่านี้ได้โดยใส่ข้อมูลต่อไปนี้ในไฟล์ gradle.properties ของโปรเจ็กต์

android.enableR8.fullMode=false

เนื่องจากการเพิ่มประสิทธิภาพเพิ่มเติมทำให้ R8 ทำงานแตกต่างจาก ProGuard คุณจึงอาจต้องใส่กฎ ProGuard เพิ่มเติมเพื่อหลีกเลี่ยงปัญหารันไทม์หากคุณใช้กฎที่ออกแบบมาสำหรับ ProGuard ตัวอย่างเช่น สมมติว่าโค้ดของคุณอ้างอิงคลาสผ่าน Java Reflection API เมื่อไม่ได้ใช้ "โหมดเต็ม" R8 จะถือว่าคุณตั้งใจที่จะตรวจสอบและจัดการออบเจ็กต์ของคลาสนั้นๆ ขณะรันไทม์ แม้ว่าโค้ดของคุณจะไม่ทำเช่นนั้นก็ตาม และจะเก็บคลาสและตัวเริ่มต้นแบบคงที่ไว้โดยอัตโนมัติ

อย่างไรก็ตาม เมื่อใช้ "โหมดเต็มรูปแบบ" R8 จะไม่ทำสมมติฐานนี้ และหาก R8 ยืนยันว่าโค้ดของคุณไม่เคยใช้คลาสดังกล่าวที่รันไทม์ ก็จะนำคลาสออกจาก DEX สุดท้ายของแอป กล่าวคือ หากต้องการเก็บคลาสและเครื่องมือเริ่มต้นแบบคงที่ คุณต้องใส่กฎ Keep ในไฟล์กฎเพื่อดำเนินการดังกล่าว

หากพบปัญหาขณะใช้ "โหมดเต็ม" ของ R8 โปรดดูหน้าคำถามที่พบบ่อยของ R8 เพื่อดูวิธีแก้ปัญหาที่เป็นไปได้ หากแก้ปัญหาไม่ได้ โปรดรายงานข้อบกพร่อง

การติดตามสแต็กเทรซอีกครั้ง

โค้ดที่ R8 ประมวลผลจะเปลี่ยนแปลงไปในหลายลักษณะ ซึ่งอาจทําให้สแต็กเทรซเข้าใจได้ยากขึ้นเนื่องจากสแต็กเทรซจะไม่ตรงกับซอร์สโค้ดอย่างสมบูรณ์ กรณีนี้อาจเกิดขึ้นเมื่อมีการแก้ไขหมายเลขบรรทัดเมื่อไม่ได้เก็บข้อมูลการแก้ไขข้อบกพร่องไว้ ซึ่งอาจเกิดจากการเพิ่มประสิทธิภาพ เช่น การแทรกบรรทัดและการสรุป ปัจจัยที่ส่งผลมากที่สุดคือการปรับแต่งโค้ด ซึ่งแม้แต่คลาสและเมธอดก็จะมีการเปลี่ยนแปลงชื่อ

ในการกู้คืนสแต็กเทรซเดิม R8 จะมีเครื่องมือบรรทัดคำสั่ง retrace ซึ่งมาพร้อมกับแพ็กเกจเครื่องมือบรรทัดคำสั่ง

หากต้องการรองรับการติดตามสแต็กเทรซของแอปพลิเคชันอีกครั้ง คุณควรตรวจสอบว่าเวอร์ชันมีข้อมูลที่เพียงพอสำหรับการติดตามอีกครั้งโดยเพิ่มกฎต่อไปนี้ลงในไฟล์ proguard-rules.pro ของโมดูล

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

แอตทริบิวต์ LineNumberTable จะเก็บข้อมูลตำแหน่งในเมธอดเพื่อให้ระบบพิมพ์ตำแหน่งเหล่านั้นในสแต็กเทรซ แอตทริบิวต์ SourceFile ช่วยให้มั่นใจได้ว่ารันไทม์ที่เป็นไปได้ทั้งหมดจะพิมพ์ข้อมูลตำแหน่งจริง คำสั่ง -renamesourcefileattribute จะตั้งค่าชื่อไฟล์ต้นฉบับในการติดตามกองซ้อนเป็น SourceFile เท่านั้น คุณไม่จำเป็นต้องใช้ชื่อไฟล์ต้นฉบับจริงเมื่อทำการติดตามอีกครั้ง เนื่องจากไฟล์การแมปมีไฟล์ต้นฉบับอยู่

R8 สร้างไฟล์ mapping.txt ทุกครั้งที่เรียกใช้ ซึ่งมีข้อมูลที่จำเป็นต่อการแมปสแต็กเทรซย้อนกลับไปยังสแต็กเทรซดั้งเดิม Android Studio จะบันทึกไฟล์ไว้ในไดเรกทอรี <module-name>/build/outputs/mapping/<build-type>/

เมื่อเผยแพร่แอปใน Google Play คุณสามารถอัปโหลดไฟล์ mapping.txt ให้กับแอปแต่ละเวอร์ชันได้ เมื่อเผยแพร่โดยใช้ Android App Bundle ระบบจะรวมไฟล์นี้ไว้ในเนื้อหา App Bundle โดยอัตโนมัติ จากนั้น Google Play จะย้อนรอยสแต็กเทรซขาเข้าจากปัญหาที่ผู้ใช้รายงานเพื่อให้คุณตรวจสอบได้ใน Play Console ดูข้อมูลเพิ่มเติมได้ที่บทความในศูนย์ช่วยเหลือเกี่ยวกับวิธีถอดรหัสซอร์สโค้ดที่สร้างความสับสน

แก้ปัญหาด้วย R8

ส่วนนี้จะอธิบายกลยุทธ์บางอย่างในการแก้ปัญหาเมื่อเปิดใช้การย่อ การสร้างความสับสน และการเพิ่มประสิทธิภาพโดยใช้ R8 หากไม่พบวิธีแก้ปัญหาด้านล่าง โปรดอ่านหน้าคำถามที่พบบ่อยของ R8 และคู่มือการแก้ปัญหาของ ProGuard

สร้างรายงานเกี่ยวกับโค้ดที่ถูกนำออก (หรือเก็บไว้)

ในการแก้ปัญหาบางอย่างของ R8 คุณอาจดูรายงานเกี่ยวกับโค้ดทั้งหมดที่ R8 นำออกจากแอปได้ โดยให้เพิ่ม -printusage <output-dir>/usage.txt ลงในไฟล์กฎที่กำหนดเองสำหรับแต่ละโมดูลที่ต้องการสร้างรายงานนี้ เมื่อคุณเปิดใช้ R8 และสร้างแอป R8 จะแสดงผลรายงานที่มีเส้นทางและชื่อไฟล์ที่คุณระบุ รายงานโค้ดที่นำออกจะมีลักษณะคล้ายกับด้านล่างนี้

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

หากต้องการดูรายงานจุดแรกเข้าที่ R8 กำหนดจากกฎ Keep ของโปรเจ็กต์ ให้ใส่ -printseeds <output-dir>/seeds.txt ในไฟล์กฎที่กำหนดเอง เมื่อคุณเปิดใช้ R8 และสร้างแอปแล้ว R8 จะแสดงผลรายงานที่มีเส้นทางและชื่อไฟล์ที่คุณระบุ รายงานของจุดเข้าชมที่เก็บไว้จะมีลักษณะคล้ายกับตัวอย่างต่อไปนี้

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

แก้ปัญหาการลดขนาดทรัพยากร

เมื่อคุณลดขนาดทรัพยากร หน้าต่างสร้าง จะแสดงสรุปของทรัพยากรที่ถูกนำออกจากแอป (คุณต้องคลิกสลับมุมมอง ทางด้านซ้ายของหน้าต่างก่อนเพื่อแสดงเอาต์พุตข้อความแบบละเอียดจาก Gradle) เช่น

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

นอกจากนี้ Gradle จะสร้างไฟล์การวินิจฉัยชื่อ resources.txt ใน <module-name>/build/outputs/mapping/release/ (โฟลเดอร์เดียวกับไฟล์เอาต์พุตของ ProGuard) ไฟล์นี้ประกอบด้วยรายละเอียดต่างๆ เช่น ทรัพยากรใดอ้างอิงทรัพยากรอื่น และทรัพยากรใดที่ใช้หรือนำออก

ตัวอย่างเช่น หากต้องการดูสาเหตุที่ @drawable/ic_plus_anim_016 ยังคงอยู่ในแอป ให้เปิดไฟล์ resources.txt แล้วค้นหาชื่อไฟล์นั้น คุณอาจพบว่ามีการอ้างอิงจากแหล่งข้อมูลอื่น ดังนี้

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

ตอนนี้คุณต้องทราบว่าเหตุใด @drawable/add_schedule_fab_icon_anim จึงเข้าถึงได้ และหากค้นหาขึ้นด้านบน คุณจะเห็นทรัพยากรนั้นแสดงอยู่ใต้ "ทรัพยากรที่เข้าถึงได้ของรูทมีดังนี้" ซึ่งหมายความว่ามีโค้ดอ้างอิงถึง add_schedule_fab_icon_anim (นั่นคือพบรหัส R.drawable ของ add_schedule_fab_icon_anim ในโค้ดที่เข้าถึงได้)

หากคุณไม่ได้ใช้การตรวจสอบแบบเข้มงวด ระบบจะทำเครื่องหมายรหัสทรัพยากรว่าเข้าถึงได้หากมีสตริงคงที่ที่ดูเหมือนว่าอาจใช้สร้างชื่อทรัพยากรสําหรับทรัพยากรที่โหลดแบบไดนามิก ในกรณีนี้ หากคุณค้นหาชื่อทรัพยากรในเอาต์พุตการสร้าง คุณอาจเห็นข้อความดังต่อไปนี้

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

หากเห็นสตริงใดสตริงหนึ่งเหล่านี้และมั่นใจว่าไม่ได้ใช้สตริงดังกล่าวเพื่อโหลดทรัพยากรที่ระบุแบบไดนามิก คุณสามารถใช้แอตทริบิวต์ tools:discard เพื่อแจ้งให้ระบบบิลด์นำสตริงนั้นออก ตามที่อธิบายไว้ในส่วนเกี่ยวกับวิธีปรับแต่งทรัพยากรที่จะเก็บไว้