เพิ่มกฎการเก็บรักษา

ในระดับสูง กฎการเก็บจะระบุคลาส (หรือคลาสย่อยหรือการใช้งาน) จากนั้นก็ระบุสมาชิก ซึ่งได้แก่ เมธอด ตัวสร้าง หรือฟิลด์ภายในคลาสนั้นเพื่อเก็บไว้

ไวยากรณ์ทั่วไปสำหรับกฎการเก็บรักษามีดังนี้


-<keep_option>[,<keep_option_modifier_1>,<keep_option_modifier_2>,...] <class_specification>

ตัวอย่างต่อไปนี้คือกฎการเก็บที่ใช้ keepclassmembers เป็นตัวเลือกการเก็บ allowoptimization เป็นตัวแก้ไข และเก็บ someSpecificMethod() จาก com.example.MyClass

-keepclassmembers,allowoptimization class com.example.MyClass {
  void someSpecificMethod();
}

ตัวเลือกการเก็บ

ตัวเลือกการเก็บคือส่วนแรกของกฎการเก็บ โดยจะระบุลักษณะของคลาสที่จะเก็บไว้ โดยมีตัวเลือกการเก็บ 6 แบบ ได้แก่ keep, keepclassmembers, keepclasseswithmembers, keepnames, keepclassmembernames, keepclasseswithmembernames

ตารางต่อไปนี้จะอธิบายตัวเลือกการเก็บรักษาเหล่านี้

ตัวเลือก Keep คำอธิบาย
keepclassmembers เก็บเฉพาะสมาชิกที่ระบุไว้หากชั้นเรียนยังคงมีอยู่หลังจากการเพิ่มประสิทธิภาพ
keep คงคลาสที่ระบุและสมาชิกที่ระบุ (ฟิลด์และเมธอด) ไว้เพื่อป้องกันไม่ให้มีการเพิ่มประสิทธิภาพ

หมายเหตุ: โดยทั่วไปแล้ว keep ควรใช้กับตัวแก้ไขตัวเลือกการคงค่าเท่านั้น เนื่องจาก keep เพียงอย่างเดียวจะป้องกันไม่ให้มีการเพิ่มประสิทธิภาพใดๆ ในคลาสที่ตรงกัน
keepclasseswithmembers เก็บรักษาชั้นเรียนและสมาชิกที่ระบุไว้เฉพาะในกรณีที่ชั้นเรียนมีสมาชิกทั้งหมดจากข้อกำหนดของชั้นเรียน
keepclassmembernames ป้องกันไม่ให้เปลี่ยนชื่อสมาชิกในชั้นเรียนที่ระบุ แต่ไม่ได้ป้องกันไม่ให้นำชั้นเรียนหรือสมาชิกออก

หมายเหตุ: ผู้ใช้มักเข้าใจความหมายของตัวเลือกนี้ผิด ให้ลองใช้ -keepclassmembers,allowshrinking ที่เทียบเท่าแทน
keepnames ป้องกันการเปลี่ยนชื่อชั้นเรียนและสมาชิก แต่ไม่ได้ป้องกันไม่ให้ระบบนำชั้นเรียนและสมาชิกออกทั้งหมดหากเห็นว่าไม่ได้ใช้งาน

หมายเหตุ: ผู้ใช้มักเข้าใจความหมายของตัวเลือกนี้ผิด ให้ลองใช้ -keep,allowshrinking ที่เทียบเท่าแทน
keepclasseswithmembernames ป้องกันการเปลี่ยนชื่อชั้นเรียนและสมาชิกที่ระบุ แต่จะทำได้ก็ต่อเมื่อสมาชิกอยู่ในโค้ดสุดท้าย แต่ไม่ได้ป้องกันการนำโค้ดออก

หมายเหตุ: มักจะมีการเข้าใจความหมายของตัวเลือกนี้ผิด ให้ลองใช้ตัวเลือก -keepclasseswithmembers,allowshrinking ที่เทียบเท่าแทน

เลือกตัวเลือกการเก็บที่เหมาะสม

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

ตัวเลือก Keep ลดขนาดคลาส ปิดบังคลาส ลดขนาดสมาชิก ซ่อนสมาชิก
keep
keepclassmembers
keepclasseswithmembers
keepnames
keepclassmembernames
keepclasseswithmembernames

ตัวปรับแต่งตัวเลือก Keep

ตัวแก้ไขตัวเลือกการเก็บจะใช้เพื่อควบคุมขอบเขตและลักษณะการทำงานของกฎการเก็บ คุณสามารถเพิ่มตัวแก้ไขตัวเลือก keep 0 รายการขึ้นไปลงในกฎการเก็บรักษา

ค่าที่เป็นไปได้สำหรับตัวแก้ไขตัวเลือกการเก็บจะอธิบายไว้ในตารางต่อไปนี้

ค่านิยม คำอธิบาย
allowoptimization อนุญาตให้เพิ่มประสิทธิภาพองค์ประกอบที่ระบุ อย่างไรก็ตาม ระบบจะไม่เปลี่ยนชื่อหรือนำองค์ประกอบที่ระบุออก
allowobfucastion อนุญาตให้เปลี่ยนชื่อองค์ประกอบที่ระบุ อย่างไรก็ตาม จะไม่มีการนำองค์ประกอบออกหรือเพิ่มประสิทธิภาพองค์ประกอบ
allowshrinking อนุญาตให้นำองค์ประกอบที่ระบุออกได้หาก R8 ไม่พบการอ้างอิงถึงองค์ประกอบเหล่านั้น อย่างไรก็ตาม ระบบจะไม่เปลี่ยนชื่อหรือเพิ่มประสิทธิภาพองค์ประกอบ
includedescriptorclasses สั่งให้ R8 เก็บรักษาคลาสทั้งหมดที่ปรากฏในตัวอธิบายของเมธอด (ประเภทพารามิเตอร์และประเภทการคืนค่า) และฟิลด์ (ประเภทฟิลด์) ที่จะเก็บไว้
allowaccessmodification อนุญาตให้ R8 เปลี่ยน (โดยปกติคือขยาย) ตัวแก้ไขการเข้าถึง (public, private, protected) ของคลาส เมธอด และฟิลด์ในระหว่างกระบวนการเพิ่มประสิทธิภาพ
allowrepackage อนุญาตให้ R8 ย้ายคลาสไปยังแพ็กเกจต่างๆ รวมถึงแพ็กเกจเริ่มต้น (รูท)

ข้อกำหนดของชั้นเรียน

คุณต้องระบุคลาส (รวมถึงอินเทอร์เฟซ enum และคลาสคำอธิบายประกอบ) ในกฎการเก็บทุกรายการ คุณอาจจำกัดกฎตามคำอธิบายประกอบได้โดย ระบุคลาสหลักหรืออินเทอร์เฟซที่ใช้ หรือโดยระบุตัวแก้ไขการเข้าถึง สำหรับคลาส ต้องระบุคลาสทั้งหมด รวมถึงคลาสจากเนมสเปซ java.lang เช่น java.lang.String โดยใช้ชื่อ Java ที่สมบูรณ์ หากต้องการทำความเข้าใจชื่อที่ควรใช้ ให้ตรวจสอบไบต์โค้ดโดยใช้เครื่องมือที่อธิบายไว้ในตรวจสอบชื่อ Java ที่สร้างขึ้น

ตัวอย่างต่อไปนี้แสดงวิธีระบุคลาส MaterialButton

  • ถูกต้อง: com.google.android.material.button.MaterialButton
  • ไม่ถูกต้อง: MaterialButton

นอกจากนี้ ข้อมูลจำเพาะของคลาสยังระบุสมาชิกภายในคลาสที่ควร เก็บไว้ด้วย ตัวอย่างเช่น กฎต่อไปนี้จะเก็บคลาส MyClass และเมธอด someSpecificMethod() ไว้

-keep class com.example.MyClass {
  void someSpecificMethod();
}

ระบุคลาสตามคำอธิบายประกอบ

หากต้องการระบุคลาสตามคำอธิบายประกอบ ให้เติมคำนำหน้าชื่อ Java ที่มีคุณสมบัติครบถ้วนของคำอธิบายประกอบด้วยสัญลักษณ์ @ เช่น

-keep class @com.example.MyAnnotation com.example.MyClass

หากกฎการเก็บมีคำอธิบายประกอบมากกว่า 1 รายการ กฎจะเก็บคลาสที่มีคำอธิบายประกอบทั้งหมดที่ระบุไว้ คุณระบุคำอธิบายประกอบได้หลายรายการ แต่กฎจะมีผลก็ต่อเมื่อคลาสมีคำอธิบายประกอบทุกรายการที่ระบุไว้ ตัวอย่างเช่น กฎต่อไปนี้ จะเก็บคลาสทั้งหมดที่อธิบายประกอบโดยทั้ง Annotation1 และ Annotation2

-keep class @com.example.Annotation1 @com.example.Annotation2 *

ระบุคลาสย่อยและการใช้งาน

หากต้องการกำหนดเป้าหมายไปยังคลาสย่อยหรือคลาสที่ใช้การติดตั้งอินเทอร์เฟซ ให้ใช้ extend และ implements ตามลำดับ

ตัวอย่างเช่น หากคุณมีคลาส Bar ที่มีคลาสย่อย Foo ดังนี้

class Foo : Bar()

กฎการเก็บรักษาต่อไปนี้จะเก็บรักษาสับคลาสทั้งหมดของ Bar โปรดทราบว่า กฎ keep ไม่รวมคลาสหลัก Bar เอง

-keep class * extends Bar

หากคุณมีคลาส Foo ที่ใช้คลาส Bar ให้ทำดังนี้

class Foo : Bar

กฎการเก็บรักษาต่อไปนี้จะเก็บรักษาคลาสทั้งหมดที่ใช้ Bar โปรดทราบว่า กฎการเก็บรักษาไม่ได้รวมอินเทอร์เฟซ Bar เอง

-keep class * implements Bar

ระบุคลาสตามตัวแก้ไขการเข้าถึง

คุณระบุตัวแก้ไขการเข้าถึง เช่น public, private, static และ final เพื่อให้กฎการเก็บรักษาแม่นยำยิ่งขึ้นได้

ตัวอย่างเช่น กฎต่อไปนี้จะเก็บคลาส public ทั้งหมดไว้ในแพ็กเกจ api และแพ็กเกจย่อย รวมถึงสมาชิกที่เป็นแบบสาธารณะและได้รับการปกป้องทั้งหมดในคลาสเหล่านี้

-keep public class com.example.api.** { public protected *; }

นอกจากนี้ คุณยังใช้ตัวแก้ไขสำหรับสมาชิกในชั้นเรียนได้ด้วย ตัวอย่างเช่น กฎต่อไปนี้จะเก็บเฉพาะpublic staticเมธอดของคลาส Utils

-keep class com.example.Utils {
  public static void *(...);
}

ตัวแก้ไขเฉพาะ Kotlin

R8 ไม่รองรับตัวแก้ไขเฉพาะของ Kotlin เช่น internal และ suspend โปรดใช้หลักเกณฑ์ต่อไปนี้เพื่อเก็บช่องดังกล่าว

  • หากต้องการเก็บคลาส เมธอด หรือฟิลด์ internal ไว้ ให้ถือว่าเป็นแบบสาธารณะ ตัวอย่างเช่น ลองพิจารณาแหล่งที่มาของ Kotlin ต่อไปนี้

    package com.example
    internal class ImportantInternalClass {
      internal val f: Int
      internal fun m() {}
    }
    

    คลาส เมธอด และฟิลด์ internal จะเป็น public ใน.class ไฟล์ที่คอมไพเลอร์ Kotlin สร้างขึ้น ดังนั้นคุณต้องใช้คีย์เวิร์ด public ตามที่แสดงในตัวอย่างต่อไปนี้

    -keepclassmembers public class com.example.ImportantInternalClass {
      public int f;
      public void m();
    }
    
  • เมื่อคอมไพล์สมาชิก suspend ให้จับคู่ลายเซ็นที่คอมไพล์แล้วใน กฎการเก็บ

    เช่น หากคุณมีฟังก์ชัน fetchUser ที่กำหนดไว้ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

    suspend fun fetchUser(id: String): User
    

    เมื่อคอมไพล์แล้ว ลายเซ็นในไบต์โค้ดจะมีลักษณะดังนี้

    public final Object fetchUser(String id, Continuation<? super User> continuation);
    

    หากต้องการเขียนกฎการเก็บรักษาสำหรับฟังก์ชันนี้ คุณต้องจับคู่ลายเซ็นที่คอมไพล์นี้ หรือใช้ ...

    ตัวอย่างการใช้ลายเซ็นที่คอมไพล์แล้วมีดังนี้

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(java.lang.String,  kotlin.coroutines.Continuation);
    }
    

    ตัวอย่างการใช้ ... มีดังนี้

    -keepclassmembers class com.example.repository.UserRepository {
      public java.lang.Object fetchUser(...);
    }
    

ข้อกำหนดของสมาชิก

โดยคุณจะระบุสมาชิกในชั้นเรียนที่จะเก็บไว้หรือไม่ก็ได้ หากคุณระบุสมาชิกอย่างน้อย 1 คนสำหรับชั้นเรียน กฎนี้จะไม่มีผลกับสมาชิกคนอื่นๆ

ระบุสมาชิกตามคำอธิบายประกอบ

คุณระบุสมาชิกได้ตามคำอธิบายประกอบของสมาชิก เช่นเดียวกับคลาส คุณ ต้องใส่คำนำหน้าชื่อ Java ที่มีคุณสมบัติครบถ้วนของคำอธิบายประกอบด้วย @ ซึ่งจะช่วยให้คุณ เก็บเฉพาะสมาชิกในคลาสที่มีเครื่องหมาย คำอธิบายประกอบที่เฉพาะเจาะจงไว้เท่านั้น เช่น หากต้องการคงคำอธิบายประกอบของเมธอดและฟิลด์ด้วย @com.example.MyAnnotation ให้ทำดังนี้

-keep class com.example.MyClass {
  @com.example.MyAnnotation <methods>;
  @com.example.MyAnnotation <fields>;
}

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

-keep class @com.example.ClassAnnotation * {
  @com.example.MethodAnnotation <methods>;
  @com.example.FieldAnnotation <fields>;
}

ซึ่งจะทำให้คลาสที่ใส่คำอธิบายประกอบด้วย @ClassAnnotation และในคลาสเหล่านั้น จะทำให้เมธอดที่ใส่คำอธิบายประกอบด้วย @MethodAnnotation และฟิลด์ที่ใส่คำอธิบายประกอบด้วย @FieldAnnotation

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

วิธีการ

ไวยากรณ์สำหรับการระบุวิธีการในการระบุสมาชิกสำหรับกฎการเก็บมีดังนี้

[<access_modifier>] [<return_type>] <method_name>(<parameter_types>);

ตัวอย่างเช่น กฎการเก็บรักษาต่อไปนี้จะเก็บเมธอดสาธารณะที่ชื่อ setLabel() ซึ่งแสดงผลเป็น void และรับ String

-keep class com.example.MyView {
    public void setLabel(java.lang.String);
}

คุณใช้ <methods> เป็นแป้นพิมพ์ลัดเพื่อจับคู่เมธอดทั้งหมดในคลาสได้ดังนี้

-keep class com.example.MyView {
    <methods>;
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีระบุประเภทสำหรับประเภทการคืนค่าและประเภทพารามิเตอร์ได้ที่ประเภท

ผู้ผลิต

หากต้องการระบุตัวสร้าง ให้ใช้ <init> ไวยากรณ์สำหรับการระบุตัวสร้าง ในการระบุสมาชิกสำหรับกฎการเก็บมีดังนี้

[<access_modifier>] <init>(parameter_types);

ตัวอย่างเช่น กฎการเก็บรักษาต่อไปนี้จะเก็บตัวสร้าง View ที่กำหนดเองซึ่ง รับ Context และ AttributeSet

-keep class com.example.ui.MyCustomView {
    public <init>(android.content.Context, android.util.AttributeSet);
}

หากต้องการเก็บตัวสร้างสาธารณะทั้งหมด ให้ใช้ตัวอย่างต่อไปนี้เป็นข้อมูลอ้างอิง

-keep class com.example.ui.MyCustomView {
    public <init>(...);
}

ช่อง

ไวยากรณ์สำหรับการระบุฟิลด์ในข้อกำหนดของสมาชิกสำหรับกฎการเก็บมีลักษณะดังนี้

[<access_modifier>...] [<type>] <field_name>;

เช่น กฎ keep ต่อไปนี้จะเก็บฟิลด์สตริงส่วนตัวที่ชื่อ userId และฟิลด์จำนวนเต็มแบบคงที่สาธารณะที่ชื่อ STATUS_ACTIVE

-keep class com.example.models.User {
    private java.lang.String userId;
    public static int STATUS_ACTIVE;
}

คุณใช้ <fields> เป็นทางลัดเพื่อจับคู่ช่องทั้งหมดในชั้นเรียนได้ดังนี้

-keep class com.example.models.User {
    <fields>;
}

ประเภท

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

ประเภทดั้งเดิม

หากต้องการระบุประเภทดั้งเดิม ให้ใช้คีย์เวิร์ด Java ของประเภทนั้น R8 จะรู้จักประเภทข้อมูลพื้นฐานต่อไปนี้ boolean, byte, short, char, int, long, float, double

ตัวอย่างกฎที่มีประเภทดั้งเดิมมีดังนี้

# Keeps a method that takes an int and a float as parameters.
-keepclassmembers class com.example.Calculator {
    public void setValues(int, float);
}

ประเภททั่วไป

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

ตัวอย่างเช่น หากคุณมีโค้ดต่อไปนี้ที่มีประเภททั่วไปแบบไม่จำกัด ซึ่งกำหนดไว้ใน Box.kt

package com.myapp.data

class Box<T>(val item: T) {
    fun getItem(): T {
        return item
    }
}

หลังจากลบประเภทแล้ว T จะถูกแทนที่ด้วย Object หากต้องการเก็บตัวสร้างและเมธอดของคลาส กฎของคุณต้องใช้ java.lang.Object แทน T ทั่วไป

ตัวอย่างกฎการเก็บมีดังนี้

# Keep the constructor and methods of the Box class.
-keep class com.myapp.data.Box {
    public init(java.lang.Object);
    public java.lang.Object getItem();
}

หากคุณมีโค้ดต่อไปนี้ที่มีประเภททั่วไปที่จำกัดใน NumberBox.kt

package com.myapp.data

// T is constrained to be a subtype of Number
class NumberBox<T : Number>(val number: T)

ในกรณีนี้ การลบประเภทจะแทนที่ T ด้วยขอบเขตของ java.lang.Number

ตัวอย่างกฎการเก็บมีดังนี้

-keep class com.myapp.data.NumberBox {
    public init(java.lang.Number);
}

เมื่อใช้ประเภททั่วไปเฉพาะแอปเป็นคลาสฐาน คุณจะต้อง รวมกฎการเก็บสำหรับคลาสฐานด้วย

เช่น สำหรับโค้ดต่อไปนี้

package com.myapp.data

data class UnpackOptions(val useHighPriority: Boolean)

// The generic Box class with UnpackOptions as the bounded type
class Box<T: UnpackOptions>(val item: T) {
}

คุณใช้กฎการเก็บรักษาได้กับ includedescriptorclasses เพื่อเก็บรักษาทั้งคลาส UnpackOptions และเมธอดของคลาส Box ด้วยกฎเดียวดังนี้

-keep,includedescriptorclasses class com.myapp.data.Box {
    public <init>(com.myapp.data.UnpackOptions);
}

หากต้องการเก็บฟังก์ชันที่เฉพาะเจาะจงซึ่งประมวลผลรายการออบเจ็กต์ คุณต้องเขียนกฎที่ตรงกับลายเซ็นของฟังก์ชันอย่างแม่นยำ โปรดทราบว่าเนื่องจากมีการลบประเภททั่วไป ออก พารามิเตอร์อย่าง List<Product> จึงถือเป็น java.util.List

ตัวอย่างเช่น หากคุณมีคลาสยูทิลิตีที่มีฟังก์ชันที่ประมวลผลรายการ ของออบเจ็กต์ Product ดังนี้

package com.myapp.utils

import com.myapp.data.Product
import android.util.Log

class DataProcessor {
    // This is the function we want to keep
    fun processProducts(products: List<Product>) {
        Log.d("DataProcessor", "Processing ${products.size} products.")
        // Business logic ...
    }
}

// The data class used in the list (from the previous example)
package com.myapp.data
data class Product(val id: String, val name: String)

คุณสามารถใช้กฎการเก็บรักษาต่อไปนี้เพื่อปกป้องเฉพาะprocessProducts ฟังก์ชันได้

-keep class com.myapp.utils.DataProcessor {
    public void processProducts(java.util.List);
}

ประเภทอาร์เรย์

ระบุประเภทอาร์เรย์โดยต่อท้าย [] ไปยังประเภทคอมโพเนนต์สำหรับแต่ละมิติข้อมูล ของอาร์เรย์ ซึ่งใช้ได้กับทั้งประเภทคลาสและประเภทดั้งเดิม

  • อาร์เรย์คลาสแบบมิติเดียว: java.lang.String[]
  • อาร์เรย์ดั้งเดิมแบบ 2 มิติ: int[][]

ตัวอย่างเช่น หากคุณมีโค้ดต่อไปนี้

package com.example.data

class ImageProcessor {
  fun process(): ByteArray {
    // process image to return a byte array
  }
}

คุณสามารถใช้กฎการเก็บรักษาต่อไปนี้

# Keeps a method that returns a byte array.
-keepclassmembers class com.example.data.ImageProcessor {
    public byte[] process();
}

ตัวอย่าง

เช่น หากต้องการเก็บรักษาชั้นเรียนที่เฉพาะเจาะจงและสมาชิกทั้งหมด ให้ใช้คำสั่งต่อไปนี้

-keep class com.myapp.MyClass { *; }

หากต้องการเก็บเฉพาะคลาสพร้อมกับตัวสร้างเริ่มต้น แต่ไม่เก็บสมาชิกอื่นๆ ให้ใช้คำสั่งต่อไปนี้

-keep class com.myapp.MyClass

เราขอแนะนำให้คุณระบุสมาชิกบางคนเสมอ ตัวอย่างเช่น ตัวอย่างต่อไปนี้จะเก็บฟิลด์สาธารณะ text และเมธอดสาธารณะ updateText() ไว้ในคลาส MyClass

-keep class com.myapp.MyClass {
    public java.lang.String text;
    public void updateText(java.lang.String);
}

หากต้องการเก็บฟิลด์และเมธอดสาธารณะทั้งหมดไว้ ให้ดูตัวอย่างต่อไปนี้

-keep public class com.example.api.ApiClient {
    public *;
}

การละเว้นการระบุสมาชิก

การละเว้นการระบุสมาชิกจะทำให้ R8 เก็บตัวสร้างเริ่มต้นสำหรับ คลาสไว้

เช่น หากคุณเขียน -keep class com.example.MyClass หรือ -keep class com.example.MyClass {} R8 จะถือว่าคุณเขียน ดังนี้

-keep class com.example.MyClass{
  void <init>();
}

ฟังก์ชันระดับแพ็กเกจ

หากต้องการอ้างอิงฟังก์ชัน Kotlin ที่กำหนดไว้นอกคลาส (โดยทั่วไปเรียกว่าฟังก์ชันระดับบนสุด) ให้ตรวจสอบว่าได้ใช้ชื่อ Java ที่สร้างขึ้นสำหรับคลาสที่คอมไพเลอร์ Kotlin เพิ่มโดยนัย ชื่อคลาสคือชื่อไฟล์ Kotlin ที่ต่อท้ายด้วย Kt ตัวอย่างเช่น หากคุณมีไฟล์ Kotlin ชื่อ MyClass.kt ที่กำหนดไว้ดังนี้

package com.example.myapp.utils

// A top-level function not inside a class
fun isEmailValid(email: String): Boolean {
    return email.contains("@")
}

หากต้องการเขียนกฎการเก็บสำหรับฟังก์ชัน isEmailValid ข้อกำหนดของคลาส ต้องกำหนดเป้าหมายเป็นคลาส MyClassKt ที่สร้างขึ้น

-keep class com.example.myapp.utils.MyClassKt {
    public static boolean isEmailValid(java.lang.String);
}

ไวลด์การ์ด

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

สัญลักษณ์แทน ใช้กับชั้นเรียนหรือสมาชิก คำอธิบาย
** ทั้งคู่ ใช้กันมากที่สุด ตรงกับชื่อประเภทใดก็ได้ รวมถึงตัวคั่นแพ็กเกจจำนวนเท่าใดก็ได้ ซึ่งจะมีประโยชน์ในการจับคู่คลาสทั้งหมดภายในแพ็กเกจและแพ็กเกจย่อย
* ทั้งคู่ สำหรับข้อกำหนดของคลาส จะตรงกับส่วนใดก็ได้ของชื่อประเภทที่ไม่มีตัวคั่นแพ็กเกจ (.)
สำหรับข้อกำหนดของสมาชิก จะตรงกับชื่อเมธอดหรือฟิลด์ใดก็ได้ เมื่อใช้คำนี้เพียงอย่างเดียว คำนี้ยังเป็นชื่อแทนของ ** ด้วย
? ทั้งคู่ จับคู่อักขระเดียวในชื่อคลาสหรือชื่อสมาชิก
*** สมาชิก ตรงกับประเภทใดก็ได้ รวมถึงประเภทดั้งเดิม (เช่น int) ประเภทคลาส (เช่น java.lang.String) และประเภทอาร์เรย์ของมิติข้อมูลใดก็ได้ (เช่น byte[][])
... สมาชิก จับคู่รายการพารามิเตอร์ใดก็ได้สำหรับเมธอด
% สมาชิก ตรงกับประเภทดั้งเดิม (เช่น `int`, `float`, `boolean` หรืออื่นๆ)

ตัวอย่างวิธีใช้ไวลด์การ์ดพิเศษมีดังนี้

  • หากคุณมีหลายวิธีที่มีชื่อเดียวกันซึ่งใช้ประเภทดั้งเดิมที่แตกต่างกันเป็นอินพุต คุณสามารถใช้ % เพื่อเขียนกฎการเก็บที่เก็บทุกวิธีไว้ เช่น คลาส DataStore นี้มีเมธอด setValue หลายรายการ

    class DataStore {
        fun setValue(key: String, value: Int) { ... }
        fun setValue(key: String, value: Boolean) { ... }
        fun setValue(key: String, value: Float) { ... }
    }
    

    กฎการเก็บรักษาต่อไปนี้จะเก็บรักษาวิธีการทั้งหมด

    -keep class com.example.DataStore {
        public void setValue(java.lang.String, %);
    }
    
  • หากคุณมีหลายชั้นเรียนที่มีชื่อต่างกันเพียง 1 อักขระ ให้ใช้ ? เพื่อเขียนกฎการเก็บรักษาที่จะเก็บรักษาชั้นเรียนทั้งหมด เช่น หากคุณมีคลาสต่อไปนี้

    com.example.models.UserV1 {...}
    com.example.models.UserV2 {...}
    com.example.models.UserV3 {...}
    

    กฎการเก็บรักษาต่อไปนี้จะเก็บรักษาชั้นเรียนทั้งหมด

    -keep class com.example.models.UserV?
    
  • หากต้องการจับคู่คลาส Example และ AnotherExample (หากเป็นคลาสระดับรูท) แต่ไม่จับคู่ com.foo.Example ให้ใช้กฎการเก็บรักษาต่อไปนี้

    -keep class *Example
    
  • หากคุณใช้ * เพียงอย่างเดียว ระบบจะถือว่าเป็นการแทน ** ตัวอย่างเช่น กฎการเก็บรักษาต่อไปนี้จะมีความหมายเหมือนกัน

    -keepclasseswithmembers class * { public static void main(java.lang.String[];) }
    
    -keepclasseswithmembers class ** { public static void main(java.lang.String[];) }
    

ตรวจสอบชื่อ Java ที่สร้างขึ้น

เมื่อเขียนกฎการเก็บรักษา คุณต้องระบุคลาสและประเภทอ้างอิงอื่นๆ โดยใช้ชื่อหลังจากที่คอมไพล์เป็น Java bytecode แล้ว (ดูตัวอย่างได้ในข้อกำหนดของคลาสและประเภท) หากต้องการตรวจสอบชื่อ Java ที่สร้างขึ้นสำหรับโค้ด ให้ใช้เครื่องมือใดเครื่องมือหนึ่งต่อไปนี้ใน Android Studio

  • ตัววิเคราะห์ APK
  • เมื่อเปิดไฟล์ต้นฉบับ Kotlin แล้ว ให้ตรวจสอบไบต์โค้ดโดยไปที่เครื่องมือ > Kotlin > แสดงไบต์โค้ด Kotlin > แยก