เพิ่มประสิทธิภาพโค้ดและทรัพยากรของแพลตฟอร์มด้วย R8

ระบบบิลด์ของแพลตฟอร์ม Android (Soong) จะเรียกใช้คอมไพเลอร์ R8 ในเป้าหมายที่คอมไพล์ไบต์โค้ด DEX ซึ่งรวมถึงแอป Android (android_app) การทดสอบ (android_test) และไลบรารี Java ที่ติดตั้งได้ซึ่งมี compile_dex: true (เช่น services.jar) เพื่อลดขนาด เพิ่มประสิทธิภาพ และลบโค้ดและทรัพยากรที่ไม่ได้ใช้ ในไลบรารีแบบคงที่ (android_library และ java_library แบบคงที่) Soong จะไม่เรียกใช้ R8 โดยตรง แต่จะใช้บล็อกพร็อพเพอร์ตี้ optimize เพื่อแนบและเผยแพร่กฎการเก็บรักษาสำหรับผู้ใช้ (export_proguard_flags_files: true) ไปยังเป้าหมาย DEX ที่ดาวน์สตรีมซึ่งลิงก์แบบคงที่

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

  • ร่องรอยพาร์ติชันของระบบที่เล็กลง: R8 จะนำคลาส เมธอด และทรัพยากรที่ไม่ได้ใช้แล้วออกก่อนที่จะแพ็กเกจ APK และ JAR ลงใน /system, /system_ext, /product และ /vendor
  • อาร์ติแฟกต์ที่คอมไพล์แล้วมีขนาดเล็กลง: เมธอด DEX น้อยลงหมายถึงไฟล์ .odex และ .vdex ที่ dex2oat สร้างขึ้นมีขนาดเล็กลงในระหว่างการคอมไพล์ขณะสร้างหรือในอุปกรณ์
  • ลดแรงกดดันด้านหน่วยความจำรันไทม์: เนื่องจาก Android จะแมป .odex, .vdex และตารางทรัพยากร APK ลงในหน่วยความจำของกระบวนการโดยใช้การเรียก mmap แบบดีมานด์เพจ ไบนารีขนาดเล็กจึงช่วยลดข้อผิดพลาดของหน้าเว็บที่สำคัญในระหว่างการเริ่มต้นแอป และลด โค้ดและหน่วยความจำของทรัพยากรที่อยู่ในกระบวนการต่างๆ ดูข้อมูลเพิ่มเติม เกี่ยวกับวิธีที่โค้ดแอปที่คอมไพล์แล้วส่งผลต่อหน่วยความจำของอุปกรณ์ได้ที่ โค้ดแอปคือหน่วยความจำ
  • การเพิ่มประสิทธิภาพทั้งโปรแกรมที่มีประสิทธิภาพมากขึ้น: ข้อจำกัดในการเก็บที่แคบลงช่วยให้ R8 สามารถแทรกเมธอด อินเทอร์เฟซแบบดีเวอร์ชวลไลซ์ และการเรียกแบบเวอร์ชวล ตัดฟิลด์ที่ไม่ได้ใช้ ออก และเผยแพร่ค่าคงที่ในคลาสต่างๆ

กำหนดค่าการเพิ่มประสิทธิภาพ R8 ใน Soong

ในไฟล์ Android.bp ให้กำหนดค่าการเพิ่มประสิทธิภาพ R8 โดยใช้พร็อพเพอร์ตี้ optimize บล็อกในเป้าหมาย android_app และเป้าหมาย java_library ที่ติดตั้งได้ (หรือใน โมดูล android_library และโมดูลแบบคงที่ java_library เพื่อส่งออกกฎการเก็บรักษาสำหรับผู้ใช้ทั่วไป):

android_app {
    name: "MySystemApp",
    srcs: ["src/**/*.java"],
    optimize: {
        obfuscate: true,
        shrink_resources: true,
    },
}

ตารางต่อไปนี้สรุปoptimizeพร็อพเพอร์ตี้ที่พบบ่อยที่สุดใน Soong (กำหนดไว้ใน build/soong/java/dex.go)

พร็อพเพอร์ตี้ คำอธิบาย
enabled ควบคุมว่าจะเรียกใช้ R8 ในเป้าหมายหรือไม่ ค่าเริ่มต้นคือ true สําหรับเป้าหมาย android_app ทั้งหมด
shrink ควบคุมการกำจัดโค้ดที่ไม่จำเป็นเพื่อนำคลาส ฟิลด์ และ เมธอดที่เข้าถึงไม่ได้ออก ค่าเริ่มต้นจะเป็น true สำหรับเป้าหมาย android_app (false สำหรับ java_library แบบสแตนด์อโลนและ โมดูลทดสอบ ซึ่งส่งผ่าน -dontshrink เว้นแต่จะตั้งค่าอย่างชัดแจ้ง)
optimize ควบคุมการเพิ่มประสิทธิภาพไบต์โค้ด เช่น การแทรกเมธอด การผสานคลาส การส่งต่อค่าคงที่ และการนำกิ่งที่ตายแล้วออก ค่าเริ่มต้นคือ true สำหรับเป้าหมาย android_app (ควบคุมโดย แฟล็กบิลด์ที่เผยแพร่ RELEASE_R8_OPTIMIZE_BY_DEFAULT)
obfuscate ควบคุมการย่อขนาดและการเปลี่ยนชื่อตัวระบุ ค่าเริ่มต้นคือ false เพื่อความเข้ากันได้ในอดีต แต่ให้ตั้งค่าเป็น true ทุกครั้งที่เป็นไปได้เพื่อลดขนาด DEX และปลดล็อก การเพิ่มประสิทธิภาพที่ลึกซึ้งยิ่งขึ้น (ดู เปิดใช้การปรับให้ยากต่อการอ่านทุกครั้งที่เป็นไปได้)
shrink_resources นำทรัพยากรที่ไม่ได้ใช้ (res/ รายการ) ออกจาก APK ที่แพ็กเกจแล้ว หลังจากลดขนาดโค้ด ค่าเริ่มต้นคือ false
optimized_shrink_resources เรียกใช้ไปป์ไลน์เครื่องมือลดขนาดโค้ดและทรัพยากร R8 ที่ผสานรวมเพื่อให้ R8 ติดตาม การอ้างอิงโค้ดและทรัพยากรพร้อมกันในรอบเดียว เมื่อตั้งค่า shrink_resources: true ค่าเริ่มต้นจะเป็น RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true ในแพลตฟอร์มมาตรฐาน)
proguard_flags_files แสดงรายการไฟล์ .flags หรือ .pro เฉพาะโมดูล ที่มีกฎการเก็บรักษาที่กำหนดเอง หลีกเลี่ยงการเพิ่มไฟล์ที่กำหนดเองเมื่อคำอธิบายประกอบมาตรฐาน หรือค่าเริ่มต้นเพียงพอ
export_proguard_flags_files เผยแพร่ proguard_flags_files ของไลบรารีนี้ไปยัง โมดูลดาวน์สตรีมที่ขึ้นอยู่กับไลบรารีนี้แบบคงที่
trace_references_from ระบุเป้าหมายคู่หูของไลบรารี Java ที่ bytecode อ้างอิงถึง เป้าหมายนี้ R8 จะติดตามและเก็บรักษาโดยอัตโนมัติ

เปิดใช้การปรับให้ยากต่อการอ่าน (Obfuscation) ทุกที่ที่เป็นไปได้

ใน Soong obfuscate จะเป็นค่าเริ่มต้นของ false เพื่อให้เข้ากันได้กับเวอร์ชันก่อนหน้า แต่คุณ ควรตั้งค่า obfuscate: true อย่างชัดเจนใน android_app และเป้าหมาย DEX แบบสแตนด์อโลน ทุกครั้งที่เป็นไปได้

  • ร่องรอยของ DEX และ .vdex ที่เล็กลง: การเปลี่ยนชื่อแพ็กเกจ คลาส ฟิลด์ และเมธอดเป็นตัวระบุแบบย่อ (a, b) จะลดขนาดกลุ่มสตริง DEX (string_ids และ string_data_item) และตัวอธิบายประเภท เนื่องจาก Android จะแมปไฟล์ .vdex และ .odex ลงในหน่วยความจำของกระบวนการ ตารางสัญลักษณ์ที่มีขนาดเล็กลง จึงช่วยลดทั้งขนาดพาร์ติชันของระบบและหน่วยความจำที่ใช้ขณะรันไทม์ได้โดยตรง
  • การเพิ่มประสิทธิภาพทั้งโปรแกรมที่ละเอียดยิ่งขึ้น: การอนุญาตให้ R8 เปลี่ยนชื่อตัวระบุ จะช่วยให้ผสานคลาส ลดระดับแพ็กเกจ และขจัดคลาสสังเคราะห์ที่ซ้ำกัน ในแพ็กเกจที่ R8 ต้องข้ามเพื่อหลีกเลี่ยงการตั้งชื่อที่ซ้ำกัน
  • การสร้างสัญลักษณ์ของ Stack Trace ทั้งหมด: การเปิดใช้การปกปิดใน Soong ไม่ได้ ลดความสามารถในการแก้ไขข้อบกพร่อง สำหรับเป้าหมายที่คอมไพล์ด้วย R8 ทุกรายการ Soong จะสร้าง proguard_dictionaryไฟล์แมปในไดเรกทอรีกลางของโมดูล รวมพจนานุกรมโมดูลทั้งหมดไว้ในอาร์ติแฟกต์ proguard-dict.zip ของบิลด์ ฝังแฮชแมป (--map-id-template) ไว้ในส่วนหัว DEX และเขียนแอตทริบิวต์ของคลาส SourceFile (--source-file-template) ใหม่เพื่อให้ retrace สามารถสร้างสัญลักษณ์ของ Stack Trace โดยอัตโนมัติ

เก็บ obfuscate: false (หรือคงชื่อ API สาธารณะไว้อย่างชัดเจน) ไว้สำหรับกรณีต่อไปนี้เท่านั้น

  • ไลบรารีที่แชร์ใน bootclasspath หรือ system_server classpath: โมดูล (java_library หรือ java_sdk_library) ที่แสดง API ภายนอก จะเชื่อมโยงแบบไดนามิกโดยโมดูลอื่นๆ ในรันไทม์ (เช่น เป้าหมาย framework.jar, services.jar หรือ <uses-library>) ต้อง เก็บ obfuscate: false ไว้หรือคงพื้นผิว API อย่างชัดเจนโดยใช้ กฎ @KeepForApi หรือ -keep (พร้อมกับ protect_api_surface: true สำหรับเป้าหมาย bootclasspath) เพื่อให้ผู้เรียกที่คอมไพล์ เทียบกับ Stub สามารถแก้ไขชื่อคลาสและชื่อสมาชิกในรันไทม์ได้

ก่อนเปิดใช้ obfuscate: true ในแอปพลิเคชัน ให้ตรวจสอบข้อกำหนดเบื้องต้นในการย้ายข้อมูลต่อไปนี้

  • ทรัพยากร Dependency ของ APK ทดสอบภายนอก: หาก android_test ชุด APK instrumentation_for: "MyApp" ภายนอกเรียกใช้คลาสหรือเมธอดภายในของ MyApp โดยตรง การเปลี่ยนชื่อสัญลักษณ์เหล่านั้นจะทำให้เกิด NoSuchMethodError หรือ NoClassDefFoundError ในรันไทม์ของการทดสอบ ควรจัดโครงสร้างการทดสอบเป็น APK ที่ลิงก์แบบคงที่ซึ่งมีเครื่องมือในตัว MyApp.implใส่คำอธิบายประกอบฮุกทดสอบ ด้วย @VisibleForTesting หรือกำหนดค่า trace_references_from (ดู ขั้นตอนที่ 2: ย้ายข้อมูลกฎการเก็บรักษาที่ขับเคลื่อนด้วยการทดสอบ) เพื่อให้ ระบบเก็บรักษาสัญลักษณ์ภายในที่การทดสอบต้องการไว้ในขณะที่ทำให้ส่วนอื่นๆ ของ แอปสับสน
  • การสะท้อนตามสตริงและ JNI: หากโมดูลค้นหาคลาส เมธอด หรือฟิลด์ตามชื่อสตริงตัวอักษร (Class.forName, getDeclaredMethod หรือ JNI FindClass และ GetMethodID) โดยไม่มีกฎหรือคำอธิบายประกอบ keep การปกปิดจะเปลี่ยนชื่อเป้าหมายเหล่านั้นและทำให้การค้นหาที่รันไทม์ใช้งานไม่ได้ (โปรดทราบว่าการสะท้อนที่ไม่มีคำอธิบายประกอบจะล้มเหลวภายใต้ shrink: true และoptimize: true ด้วย) ใส่คำอธิบายประกอบให้กับจุดแรกเข้าเหล่านั้นด้วยคำแนะนำในการใส่คำอธิบายประกอบ (@UsesReflection, @UsedByReflection หรือ @UsedByNative) เพื่อให้ R8 เก็บชื่อไว้และปิดบังส่วนที่เหลือของโมดูล

ปฏิบัติตามหลักการไม่ใช้ฟีเจอร์ที่กำหนดเอง

โมดูลแพลตฟอร์ม Android ที่ดีที่สุดจะไม่มีไฟล์ proguard.flags หรือ keep.xml ที่กำหนดเอง ในบิลด์แพลตฟอร์ม Android กฎการเก็บรักษาที่กำหนดเองส่วนใหญ่ ซ้ำซ้อน

  • พื้นฐานของแพลตฟอร์มและ AAPT2: จุดแรกเข้าส่วนใหญ่จะยังคงอยู่ โดยอัตโนมัติตามพื้นฐานของแพลตฟอร์มทั่วโลก (เช่น @Keep, JNI native และ @VisibleForTesting) หรือสร้างโดย AAPT2 จาก AndroidManifest.xml และทรัพยากรเลย์เอาต์ (ดู ทำความเข้าใจกฎการคงค่าเริ่มต้นของแพลตฟอร์ม)
  • ทางเลือกที่กำหนดเป้าหมาย: ในกรณีที่ต้องรักษาจุดแรกเข้าไว้ ให้ใช้คำอธิบายประกอบในไฟล์ประกาศ (keepanno, @VisibleForTesting), กฎของคลังที่ส่งออก (export_proguard_flags_files: true) หรือการลิงก์การทดสอบแบบคงที่แทนไฟล์ .flags ที่แยกออกมา (ดูตรวจสอบและย้ายข้อมูลกฎการเก็บรักษาที่มีอยู่)

ก่อนเพิ่มหรือเก็บไฟล์ proguard.flags หรือ keep.xml ที่กำหนดเองไว้ ให้ตรวจสอบว่ากฎดังกล่าวได้รับการจัดการโดยค่าพื้นฐานเริ่มต้นอยู่แล้วหรือสามารถย้ายข้อมูลไปยัง คำอธิบายประกอบโค้ดได้หรือไม่

ทำความเข้าใจกฎการเก็บรักษาเริ่มต้นของแพลตฟอร์ม

Soong จะส่งกฎพื้นฐานส่วนกลางต่อไปนี้ไปยังการเรียกใช้ R8 ทุกครั้งในเป้าหมายการคอมไพล์ DEX (โมดูล android_app, android_test และ java_library ที่ติดตั้งได้) ซึ่งกำหนดค่าไว้ใน build/soong/java/dex.go โดยอัตโนมัติ

  • build/make/core/proguard.flags: เก็บรักษาคลาสและสมาชิก ที่ใส่คำอธิบายประกอบด้วย @com.android.internal.annotations.VisibleForTesting ใน ทุกแพ็กเกจ และด้วย @VisibleForTesting (androidx.annotation.VisibleForTesting หรือ com.google.common.annotations.VisibleForTesting) ภายในแพ็กเกจ android.**, com.android.** และ com.google.android.** รวมถึงยังคง @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback และ @dalvik.annotation.optimization.**
  • build/make/core/proguard_basic_keeps.flags: เก็บรักษาแอตทริบิวต์ SourceFile สำหรับ Stack Trace, คำอธิบายประกอบการมองเห็นรันไทม์ (RuntimeVisible*Annotations), แอตทริบิวต์ Exceptions และ AnnotationDefault , เมธอด native, สมาชิก Serializable, เมธอด @JavascriptInterface , ตัวสร้าง Throwable(String), ฟิลด์ Parcelable$CREATOR และ ฟิลด์ MessageLite ของ Protobuf
  • build/make/core/proguard/kotlin.flags: ปิดเสียงคำเตือนที่ไม่เป็นอันตรายสำหรับ Meta-Annotation ของ Kotlin บางรายการ (kotlin.Metadata และ kotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) และนำ Annotation DebugMetadata ของ Kotlin ออกในบิลด์ที่เผยแพร่
  • build/make/core/proguard/checknotnull.flags: แทนที่การเรียกใช้ฟังก์ชันช่วยตรวจสอบค่า Null ทั่วไป (com.google.common.base.Preconditions.checkNotNull และ dagger.internal.Preconditions.checkNotNull*) ด้วยการตรวจสอบค่า Null ในไบต์โค้ดแบบย่อ ไฟล์นี้จงใจละเว้น Objects.requireNonNull เพื่อรักษา ข้อความข้อยกเว้นที่ชัดเจนในขอบเขต API ของเฟรมเวิร์ก
  • build/make/core/proguard/enumvalues.flags: เก็บเมธอด values และ valueOf ไว้ในประเภท enum เว้นแต่จะปิดใช้โดยการกำหนดค่าบิลด์
  • กฎที่ AAPT2 สร้างขึ้นโดยอัตโนมัติ: AAPT2 จะตรวจสอบไฟล์ AndroidManifest.xml ที่ผสานรวมแล้ว XML ของเลย์เอาต์ และ XML ของค่ากำหนดเพื่อสร้างกฎการคงไว้ที่แน่นอนสำหรับActivity, Service, BroadcastReceiver, ContentProvider BackupAgent, Application, คลาสย่อย View ที่กำหนดเอง, คลาสย่อย Preference และเมธอด android:onClick ของ XML ที่ลงทะเบียนทั้งหมด

ตรวจสอบและย้ายข้อมูลกฎการเก็บรักษาที่มีอยู่

เมื่อตรวจสอบไฟล์ proguard.flags หรือ keep.xml ที่มีอยู่แล้วในที่เก็บของแพลตฟอร์ม ให้ประเมินแต่ละกฎตามลำดับเทียบกับลำดับชั้น 4 ขั้นตอนต่อไปนี้

ขั้นตอนที่ 1: ลบกฎที่ซ้ำซ้อนหรือล้าสมัย

ลบกฎที่ครอบคลุมโดยค่าพื้นฐานส่วนกลาง, กฎไฟล์ Manifest และเลย์เอาต์ของ AAPT2 หรือค่าเริ่มต้นของ R8 รวมถึงกฎที่อ้างอิงคลาสหรือแพ็กเกจที่ไม่มีอยู่อีกต่อไป

หากกฎทั้งหมดในไฟล์ .flags ซ้ำซ้อนกัน ให้ลบไฟล์และนำ proguard_flags_files ออกจาก Android.bp หากoptimizeที่เหลืออยู่ ระบุการตั้งค่าเริ่มต้นซ้ำ ให้นำบล็อกที่ซ้ำกันออกและจัดรูปแบบไฟล์บิลด์ ด้วย bpfmt -w Android.bp เมื่อล้างข้อมูลแพ็กเกจ ให้ตรวจสอบว่าโมดูลเสริมในไดเรกทอรีย่อย (เช่น ตัวแปรเป้าหมาย Kotlin) อ้างอิงไฟล์แฟล็กเดียวกันหรือไม่ แล้วอัปเดตพร้อมกัน

ขั้นตอนที่ 2: ย้ายข้อมูลกฎการเก็บรักษาที่ขับเคลื่อนด้วยการทดสอบ

ย้ายกฎการเก็บรักษาที่ขับเคลื่อนด้วยการทดสอบออกจากไฟล์การผลิตที่กำหนดเอง .flags โดยใช้รูปแบบใดรูปแบบหนึ่งต่อไปนี้

  • ลิงก์ไลบรารีการติดตั้งใช้งานในการทดสอบ (static_libs): เมื่อ android_testใช้รายละเอียดการติดตั้งใช้งานภายในหรือแบบแพ็กเกจส่วนตัว ของแอป สถาปัตยกรรมแพลตฟอร์มที่แนะนำคือการวางไฟล์แหล่งที่มาของแอป ใน android_library (MyApp.impl) และลิงก์แบบคงที่ MyApp.impl ทั้งใน MyApp และ MyAppTests

    android_library {
        name: "MyApp.impl",
        srcs: ["src/**/*.java"],
        manifest: "AndroidManifest.xml",
    }
    
    android_app {
        name: "MyApp",
        static_libs: ["MyApp.impl"],
        optimize: {
            obfuscate: true,
            shrink_resources: true,
        },
    }
    
    android_test {
        name: "MyAppTests",
        srcs: ["tests/**/*.java"],
        static_libs: ["MyApp.impl"],
    }
    

    รูปแบบ 3 โมดูลนี้ช่วยให้ MyAppTests ทำงานเป็นการทดสอบที่สร้างเครื่องมือด้วยตนเอง โดยมีสิทธิ์เข้าถึงคลาสภายในอย่างเต็มรูปแบบ ช่วยให้ MyApp เปิดใช้ obfuscate: true ได้อย่างอิสระโดยไม่ทำให้การทดสอบหยุดทำงาน หลีกเลี่ยงการจัดส่งจุดแรกเข้าสำหรับการทดสอบเท่านั้นใน APK ที่ใช้งานจริง และไม่ต้องสร้าง MyApp ใหม่เมื่อมีการเปลี่ยนแปลงโค้ดทดสอบ

  • ใส่คำอธิบายประกอบให้กับ Hook การทดสอบด้วย @VisibleForTesting: หากการทดสอบภายนอก APK เรียกใช้เมธอดหรือตัวสร้างภายในจำนวนเล็กน้อยในandroid_app เป้าหมาย และการปรับโครงสร้างเป็นไลบรารี .impl เป็นไปไม่ได้ ให้ใส่คำอธิบายประกอบให้กับประกาศเหล่านั้นด้วย @VisibleForTesting build/make/core/proguard.flagsพื้นฐาน@VisibleForTestingทั่วโลกจะเก็บandroid.**รายการในแพ็กเกจ com.android.** และcom.google.android.** โดยอัตโนมัติโดยไม่ต้องใช้ไฟล์ .flags ที่กำหนดเอง (สำหรับโมดูลของผู้ให้บริการภายนอกเนมสเปซเหล่านี้ ให้ใช้ @UsedByReflection หรือกฎของไลบรารีที่ส่งออก)

  • ใช้ trace_references_from ข้ามขอบเขตไลบรารีของแพลตฟอร์ม: เมื่อการทดสอบลิงก์การติดตั้งใช้งานเป้าหมายแบบคงที่ไม่ได้ (เช่น การทดสอบที่ใช้บริการเซิร์ฟเวอร์ระบบหรือ JAR ของแพลตฟอร์ม เช่น framework-connectivity) ให้กำหนดค่า trace_references_from ในโมดูลเป้าหมายที่ชี้ไปยังโมดูลคู่ของไลบรารี Java ที่มีแหล่งที่มาของการทดสอบ R8 จะติดตามและเก็บรักษาคลาสและสมาชิกทั้งหมดที่อ้างอิงโดย ไบต์โค้ดคู่

ขั้นตอนที่ 3: ส่งออกกฎจากคลังที่เป็นเจ้าของ

หาก java_library หรือ android_library ที่ใช้ร่วมกันต้องมีกฎการเก็บรักษาสำหรับการสะท้อนภายในหรือการเรียกกลับ JNI ของตัวเอง ให้กำหนดกฎในเป้าหมายไลบรารีและตั้งค่า export_proguard_flags_files: true ดังนี้

java_library {
    name: "my-shared-library",
    srcs: ["src/**/*.java"],
    optimize: {
        proguard_flags_files: ["proguard.flags"],
        export_proguard_flags_files: true,
    },
}

android_appและเป้าหมายไลบรารีทั้งหมดที่ลิงก์แบบคงที่ my-shared-libraryจะรับค่ากฎเหล่านั้นโดยอัตโนมัติ ดังนั้นแอปดาวน์สตรีมจึงไม่ จำเป็นต้องทำซ้ำ เมื่อโมดูลหลายรายการในไดเรกทอรีต่างๆ ต้องแชร์ชุดกฎที่ไม่ได้เชื่อมโยงกับไลบรารีโค้ดเดียว ให้เผยแพร่ไฟล์ .flags ผ่าน filegroup ที่ชัดเจนใน Android.bp เพื่อให้โมดูลอ้างอิง :my-shared-flags ได้อย่างชัดเจนในขอบเขตของแพ็กเกจ ดูคำแนะนำทั่วไปเกี่ยวกับ การเขียนกฎการเก็บรักษาของผู้ใช้ไลบรารีโดยไม่จำกัดการเพิ่มประสิทธิภาพแอปปลายทาง ได้ที่การเพิ่มประสิทธิภาพสำหรับผู้เขียนไลบรารี

ขั้นตอนที่ 4: ใส่คำอธิบายประกอบการประกาศในซอร์สโค้ด

เมื่อเข้าถึงคลาส เมธอด ตัวสร้าง หรือฟิลด์ผ่านการสะท้อนหรือ JNI และไม่ได้ครอบคลุมโดย AAPT2 หรือค่าพื้นฐานส่วนกลาง ให้แทนที่กฎที่แยกออกมา-keep ในไฟล์ .flags ด้วยคำอธิบายประกอบของแหล่งที่มาจาก คำแนะนำเกี่ยวกับคำอธิบายประกอบ Keep (ดูkeepannoเอกสารอ้างอิง Javadoc)

  • @UsesReflection: ใช้คำอธิบายประกอบนี้เมื่อคุณควบคุมโค้ดที่ทำการรีเฟลกชัน วางไว้ที่การเรียกการสะท้อน เพื่อประกาศว่ามีการเข้าถึงคลาส เมธอด หรือฟิลด์เป้าหมายใด แบบไดนามิก เนื่องจาก @UsesReflection จะเข้ารหัสเงื่อนไขเบื้องต้นโดยอัตโนมัติ ว่าเว็บไซต์ที่เรียกใช้ที่มีคำอธิบายประกอบสามารถเข้าถึงได้ R8 จึงตัดทั้งผู้เรียก และเป้าหมายการสะท้อนหากไม่มีการใช้เว็บไซต์ที่เรียกใช้
  • @UsedByReflection: วางไว้ในคลาส เมธอด ฟิลด์ หรือตัวสร้างที่โค้ดหรือไลบรารีภายนอกสร้างอินสแตนซ์หรือเรียกใช้แบบรีเฟลกทีฟ เช่น คลาสที่โหลดด้วย Class.forName จากคีย์ Bundle หรือ Settings หรือคลาสปลั๊กอินที่โหลดใน ClassLoader แบบไดนามิก ระบุ kind (เช่น KeepItemKind.CLASS_AND_METHODS) preconditions (@KeepCondition) และข้อจำกัดของพารามิเตอร์เพื่อให้ R8 เก็บ เฉพาะสัญญาการสะท้อนที่แน่นอน และยังคงเพิ่มประสิทธิภาพหรือตัด สมาชิกที่ไม่ได้ใช้ของคลาสออกได้ อย่าเพิ่ม @UsedByReflection ลงในคอมโพเนนต์ที่ลงทะเบียนในไฟล์ Manifest เช่น JobService หรือ BroadcastReceiver เนื่องจาก AAPT2 จะเก็บคอมโพเนนต์เหล่านี้ไว้แล้ว
  • @UsedByNative: วางไว้ในเมธอดหรือฟิลด์ที่เข้าถึงจากโค้ด JNI ของ C หรือ C++ โดยใช้ GetMethodID, GetStaticMethodID หรือ GetFieldID
  • @KeepForApi: วางไว้ในคลาสหรือสมาชิก API ของไลบรารีที่ต้องคงไว้เหมือนเดิมเมื่อมีการลดขนาดไลบรารีเองก่อนการเผยแพร่
  • @Keep (androidx.annotation.Keep): ใช้เป็นข้อมูลสำรอง เมื่อ keepanno ใช้ไม่ได้ การใช้ @Keep กับคลาสจะเก็บคลาสและสมาชิกทั้งหมดไว้โดยไม่มีเงื่อนไข การใช้ @Keep กับเมธอดหรือฟิลด์จะทำหน้าที่เป็นจุดแรกเข้าแบบไม่มีเงื่อนไขซึ่งจะเก็บสมาชิกและคลาสที่มีสมาชิกนั้นไว้แม้ว่าจะไม่มีการสร้างอินสแตนซ์ของคลาสเลยก็ตาม ในขณะที่ keepanno แสดงถึงการเข้าถึงแบบมีเงื่อนไข

วิธีใช้คำอธิบายประกอบ keepanno ของ R8 ในโมดูล Soong

  1. วิธีเพิ่ม "keepanno-annotations" ไปยัง libs ใน Android.bp

    libs: [
        "keepanno-annotations",
    ],
    
  2. นำเข้า com.android.tools.r8.keepanno.annotations.* และใส่คำอธิบายประกอบ การประกาศหรือตำแหน่งที่เรียกในซอร์สโค้ด Java หรือ Kotlin

    import com.android.tools.r8.keepanno.annotations.KeepItemKind;
    import com.android.tools.r8.keepanno.annotations.UsedByReflection;
    
    public final class CustomPluginController {
        @UsedByReflection(
            description = "Instantiated via Class.forName from plugin config",
            kind = KeepItemKind.CLASS_AND_METHODS)
        public CustomPluginController(Context context) {
            // ...
        }
    }
    

    R8 จะแปลkeepannoคำอธิบายประกอบเป็นรูปแบบกฎการเก็บรักษาภายในโดยตรง และลบคำอธิบายประกอบออกจากเอาต์พุต DEX สุดท้าย โดยไม่เพิ่ม ค่าใช้จ่ายไบต์โค้ดรันไทม์

หลีกเลี่ยงข้อผิดพลาดที่พบบ่อย

ส่วนต่อไปนี้จะอธิบายkeep.xml ข้อผิดพลาดที่พบบ่อยproguard.flagsในโค้ดแพลตฟอร์มและวิธีแก้ไข

กฎการเก็บรักษาคอมโพเนนต์แบบกว้าง

# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
  • เหตุใดจึงส่งผลเสียต่อประสิทธิภาพ: กฎนี้ซ้ำซ้อนกับ AAPT2 โดยสมบูรณ์ เนื่องจากใช้ไวลด์การ์ดโดยไม่ได้ตรวจสอบว่าคอมโพเนนต์ลงทะเบียนใน AndroidManifest.xml ที่ผสานขั้นสุดท้ายหรือไม่ จึงบังคับให้ R8 เก็บ Activity, Service หรือคลาสย่อย BroadcastReceiver ทุกรายการที่พบในคลาสพาธ รวมถึงคอมโพเนนต์ไลบรารีที่ไม่ได้ใช้และกิจกรรมการแก้ไขข้อบกพร่องที่ปิดใช้
  • การแก้ไขที่แนะนำ: ลบกฎ AAPT2 จะตรวจสอบไฟล์ Manifest ที่ผสานแล้วและ สร้าง-keepที่แน่นอนสำหรับคอมโพเนนต์ที่ลงทะเบียน

กฎการเก็บตัวแฮนเดิลการคลิก XML แบบกว้าง

# Don't do this:
-keepclassmembers class * {
    public void *(android.view.View);
}
  • เหตุใดจึงทำให้ประสิทธิภาพลดลง: การเก็บรักษาวิธีการ public void *(View) ทั้งหมด ในทุกคลาสของโมดูลจะทำให้ R8 ไม่สามารถลบหรือแทรกวิธีการ ที่รับพารามิเตอร์ View รายการเดียว
  • การแก้ไขที่แนะนำ: ลบกฎ AAPT2 จะสแกนไฟล์ XML ของเลย์เอาต์และ สร้างกฎการเก็บเป้าหมายสำหรับเมธอดที่อ้างอิงโดยแอตทริบิวต์ android:onClick

กฎการเก็บรักษา Getter และ Setter ของ Broad View

# Don't do this:
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
    public *** get*();
}
  • เหตุผลที่ทำให้ประสิทธิภาพลดลง: กฎนี้บังคับให้ R8 เก็บตัวรับและตัวตั้งค่าทุกรายการในทุกคลาสย่อย View ในแอปและไลบรารีที่ลิงก์ทั้งหมด (รวมถึงไลบรารี AndroidX และ Material) ซึ่งบล็อกการนำเมธอดที่ไม่ได้ใช้ออก และการแทรกโค้ดในโค้ด UI
  • การแก้ไขที่แนะนำ: ลบกฎ AAPT2 จะเก็บรักษาตัวสร้าง สำหรับคลาส View ที่กำหนดเองซึ่งขยายจากไฟล์ XML ของเลย์เอาต์อยู่แล้ว หากโค้ดเคลื่อนไหวพร็อพเพอร์ตี้ของ View โดยใช้ชื่อสตริงแบบรีเฟลกทีฟ เช่น ObjectAnimator.ofFloat(view, "translationZ", ...) ให้แทนที่ชื่อสตริง ด้วยการอ้างอิงพร็อพเพอร์ตี้ที่พิมพ์ (View.TRANSLATION_Z หรือการใช้งาน FloatProperty หรือ IntProperty ที่กำหนดเอง) เพื่อหลีกเลี่ยงการรีเฟลกชัน โดยสิ้นเชิง หากหลีกเลี่ยงการเข้าถึงพร็อพเพอร์ตี้แบบรีเฟลกชันไม่ได้ ให้ใส่คำอธิบายประกอบใน Getter หรือ Setter ที่เฉพาะเจาะจงด้วย @UsedByReflection

การปิดใช้การเพิ่มประสิทธิภาพหรือการปกปิดทั้งหมด

# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
  • เหตุใดจึงส่งผลเสียต่อประสิทธิภาพ: การวาง -dontoptimize หรือ -dontshrink ภายในไฟล์ .flags จะลบล้างการตั้งค่า Android.bp ของโมดูลโดยไม่มีการแจ้งเตือน และปิดใช้การเพิ่มประสิทธิภาพทั่วทั้งเป้าหมาย ที่แย่กว่านั้นคือ หากไลบรารีส่งออกไฟล์ .flags ที่มี -dontoptimize ระบบจะปิดใช้การเพิ่มประสิทธิภาพ R8 สำหรับ android_app ทุกรายการที่ลิงก์ไลบรารี
  • วิธีแก้ไขที่แนะนำ: นำ -dontoptimize, -dontshrink และ -dontobfuscate ออกจากไฟล์ .flags ควบคุมลักษณะการเพิ่มประสิทธิภาพ อย่างชัดเจนใน Android.bp โดยใช้บล็อก optimize (shrink, optimize, obfuscate) ในเป้าหมายใบ

การแจ้งการวินิจฉัยในกฎที่คอมมิต

# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
  • เหตุใดจึงทำให้บิลด์ช้าลง: แฟล็กการวินิจฉัยจะทำให้บันทึกบิลด์เต็มไปด้วยข้อความหรือพยายามเขียนไฟล์เอาต์พุตไปยังเส้นทางในเครื่องระหว่างการสร้าง Soong ในแซนด์บ็อกซ์
  • วิธีแก้ไขที่แนะนำ: ลบ Flag เหล่านี้ออกจากไฟล์ .flags ที่คอมมิตแล้ว Soong จะเขียนเอาต์พุตการแมปและการใช้งาน R8 โดยอัตโนมัติ เช่น proguard_dictionary และ proguard_usage.zip ไปยังไดเรกทอรีกลางของโมดูล ภายใต้ out/soong/.intermediates/

กฎการเก็บรักษาเวอร์ชันที่ใช้งานจริงสำหรับโค้ดทดสอบ

  • เหตุผลที่ทำให้ประสิทธิภาพลดลง: การเพิ่มกฎ -keep ที่กำหนดเองเพื่อการทดสอบ การเข้าถึงเพียงอย่างเดียวจะบังคับให้โค้ดนั้นยังคงไม่ได้รับการปกปิดและยังคงอยู่ในบิลด์เวอร์ชันที่ใช้งานจริง แม้ว่าการใส่คำอธิบายประกอบเมธอดด้วย @VisibleForTesting หรือการกำหนดค่า trace_references_from จะช่วยให้ไม่ต้องดูแลกฎ -keep ด้วยตนเอง แต่สัญลักษณ์เหล่านั้นจะยังคงอยู่ในไบนารีที่ใช้งานจริง
  • การแก้ไขที่แนะนำ: หากต้องการให้จุดแรกเข้าสำหรับการทดสอบเท่านั้นอยู่นอก APK ของเวอร์ชันที่ใช้งานจริงอย่างสมบูรณ์ ให้จัดโครงสร้างแอปพลิเคชันเป็นไลบรารี .impl แล้วลิงก์เข้ากับ APK ของการทดสอบที่ใช้เครื่องมือด้วยตนเองโดยใช้ static_libs ตามที่อธิบายไว้ในขั้นตอนที่ 2: ย้ายข้อมูลกฎการเก็บรักษาที่ขับเคลื่อนด้วยการทดสอบ

ไวลด์การ์ดทรัพยากรแบบครอบคลุมใน keep.xml

<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@raw/*,@drawable/*,@string/*" />
  • เหตุใดจึงส่งผลเสียต่อประสิทธิภาพ: อักขระไวด์การ์ดแบบครอบคลุมจะเก็บทรัพยากรทุกรายการใน ประเภทการจับคู่ทั่วทั้งโมดูลและการอ้างอิง ซึ่งจะทำให้การลดขนาดทรัพยากร (shrink_resources: true) ไม่ได้ผล และทำให้ตาราง APK และตารางที่แมป resources.arsc มีขนาดใหญ่ขึ้น
  • การแก้ไขที่แนะนำ
    • ใช้การอ้างอิงทรัพยากรแบบคงที่มากกว่า keep.xml: หลีกเลี่ยงการใช้วิธี Resources.getIdentifier เพื่อค้นหาชุดทรัพยากรที่จำกัด แบบไดนามิก เช่น สตริงการทดสอบที่มีหมายเลขหรือ Drawable ที่มีธีม แต่ให้ใช้คำสั่ง switch ในเวลาคอมไพล์หรือแมปค่าคงที่ static R.id, R.string หรือ R.drawable แทน การอ้างอิงแบบคงที่ช่วยให้ R8 และ AAPT2 ติดตามทรัพยากรที่ใช้งานอยู่ได้อย่างแม่นยำ ลดค่าใช้จ่ายในการค้นหาสตริงรันไทม์ และไม่จำเป็นต้องใช้ keep.xml อีกต่อไป
    • ระบุชื่อทรัพยากรที่เฉพาะเจาะจง: หากต้องมีการค้นหาแบบไดนามิก เช่น โดยโปรแกรมอ่านใบอนุญาตของบุคคลที่สาม ให้ระบุตัวระบุทรัพยากรที่แน่นอนใน tools:keep (เช่น tools:keep="@raw/third_party_licenses")
    • ลบไฟล์ keep.xml ที่ซ้ำกัน: หากมีการอ้างอิงทรัพยากรที่แสดงในโค้ด (R.raw.foo) หรือ XML (@raw/foo) แบบคงที่อยู่แล้ว เครื่องมือลดขนาดจะเก็บทรัพยากรเหล่านั้นไว้โดยอัตโนมัติ คุณจึงลบ res/raw/keep.xml ได้

ยืนยันและตรวจสอบการเปลี่ยนแปลงกฎ

เมื่อใดก็ตามที่คุณนำกฎการเก็บรักษาออกหรือจำกัดกฎการเก็บรักษาใน proguard.flags หรือ keep.xml ให้ตรวจสอบว่าโมดูลสร้างได้อย่างถูกต้อง ผ่านการทดสอบหน่วยและการทดสอบเครื่องมือ และยังคงมีจุดแรกเข้าที่จำเป็นทั้งหมด

สร้างและทดสอบโมดูล

  1. สร้างโมดูลอย่างสะอาดเพื่อยืนยันว่า R8 และ AAPT2 เสร็จสมบูรณ์โดยไม่มี คำเตือนเกี่ยวกับการอ้างอิงที่ขาดหายไป

    m <MODULE_NAME>
    
  2. เรียกใช้การทดสอบหน่วยและการทดสอบการวัดคุมสำหรับโมดูลโดยใช้ atest ดังนี้

    atest <TEST_MODULE_NAME>
    
  3. สำหรับแอปของระบบ บริการที่มีสิทธิ์ หรือโมดูลที่ขึ้นอยู่กับฮาร์ดแวร์ ให้เรียกใช้ การทดสอบเครื่องมือและการทดสอบ UI ในอุปกรณ์จริงเป้าหมายหรือในห้อง ทดสอบอุปกรณ์เพื่อใช้เส้นทางการสะท้อนรันไทม์ การเชื่อมโยง IPC และการขยายทรัพยากร

ตรวจสอบความแตกต่างของ DEX และทรัพยากร

เปรียบเทียบ APK หรือ JAR ที่คอมไพล์แล้วก่อนและหลังการเปลี่ยนแปลงกฎเพื่อยืนยันว่า R8 นำโค้ดและทรัพยากรที่ไม่ได้ใช้แล้วออกโดยไม่นำจุดแรกเข้าที่คาดไว้

  • ใช้ apkanalyzer หรือ dexdump เพื่อตรวจสอบคลาส เมธอด และฟิลด์ที่เก็บไว้ในไฟล์ APK หรือ DEX เอาต์พุต

    apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    
  • เมื่อแก้ไข keep.xml หรือเปิดใช้ shrink_resources ให้ใช้ aapt2 dump resources เพื่อยืนยันว่าบิลด์จะลบแหล่งข้อมูลที่ไม่ได้ใช้และ เก็บแหล่งข้อมูลที่จำเป็นไว้

    aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
    

วิเคราะห์รัศมีการเก็บรักษาและการแทนที่กฎด้วย R8

คอมไพเลอร์ R8 แบบโอเพนซอร์สมีเครื่องมือวิเคราะห์รัศมีการเก็บรักษา (ซึ่งแสดงใน Android Studio และ Gradle เป็นเครื่องมือวิเคราะห์การกำหนดค่า R8 ด้วย) ที่วัดผลกระทบที่แน่นอนของกฎการเก็บรักษาทุกข้อในระหว่างการคอมไพล์ Soong ผสานรวมเครื่องมือวิเคราะห์นี้ เข้ากับการสร้างแพลตฟอร์ม Android โดยตรง (กำหนดค่าใน build/soong/java/dex.go)

หากต้องการวิเคราะห์กฎการเก็บสำหรับโมดูลแพลตฟอร์มหรือทั้งบิลด์ ให้ทำดังนี้

  1. เรียกใช้บิลด์ด้วยตัวแปรสภาพแวดล้อม R8_DUMP_KEEP_RADIUS=true ดังนี้

    R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>
    

    เมื่อตั้งค่า R8_DUMP_KEEP_RADIUS=true แล้ว Soong จะสั่งให้ R8 บันทึกเมตริกของกฎ keep rule ในไฟล์ r8keepradius.pb ระดับกลางสำหรับแต่ละโมดูลที่คอมไพล์ ภายใต้ out/soong/.intermediates/ สำหรับกฎการเก็บรักษาและ keepanno คำอธิบายประกอบแต่ละรายการ R8 จะบันทึกข้อมูลต่อไปนี้

    • รัศมีการคงอยู่ทันที: คลาส ฟิลด์ และเมธอดที่แน่นอน ซึ่งกฎเก็บไว้ พร้อมกับข้อจำกัดเฉพาะที่บังคับใช้ กับการลดขนาด การเพิ่มประสิทธิภาพ หรือการปกปิด
    • การรวมกฎ: กฎการเก็บรักษาหรือคำอธิบายประกอบอื่นๆ ที่เก็บรักษารายการเดียวกันอยู่แล้ว หากกฎที่กำหนดเองถูกแทนที่ด้วยกฎ AAPT2 หรือกฎพื้นฐานส่วนกลางอย่างสมบูรณ์ คุณสามารถลบกฎที่กำหนดเองได้อย่างปลอดภัย
    • กฎระดับแพ็กเกจและกฎส่วนกลาง: กฎที่ใช้ไวลด์การ์ดระดับแพ็กเกจแบบกว้าง หรือใช้คำสั่งการกำหนดค่าส่วนกลาง
  2. แปลงr8keepradius.pbเอาต์พุตเป็นรายงาน HTML แบบอินเทอร์แอกทีฟโดยใช้ KeepRadiusHtmlReportGenerator (รวมอยู่ใน prebuilts/r8/r8.jar)

    # Generate an HTML report for a single module
    INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME>
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        $INTERMEDIATES/android_common/r8keepradius.pb \
        keep_radius_report.html
    
    # Scan all built modules under out/soong/.intermediates and generate
    # per-module HTML reports plus an aggregate keepradius.html summary
    java -cp prebuilts/r8/r8.jar \
        com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \
        out/soong/.intermediates \
        out/keep_radius_reports
    

    เมื่อระบุไดเรกทอรี KeepRadiusHtmlReportGenerator จะเดินผ่านไฟล์ทั้งหมด *keepradius*.pb สร้างรายงาน HTML สำหรับแต่ละโมดูล และ สร้างout/keep_radius_reports/keepradius.htmlสรุปจำนวนรายการที่ใช้งานอยู่ จำนวนรายการที่เก็บไว้ และกฎการเก็บรักษาที่มีรัศมีสูงสุดใน บิลด์ ดูข้อมูลเพิ่มเติมเกี่ยวกับการตีความคะแนนการลดขนาด การเพิ่มประสิทธิภาพ และการปกปิดในรายงานที่สร้างขึ้นได้ที่หัวข้อ ใช้เครื่องมือวิเคราะห์การกำหนดค่า R8

นอกจากนี้ Soong ยังรองรับตัวแปรสภาพแวดล้อมในการวินิจฉัย R8 เพิ่มเติมอีก 2 รายการใน build/soong/java/dex.go

  • R8_DUMP_INPUT=true: เขียน r8inputs.zip ไปยังไดเรกทอรีกลางของโมดูล ซึ่งมี JAR อินพุตทั้งหมด, JAR ของไลบรารี และการกำหนดค่า ProGuard ที่ผสานแล้ว สำหรับการสร้าง R8 แบบสแตนด์อโลนซ้ำ
  • R8_DUMP_PERFETTO_TRACE=true: เขียน r8trace.ptrace ไปยังไดเรกทอรีกลางของโมดูล เพื่อตรวจสอบการส่งผ่านการคอมไพล์ R8 ใน Perfetto

ตรวจสอบกฎสำหรับผู้ใช้ห้องสมุด

เมื่อ java_library หรือ android_library ส่งออกกฎไปยังผู้ใช้ปลายทาง (export_proguard_flags_files: true) กฎเหล่านั้นต้องไม่มีฟีเจอร์ส่วนกลางที่เปลี่ยนแปลงหรือปิดใช้การเพิ่มประสิทธิภาพสำหรับแอปที่ใช้

R8 มีเครื่องมือวิเคราะห์กฎการเก็บรักษาแบบโอเพนซอร์ส (ProcessKeepRules) ซึ่งแสดงในแพลตฟอร์ม Android build เป็นเครื่องมือโฮสต์ process-keep-rules (กำหนดไว้ใน prebuilts/r8/Android.bp)

# Build the R8 keep rules validator host binary
m process-keep-rules

# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>

process-keep-rules เครื่องมือจะแยกวิเคราะห์ไฟล์การกำหนดค่าแต่ละไฟล์และจะล้มเหลวพร้อมการวินิจฉัยไฟล์ และบรรทัดหากพบคำสั่งที่ไม่ได้รับอนุญาตในกฎของไลบรารี ผู้ใช้ คำสั่งที่ไม่อนุญาต ได้แก่ การเพิ่มประสิทธิภาพทั่วโลก การลดขนาด หรือการปิดใช้การปกปิด (เช่น -dontoptimize หรือ -dontshrink) การ บรรจุแพ็กเกจใหม่และการแก้ไขสิทธิ์เข้าถึง การวินิจฉัยหรือการแมป และ-keepattributes ระดับแอป

ตรวจสอบแผนผังแหล่งที่มาด้วย pgaudit.py

หากต้องการตรวจสอบไดเรกทอรีแหล่งที่มาที่ยังไม่ได้สร้างควบคู่ไปกับเครื่องมือวิเคราะห์ขณะสร้างของ R8 แผนผังแพลตฟอร์ม Android จะมีสคริปต์ pgaudit.py อยู่ภายใต้ build/make/core/proguard/tools/ เครื่องมือวิเคราะห์แบบคงที่นี้จะสแกนไฟล์ Android.bp, proguard.flags และ keep.xml ในที่เก็บเพื่อแจ้งกฎที่ครอบคลุมอยู่แล้ว โดย AAPT2 หรือค่าพื้นฐานของแพลตฟอร์มทั่วโลก ไวลด์การ์ดแบบกว้าง -dontoptimize การลบล้าง และกฎการเก็บเฉพาะการทดสอบ

# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/

# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json

ทีมแพลตฟอร์มและ OEM สามารถใช้ pgaudit.py เพื่อการคัดแยกแหล่งที่มาอย่างรวดเร็ว process-keep-rules เพื่อตรวจสอบกฎของไลบรารีที่ส่งออก และ R8_DUMP_KEEP_RADIUS=true ร่วมกับ KeepRadiusHtmlReportGenerator เพื่อวัด รัศมีการคงอยู่ของคลาส ฟิลด์ และเมธอดที่แน่นอนของทุกกฎในการสร้าง