ระบบบิลด์แพลตฟอร์ม 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 |
ควบคุมการทำ Tree Shaking เพื่อนำคลาส ฟิลด์ และเมธอดที่เข้าถึงไม่ได้ออก ค่าเริ่มต้นคือ 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 |
แสดงรายการเป้าหมายที่ขึ้นอยู่กับการคอมไพล์ DEX โดยตรง (เชื่อมต่อผ่าน instrumentation_for หรือ libs) ซึ่งไบต์โค้ดอ้างอิงไปยังเป้าหมายนี้ R8 จะติดตามและเก็บรักษาโดยอัตโนมัติ (รวมถึงเผยแพร่ไปยังตัวแปร override_android_app และ override_android_test และรองรับไลบรารี Java ที่เป็นคู่กันซึ่งไม่ขึ้นอยู่กับเป้าหมายเมื่อจับคู่กับ .impl แยก)
|
allow_traced_references |
อนุญาตให้เป้าหมายที่ขึ้นอยู่กับการคอมไพล์ DEX โดยตรง (เชื่อมต่อผ่าน instrumentation_for หรือ libs เช่น การทดสอบข้ามที่เก็บหรือการทดสอบของผู้ให้บริการที่แสดงใน trace_references_from ไม่ได้) เลือกใช้การติดตามการอ้างอิงไบต์โค้ดไปยังเป้าหมายนี้โดยตั้งค่า trace_references: true
|
trace_references |
เมื่อตั้งค่าเป็น true ในทรัพยากรที่ขึ้นอยู่กับการคอมไพล์ DEX โดยตรงของเป้าหมายที่มี allow_traced_references: true จะเรียกใช้ R8 tracereferences ในโมดูลนี้และส่งกฎการเก็บรักษาที่สร้างขึ้นไปยังโมดูลเป้าหมาย
|
protect_api_surface |
ส่ง --protect-api-surface ไปยัง R8 เมื่อตั้งค่าใน java_sdk_library ระบบจะสร้างและแนบกฎการเก็บ Metalava API โดยอัตโนมัติในขอบเขต API ที่เปิดใช้ทั้งหมด (สาธารณะ, ระบบ, module-lib, system-server และทดสอบ) ไปยังไลบรารีการติดตั้งใช้งาน
|
ไลบรารีแบบคงที่ (android_library, java_library และการนำเข้า AAR) ยังสามารถตั้งค่า required_downstream_optimization: { enabled: true, optimize: true } ใน Android.bp เพื่อกำหนดให้เป้าหมายการคอมไพล์ DEX ปลายทางที่ลิงก์แบบคงที่ (โดยตรงหรือโดยอ้อม) ต้องเปิดใช้การลดขนาด R8 และการเพิ่มประสิทธิภาพไบต์โค้ด Soong จะเปิดใช้ฟีเจอร์นี้โดยอัตโนมัติใน androidx.compose.runtime_runtime เพื่อให้เป้าหมายที่ลิงก์ Jetpack Compose ได้รับการเพิ่มประสิทธิภาพด้วย R8
เปิดใช้การสร้างความสับสนทุกที่ที่ทำได้
ใน Soong obfuscate จะมีค่าเริ่มต้นเป็น false เพื่อความเข้ากันได้กับเวอร์ชันก่อนหน้า แต่คุณควรตั้งค่า obfuscate: true ในเป้าหมาย android_app และ DEX แบบสแตนด์อโลนอย่างชัดเจนทุกครั้งที่ทำได้ ดังนี้
- ขนาด DEX และ
.vdexที่เล็กลง: การเปลี่ยนชื่อแพ็กเกจ คลาส ฟิลด์ และเมธอดเป็นตัวระบุแบบสั้น (a,b) จะลดขนาดพูลสตริง DEX (string_idsและstring_data_item) และตัวบ่งชี้ประเภท เนื่องจาก Android แมปไฟล์.vdexและ.odexลงในหน่วยความจำของกระบวนการ ตารางสัญลักษณ์ที่เล็กลงจึงลดทั้งขนาดพาร์ติชันระบบและหน่วยความจำที่ใช้ขณะรันไทม์โดยตรง - การเพิ่มประสิทธิภาพทั้งโปรแกรมที่ลึกซึ้งยิ่งขึ้น: การอนุญาตให้ R8 เปลี่ยนชื่อตัวระบุจะช่วยปลดล็อกการผสานคลาส การทำให้แพ็กเกจเป็นแบบแบน และการกรองข้อมูลที่ซ้ำกันออกของคลาสสังเคราะห์ในแพ็กเกจต่างๆ ที่ R8 ต้องข้ามไปเพื่อหลีกเลี่ยงการชนกันของชื่อ
- การแทนที่ด้วยสัญลักษณ์สแต็กเทรซแบบเต็ม: การเปิดใช้การปรับให้ยากต่อการอ่าน (Obfuscation) ใน Soong ไม่ได้ลดความสามารถในการแก้ไขข้อบกพร่อง สำหรับเป้าหมายที่คอมไพล์ด้วย R8 ทุกรายการ Soong จะสร้างไฟล์การแมป
proguard_dictionaryในไดเรกทอรีระดับกลางของโมดูล รวมพจนานุกรมโมดูลทั้งหมดไว้ในอาร์ติแฟกต์proguard-dict.zipของบิลด์ ฝังแฮชการแมป (--map-id-template) ไว้ในส่วนหัว DEX และเขียนแอตทริบิวต์SourceFileของคลาส (--source-file-template) ใหม่เพื่อให้retraceสามารถสร้างสัญลักษณ์สแต็กเทรซได้โดยอัตโนมัติ
เก็บ obfuscate: false (หรือเก็บชื่อ API สาธารณะไว้อย่างชัดเจน) ไว้สำหรับสิ่งต่อไปนี้เท่านั้น
- ไลบรารีที่ใช้ร่วมกันใน bootclasspath หรือ
system_serverclasspath: โมดูล (java_libraryหรือjava_sdk_library) ที่แสดง API ภายนอก ซึ่งลิงก์แบบไดนามิกโดยโมดูลอื่นๆ ขณะรันไทม์ (เช่น เป้าหมายframework.jar,services.jarหรือ<uses-library>) ต้อง เก็บobfuscate: falseไว้หรือรักษา API ของตนไว้อย่างชัดเจนโดยใช้กฎprotect_api_surface: true,@KeepForApiหรือ-keepเพื่อให้ผู้เรียกที่คอมไพล์กับ Stub สามารถแก้ไขชื่อคลาสและสมาชิก ขณะรันไทม์ได้ ในโมดูลjava_sdk_libraryการตั้งค่าprotect_api_surface: trueจะสร้างและแนบกฎการเก็บ Metalava ProGuard โดยอัตโนมัติในขอบเขต API ที่เปิดใช้ทั้งหมด (สาธารณะ, ระบบ, module-lib, system-server และการทดสอบ) ไปยังไลบรารีการติดตั้งใช้งาน เมื่อเปิดใช้การเพิ่มประสิทธิภาพ R8 ทั่วไปในเป้าหมาย bootclasspath ที่อัปเดตไม่ได้ (framework-minus-apex,extและframework-location) เป้าหมายเหล่านี้จะรวมframework-hiddenapi-proguard-rules(สร้างโดยhiddenapi_to_proguard) เพื่อเก็บสมาชิกที่แสดงในไฟล์boot/hiddenapi/*.txtที่ใช้งานอยู่ (ละเว้นhiddenapi-max-target-o.txt)
ก่อนเปิดใช้ obfuscate: true ในแอปพลิเคชัน ให้ตรวจสอบข้อกำหนดเบื้องต้นในการย้ายข้อมูลต่อไปนี้
- ทรัพยากร Dependency ของ APK ทดสอบภายนอก: หาก APK
android_testภายนอกตั้งค่าinstrumentation_for: "MyApp"และเรียกใช้คลาสหรือเมธอดภายในของMyAppโดยตรง การเปลี่ยนชื่อสัญลักษณ์เหล่านั้นจะทำให้เกิดNoSuchMethodErrorหรือNoClassDefFoundErrorณ เวลาที่ทำการทดสอบ กำหนดค่าtrace_references_from(หรือallow_traced_referencesด้วยtrace_references: true) จัดโครงสร้างการทดสอบเป็น APK ที่ใช้เครื่องมือวัดด้วยตนเองซึ่งลิงก์MyApp.implแบบคงที่ หรือใส่คำอธิบายประกอบฮุกการทดสอบด้วย@VisibleForTesting(ดูขั้นตอนที่ 2: ย้ายข้อมูลกฎการเก็บรักษาที่ขับเคลื่อนด้วยการทดสอบ) เพื่อให้สัญลักษณ์ภายในที่การทดสอบต้องการได้รับการเก็บรักษาไว้ขณะที่สร้างความสับสนให้กับส่วนที่เหลือของแอป - การสะท้อนตามสตริงและ JNI: หากโมดูลค้นหาคลาส เมธอด หรือฟิลด์ตามชื่อสตริงลิเทอรัล (
Class.forName,getDeclaredMethodหรือ JNIFindClassและGetMethodID) โดยไม่มีกฎการเก็บรักษาหรือคำอธิบายประกอบ การสร้างความสับสนจะเปลี่ยนชื่อเป้าหมายเหล่านั้นและทำให้การค้นหารันไทม์ใช้งานไม่ได้ (โปรดทราบว่าการสะท้อนที่ไม่มีคำอธิบายประกอบจะล้มเหลวภายใต้shrink: trueและoptimize: trueด้วย) ใส่คำอธิบายประกอบจุดแรกเข้าเหล่านั้นด้วยคำแนะนำในการเก็บคำอธิบายประกอบ (@UsesReflection,@UsedByReflectionหรือ@UsedByNative) เพื่อให้ R8 คงชื่อไว้และสร้างความสับสนให้กับส่วนที่เหลือของโมดูล
ทำตามหลักการไม่มีแฟล็กที่กำหนดเอง
โมดูลแพลตฟอร์ม Android ที่เหมาะสมจะไม่มีไฟล์ proguard.flags หรือ keep.xml ที่กำหนดเอง ในบิลด์แพลตฟอร์ม Android กฎการเก็บที่กำหนดเองส่วนใหญ่จะซ้ำซ้อนกัน ดังนี้
- เกณฑ์พื้นฐานของแพลตฟอร์มและ AAPT2: จุดแรกเข้าส่วนใหญ่จะได้รับการเก็บรักษาไว้โดยอัตโนมัติอยู่แล้วตามเกณฑ์พื้นฐานของแพลตฟอร์มส่วนกลาง (เช่น
@Keep, เมธอด JNInativeและ@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สำหรับสแต็กเทรซ, คำอธิบายประกอบระดับการเข้าถึงรันไทม์ (RuntimeVisible*Annotations), แอตทริบิวต์ExceptionsและAnnotationDefault, เมธอดnative, สมาชิกSerializable, เมธอด@JavascriptInterface, ตัวสร้างThrowable(String), ฟิลด์Parcelable$CREATORและฟิลด์ protobufMessageLitebuild/make/core/proguard/kotlin.flags: ปิดเสียงคำเตือนที่ไม่เป็นอันตรายสำหรับคำอธิบายประกอบเมตา Kotlin ที่เฉพาะเจาะจง (kotlin.Metadataและkotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) และตัดคำอธิบายประกอบ KotlinDebugMetadataในบิลด์ที่เผยแพร่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 ของเวอร์ชันที่ใช้งานจริงที่กำหนดเองโดยใช้รูปแบบใดรูปแบบหนึ่งต่อไปนี้
ติดตามการอ้างอิงจากการทดสอบหรือเป้าหมายที่ใช้ร่วมกัน (
trace_references_fromหรือallow_traced_references): เมื่อทั้งเป้าหมายและโมดูลที่ขึ้นต่อกันโดยตรงคอมไพล์ DEX ของตนเอง (android_app,android_testหรือjava_libraryที่ใช้ DEX) และเชื่อมต่อผ่านinstrumentation_forหรือlibsSoong จะเรียกใช้ R8tracereferencesในโมดูลที่ขึ้นต่อกันและป้อนกฎการเก็บที่สร้างขึ้นลงในการคอมไพล์ R8 ของเป้าหมายได้โดยไม่ต้องสร้างวงจรการขึ้นต่อกันหรือต้องแยกไลบรารี.implด้วยตนเองการทดสอบที่เก็บเดียวกัน (
trace_references_from): แสดงรายการทรัพยากรที่ขึ้นต่อกันโดยตรงของinstrumentation_forหรือlibsในโมดูลเป้าหมาย (ซึ่งจะเผยแพร่โดยอัตโนมัติในตัวแปรoverride_android_appและoverride_android_testและยังคงรองรับไลบรารี Java ที่ใช้ร่วมกันซึ่งไม่ขึ้นต่อกันเมื่อจับคู่กับการแยก.impl):android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { obfuscate: true, shrink_resources: true, trace_references_from: ["MyAppTests"], }, } android_test { name: "MyAppTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], }การทดสอบข้ามที่เก็บหรือผู้ให้บริการ (
allow_traced_referencesและtrace_references): เมื่อโมดูลเป้าหมายไม่สามารถแสดงรายการทรัพยากรที่ต้องพึ่งพาในtrace_references_fromโดยตรง (เช่น การทดสอบในที่เก็บหรือโครงสร้างผู้ให้บริการอื่น) ให้ตั้งค่าallow_traced_references: trueในโมดูลเป้าหมายและเลือกใช้จากทรัพยากรที่ต้องพึ่งพาแต่ละรายการด้วยtrace_references: true:android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { allow_traced_references: true, }, } android_test { name: "MyAppVendorTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], optimize: { trace_references: true, }, }การสะท้อนตามสตริงในการทดสอบ:
tracereferencesจะตรวจสอบการอ้างอิงพูลค่าคงที่ของไบต์โค้ดและไม่สามารถตรวจจับการสะท้อนตามสตริงในการทดสอบ (Class.forName,getDeclaredField,getDeclaredMethodหรือตัวช่วยการแทรกฟิลด์แบบสะท้อน) ใส่คำอธิบายประกอบฟิลด์และเมธอดเวอร์ชันที่ใช้งานจริงที่เข้าถึงแบบสะท้อนด้วย@VisibleForTesting(หรือ@UsedByReflection)
ลิงก์ไลบรารีการติดตั้งใช้งานเข้ากับการทดสอบ (
static_libs): หากคุณไม่ต้องการให้มีจุดแรกเข้าสำหรับการทดสอบเท่านั้นคงอยู่ใน APK เวอร์ชันที่ใช้งานจริง และไม่ต้องการให้การเปลี่ยนแปลงการทดสอบทริกเกอร์ R8 อีกครั้งในเป้าหมายเวอร์ชันที่ใช้งานจริง ให้วางไฟล์ต้นฉบับของแอปไว้ใน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ลดขนาดและสร้างความสับสนให้กับจุดแรกเข้าของเวอร์ชันที่ใช้งานจริงเท่านั้นใส่คำอธิบายประกอบฮุกการทดสอบด้วย
@VisibleForTesting: หาก APK ทดสอบภายนอกเรียกใช้เมธอดหรือคอนสตรัคเตอร์ภายในเพียงไม่กี่รายการในเป้าหมายandroid_appคุณสามารถใส่คำอธิบายประกอบการประกาศเหล่านั้นด้วย@VisibleForTestingเกณฑ์พื้นฐานbuild/make/core/proguard.flagsทั่วโลกจะเก็บรักษา@VisibleForTestingรายการในแพ็กเกจandroid.**,com.android.**และcom.google.android.**โดยอัตโนมัติโดยไม่ต้องใช้ไฟล์.flagsที่กำหนดเอง (สำหรับโมดูลของผู้ให้บริการที่อยู่นอกเนมสเปซเหล่านี้ ให้ใช้trace_references_from,@UsedByReflectionหรือกฎไลบรารีที่ส่งออก) เมื่อใส่คำอธิบายประกอบด้วยตนเองด้วย@VisibleForTestingแทนการใช้trace_references_fromหรือtrace_referencesให้ใส่คำอธิบายประกอบคลาสที่ส่งไปยัง Mockitomock(...)หรือspy(...)ด้วย (ไม่ใช่แค่เมธอดที่ถูก Stub) เนื่องจากmockito-target(dexmaker-mockito) ในอุปกรณ์จะสร้างคลาสย่อยรันไทม์ และ R8 สามารถทำเครื่องหมายคลาสที่ไม่มีคลาสย่อยเป็นfinal(MockitoException: Can't mock/spy class ... - final class)หลีกเลี่ยงหรือจัดแพ็กเกจไลบรารีแบบคงที่ที่ทับซ้อนกันใหม่ (
jarjar_rules): เมื่อทั้งandroid_appและ APK ทดสอบinstrumentation_forลิงก์ไลบรารีเดียวกันแบบคงที่ (โดยตรง เช่นguavaหรือแบบทรานซิทีฟ)tracereferencesจะเห็นคลาสเหล่านั้นที่กำหนดไว้ในการทดสอบและไม่แสดงกฎการเก็บรักษาสำหรับแอป และในรันไทม์ ClassLoader ระดับบนของแอปจะบดบังสำเนาของการทดสอบด้วยคลาสที่ลดขนาด R8 ซึ่งทำให้เกิดIllegalAccessErrorหรือNoSuchMethodErrorหากการทดสอบไม่จำเป็นต้องมีสำเนาที่รวมมาด้วย ให้ลบรายการที่ซ้ำกันออกจากstatic_libsของการทดสอบ (หรือลิงก์ผ่านlibs) เพื่อหลีกเลี่ยงการทับซ้อนกัน มิฉะนั้น ให้จัดแพ็กเกจใหม่สำหรับแพ็กเกจไลบรารีแบบคงที่ที่ทับซ้อนกันของแอปภายใต้คำนำหน้าที่เจาะจงโมดูลโดยใช้jarjar_rules
ขั้นตอนที่ 3: ส่งออกกฎจากคลังที่เป็นเจ้าของ
หาก java_library หรือ android_library ที่แชร์ต้องใช้กฎการเก็บรักษาสำหรับการสะท้อนภายในหรือ Callback ของ 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 ด้วยคำอธิบายประกอบแหล่งที่มาจากคำแนะนำในการเก็บคำอธิบายประกอบ (ดูข้อมูลอ้างอิง 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: วางบนเมธอดหรือฟิลด์ที่เข้าถึงจากโค้ด C หรือ C++ JNI โดยใช้GetMethodID,GetStaticMethodIDหรือGetFieldID@KeepForApi: วางบนคลาสหรือสมาชิก API ของไลบรารีที่ต้องคงสภาพเดิมเมื่อไลบรารีถูกย่อขนาดก่อนการเผยแพร่@Keep(androidx.annotation.Keep): ใช้เป็นตัวเลือกสำรองเมื่อใช้keepannoไม่ได้ การใช้@Keepกับชั้นเรียนจะเก็บรักษาชั้นเรียนและสมาชิกทั้งหมดไว้โดยไม่มีเงื่อนไข การใช้@Keepกับเมธอดหรือฟิลด์จะทำหน้าที่เป็นจุดแรกเข้าแบบไม่มีเงื่อนไขซึ่งจะเก็บสมาชิกและคลาสที่มีสมาชิกนั้นไว้แม้ว่าจะไม่มีการสร้างอินสแตนซ์ของคลาสก็ตาม ส่วนkeepannoจะแสดงความสามารถในการเข้าถึงแบบมีเงื่อนไข
วิธีใช้คำอธิบายประกอบ R8 keepanno ในโมดูล Soong
เพิ่ม
"keepanno-annotations"ไปยังlibsในAndroid.bp:libs: [ "keepanno-annotations", ],นำเข้า
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 สุดท้าย โดยไม่เพิ่มค่าใช้จ่ายเพิ่มเติมสำหรับไบต์โค้ดรันไทม์
หลีกเลี่ยงข้อผิดพลาดที่พบบ่อย
ส่วนต่อไปนี้จะอธิบายข้อผิดพลาดที่พบบ่อยเกี่ยวกับ proguard.flags และ keep.xml ในโค้ดแพลตฟอร์มและวิธีแก้ไข
กฎการเก็บรักษาองค์ประกอบแบบกว้าง
# 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ทุกรายการที่พบใน classpath รวมถึงคอมโพเนนต์ไลบรารีที่ไม่ได้ใช้และกิจกรรมการแก้ไขข้อบกพร่องที่ปิดใช้ - วิธีแก้ไขที่แนะนำ: ลบกฎ 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 เก็บ Getter และ Setter ทุกรายการในคลาสย่อย
Viewทุกคลาสในแอปและไลบรารีที่ลิงก์ทั้งหมด (รวมถึงไลบรารี AndroidX และ Material) ซึ่งจะบล็อกการนำเมธอดที่ไม่ได้ใช้งานออกและการแทรกในโค้ด UI - วิธีแก้ไขที่แนะนำ: ลบกฎ AAPT2 จะเก็บรักษาคอนสตรัคเตอร์สำหรับคลาส
Viewที่กำหนดเองซึ่งขยายจากไฟล์ XML ของเลย์เอาต์อยู่แล้ว หากโค้ดทำให้พร็อพเพอร์ตี้มุมมองเคลื่อนไหวโดยใช้ชื่อสตริงแบบสะท้อน เช่น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) ในเป้าหมาย Leaf
การแจ้งว่าไม่เหมาะสมเพื่อการวินิจฉัยในกฎที่คอมมิต
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- เหตุใดจึงส่งผลเสียต่อบิลด์: แฟล็กการวินิจฉัยจะทำให้บันทึกบิลด์เต็มหรือพยายามเขียนไฟล์เอาต์พุตไปยังเส้นทางในเครื่องระหว่างบิลด์ Soong ที่ทำในแซนด์บ็อกซ์
- วิธีแก้ไขที่แนะนำ: ลบแฟล็กเหล่านี้ออกจากไฟล์
.flagsที่คอมมิตแล้ว Soong จะเขียนเอาต์พุตการแมปและการใช้งาน R8 เช่นproguard_dictionaryและproguard_usage.zipไปยังไดเรกทอรีกลางของโมดูลในout/soong/.intermediates/โดยอัตโนมัติ
กฎการเก็บรักษาเวอร์ชันที่ใช้งานจริงสำหรับโค้ดทดสอบ
- เหตุใดจึงส่งผลเสียต่อประสิทธิภาพ: การเพิ่มกฎ
-keepที่กำหนดเองเพื่อการเข้าถึงการทดสอบเพียงอย่างเดียวจะบังคับให้โค้ดนั้นยังคงไม่ถูกสร้างความสับสนและเก็บไว้ในบิลด์เวอร์ชันที่ใช้งานจริง แม้ว่าการกำหนดค่าtrace_references_from(หรือallow_traced_referencesด้วยtrace_references: true) หรือการใส่คำอธิบายประกอบเมธอดด้วย@VisibleForTestingจะช่วยหลีกเลี่ยงการคงกฎ-keepด้วยตนเองและเก็บเฉพาะสิ่งที่การทดสอบอ้างอิงไว้ แต่สัญลักษณ์เหล่านั้นจะยังคงอยู่ในไบนารีเวอร์ชันที่ใช้งานจริง - การแก้ไขที่แนะนำ: กำจัดกฎ
-keepของการทดสอบที่กำหนดเองโดยเปิดใช้trace_references_from(หรือallow_traced_referencesและtrace_references: true) หรือจัดโครงสร้างแอปเป็นไลบรารี.implที่ลิงก์กับ APK ทดสอบที่วัดคุมด้วยตนเองโดยใช้static_libsเพื่อให้จุดแรกเข้าสำหรับการทดสอบเท่านั้นอยู่นอก APK เวอร์ชันที่ใช้งานจริงโดยสมบูรณ์ ตามที่อธิบายไว้ในขั้นตอนที่ 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เวลาคอมไพล์หรือแมปค่าคงที่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
ให้ตรวจสอบว่าโมดูลสร้างขึ้นอย่างสมบูรณ์ ผ่านการทดสอบหน่วยและการทดสอบการวัดคุม
และคงจุดแรกเข้าที่จำเป็นทั้งหมดไว้
สร้างและทดสอบโมดูล
สร้างโมดูลใหม่ทั้งหมดเพื่อยืนยันว่า R8 และ AAPT2 ทำงานเสร็จสมบูรณ์โดยไม่มีคำเตือนเกี่ยวกับการอ้างอิงที่ขาดหายไป
m <MODULE_NAME>เรียกใช้การทดสอบหน่วยและการวัดคุมสำหรับโมดูลโดยใช้
atest:atest <TEST_MODULE_NAME>สำหรับแอประบบ บริการที่มีสิทธิ์ หรือโมดูลที่ขึ้นอยู่กับฮาร์ดแวร์ ให้เรียกใช้การทดสอบการวัดคุมและ 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เมื่อเรียกใช้
prebuilts/r8/r8.jarโดยตรงในclasses.jarก่อน R8 ของโมดูลเพื่อยืนยันการเปลี่ยนแปลงแฟล็กแบบออฟไลน์โดยไม่ต้องสร้างแพลตฟอร์มแบบเต็ม ให้ส่งทั้ง JAR คลาสพาธ--libของโมดูล (android.jar,framework.jarและlibsที่แชร์) และกฎproguard.optionsที่สร้างขึ้นของ AAPT2 จากAndroidManifest.xmlและ XML เลย์เอาต์ หากไม่มีอินพุตเหล่านั้น R8 จะไม่สามารถแก้ไขซูเปอร์ไทป์ของเฟรมเวิร์ก (Activity,Service,Phone,Handler) และเก็บรักษาเมธอดลำดับชั้นไว้ไม่เพียงพอ
วิเคราะห์รัศมีการเก็บรักษาและการรวมกฎด้วย R8
คอมไพเลอร์ R8 แบบโอเพนซอร์สมีเครื่องมือวิเคราะห์ Keep Radius (ซึ่งแสดงใน Android Studio และ Gradle เป็นเครื่องมือวิเคราะห์การกำหนดค่า R8 ด้วย) ที่วัดผลกระทบที่แน่นอนของกฎการเก็บรักษาทุกข้อในระหว่างการคอมไพล์ Soong จะผสานรวมเครื่องมือวิเคราะห์นี้เข้ากับบิลด์แพลตฟอร์ม Android โดยตรง (กำหนดค่าใน build/soong/java/dex.go)
วิธีวิเคราะห์กฎการเก็บรักษาสำหรับโมดูลแพลตฟอร์มหรือทั้งบิลด์มีดังนี้
เรียกใช้บิลด์ด้วยตัวแปรสภาพแวดล้อม
R8_DUMP_KEEP_RADIUS=trueดังนี้R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>เมื่อตั้งค่า
R8_DUMP_KEEP_RADIUS=trueแล้ว Soong จะสั่งให้ R8 บันทึกเมตริกกฎการเก็บไว้ในไฟล์r8keepradius.pbระดับกลางสำหรับแต่ละโมดูลที่คอมไพล์ภายใต้out/soong/.intermediates/สำหรับกฎการเก็บรักษาและคำอธิบายประกอบkeepannoแต่ละรายการ R8 จะบันทึกข้อมูลต่อไปนี้- รัศมีการเก็บรักษาทันที: คลาส ฟิลด์ และเมธอดที่แน่นอนซึ่งเก็บรักษาไว้โดยกฎ พร้อมกับข้อจำกัดเฉพาะที่บังคับใช้กับการลดขนาด การเพิ่มประสิทธิภาพ หรือการสร้างความสับสน
- การรวมกฎ: กฎการเก็บรักษาหรือคำอธิบายประกอบอื่นๆ ที่เก็บรายการเดียวกันไว้แล้ว หากกฎที่กำหนดเองถูกรวมไว้ในกฎ AAPT2 หรือกฎพื้นฐานส่วนกลางอย่างสมบูรณ์ คุณจะลบกฎดังกล่าวได้อย่างปลอดภัย
- กฎทั่วทั้งแพ็กเกจและกฎส่วนกลาง: กฎที่ใช้ไวลด์การ์ดทั่วทั้งแพ็กเกจแบบกว้างหรือใช้คำสั่งการกำหนดค่าส่วนกลาง
แปลงเอาต์พุต
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 และ Inner-loop เพิ่มเติมใน build/soong/java/dex.go ดังนี้
R8_DUMP_INPUT=true: เขียนr8inputs.zipไปยังไดเรกทอรีระดับกลางของโมดูลที่มี JAR อินพุต, JAR ไลบรารี และการกำหนดค่า ProGuard ที่ผสานรวมทั้งหมดสำหรับการจำลอง R8 แบบสแตนด์อโลนR8_DUMP_PERFETTO_TRACE=true: เขียนr8trace.ptraceไปยังไดเรกทอรีระดับกลางของโมดูลเพื่อตรวจสอบการคอมไพล์ R8 ที่ผ่านใน PerfettoSOONG_SKIP_TRACE_REFERENCES=true: ข้ามการเรียกใช้กฎtracereferencesที่กำหนดค่าโดยtrace_references_fromหรือtrace_referencesเพื่อเร่งความเร็วของบิลด์ Inner Loop ภายในเครื่องเมื่อทำซ้ำในโค้ดทดสอบ
ตรวจสอบกฎผู้ใช้ไลบรารี
เมื่อ java_library หรือ android_library ส่งออกกฎการเก็บรักษาไปยังแอปผู้บริโภคปลายน้ำ (export_proguard_flags_files: true) กฎเหล่านั้นต้องไม่มีแฟล็กส่วนกลางที่แก้ไขหรือปิดใช้การเพิ่มประสิทธิภาพสำหรับแอปที่ใช้
R8 มีเครื่องมือวิเคราะห์กฎการเก็บรักษาแบบโอเพนซอร์ส (ProcessKeepRules) ซึ่งแสดงในบิลด์แพลตฟอร์ม Android เป็นเครื่องมือโฮสต์ 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 เพื่อวัดรัศมีการเก็บรักษาคลาส ฟิลด์ และเมธอดที่แน่นอนของทุกกฎในบิลด์