การเพิ่มประสิทธิภาพสำหรับผู้เขียนคลัง

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

เอกสารนี้มีไว้สำหรับนักพัฒนาไลบรารีที่เผยแพร่ แต่ก็อาจเป็นประโยชน์สำหรับนักพัฒนาโมดูลไลบรารีภายในในแอปขนาดใหญ่ที่แยกเป็นโมดูลด้วย

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

ใช้ Codegen แทนการสะท้อน

เมื่อเป็นไปได้ ให้ใช้การสร้างโค้ด (codegen) แทนการรีเฟลกชัน ทั้ง Codegen และ Reflection เป็นแนวทางที่ใช้กันทั่วไปเพื่อหลีกเลี่ยง โค้ดสำเร็จรูปเมื่อเขียนโปรแกรม แต่ Codegen จะเข้ากันได้กับเครื่องมือเพิ่มประสิทธิภาพแอป เช่น R8 มากกว่า

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

ไลบรารีที่ทันสมัยหลายแห่งใช้ Codegen แทนการสะท้อน ดูKSP สำหรับจุดแรกเข้าทั่วไปที่ใช้โดย Room Dagger2 และอื่นๆ อีกมากมาย

เมื่อการสะท้อนเป็นเรื่องที่ยอมรับได้

หากจำเป็นต้องใช้การสะท้อน คุณควรสะท้อนเฉพาะในรายการใดรายการหนึ่งต่อไปนี้

  • ประเภทเป้าหมายที่เฉพาะเจาะจง (ผู้ใช้ที่เฉพาะเจาะจงหรือคลาสย่อย)
  • โค้ดที่ใช้คำอธิบายประกอบรันไทม์ที่เฉพาะเจาะจง

การใช้การสะท้อนในลักษณะนี้จะจำกัดค่าใช้จ่ายรันไทม์ และช่วยให้เขียนกฎการเก็บรักษาที่กำหนดเป้าหมายได้

รูปแบบการสะท้อนที่เฉพาะเจาะจงและตรงเป้าหมายนี้เป็นรูปแบบที่คุณเห็นได้ทั้งในเฟรมเวิร์ก Android (เช่น เมื่อขยายกิจกรรม มุมมอง และ Drawable) และไลบรารี AndroidX (เช่น เมื่อสร้าง WorkManager ListenableWorkers หรือ RoomDatabases) ในทางตรงกันข้าม การสะท้อนแบบเปิดของ Gson ไม่เหมาะสำหรับการใช้งานในแอป Android

ประเภทของกฎการเก็บในไลบรารี

กฎการเก็บมี 2 ประเภทที่แตกต่างกันซึ่งคุณใช้ได้ในคลัง

  • กฎการเก็บรักษาของผู้บริโภคต้องระบุกฎที่เก็บรักษาทุกอย่างที่ห้องสมุด แสดง หากไลบรารีใช้การสะท้อนหรือ JNI เพื่อเรียกใช้โค้ดของตัวเอง หรือ โค้ดที่กำหนดโดยแอปไคลเอ็นต์ กฎเหล่านี้จะต้องอธิบายว่าต้องเก็บโค้ดใดไว้ ไลบรารีควรจัดแพ็กเกจกฎการเก็บรักษาของผู้บริโภคซึ่งใช้รูปแบบเดียวกับกฎการเก็บรักษาแอป กฎเหล่านี้จะรวมอยู่ในอาร์ติแฟกต์ของไลบรารี (AAR หรือ JAR) และจะใช้โดยอัตโนมัติในระหว่างการเพิ่มประสิทธิภาพแอป Android เมื่อมีการใช้ไลบรารี กฎเหล่านี้จะได้รับการดูแลในไฟล์ที่ระบุด้วยพร็อพเพอร์ตี้ consumerProguardFiles ในไฟล์ build.gradle.kts (หรือ build.gradle) ดูข้อมูลเพิ่มเติมได้ที่เขียน กฎการเก็บรักษาสำหรับผู้บริโภค
  • ระบบจะใช้กฎการเก็บรักษาสำหรับการสร้างไลบรารีเมื่อสร้างไลบรารี โดยคุณจะ ต้องใช้เฉพาะในกรณีที่ตัดสินใจเพิ่มประสิทธิภาพไลบรารีบางส่วนในเวลาบิลด์ โดยจะต้องป้องกันไม่ให้มีการนำ API สาธารณะของไลบรารีออก มิฉะนั้น API สาธารณะจะไม่ปรากฏในการเผยแพร่ไลบรารี ซึ่งหมายความว่านักพัฒนาแอป จะใช้ไลบรารีไม่ได้ กฎเหล่านี้จะได้รับการดูแลในไฟล์ที่ระบุด้วยพร็อพเพอร์ตี้ proguardFiles ในไฟล์ build.gradle.kts (หรือ build.gradle) ดูข้อมูลเพิ่มเติมได้ที่เพิ่มประสิทธิภาพการสร้างไลบรารี AAR

เขียนกฎการเก็บรักษาสำหรับผู้บริโภค

นอกเหนือจากแนวทางปฏิบัติแนะนำทั่วไปสำหรับกฎการเก็บรักษาแล้ว เราขอแนะนำสิ่งต่อไปนี้สำหรับผู้เขียนไลบรารีโดยเฉพาะ

  • อย่าใช้กฎส่วนกลางที่ไม่เหมาะสม โดยหลีกเลี่ยงการใส่การตั้งค่าส่วนกลาง เช่น -dontobfuscate หรือ -allowaccessmodification ในไฟล์กฎการเก็บรักษาสำหรับผู้บริโภคของไลบรารี เนื่องจากจะส่งผลต่อแอปทั้งหมดที่ใช้ไลบรารีของคุณ
  • อย่าใส่กฎการเก็บรักษาทั่วทั้งแพ็กเกจ เช่น -keep class com.mylibrary.** { *; } กฎดังกล่าวจะจำกัดการเพิ่มประสิทธิภาพในทั้งไลบรารี ซึ่งส่งผลต่อขนาดของแอปทั้งหมดที่ใช้ไลบรารี
  • อย่าใช้ -repackageclasses ในไฟล์กฎการเก็บรักษาสำหรับผู้บริโภคของไลบรารี อย่างไรก็ตาม หากต้องการเพิ่มประสิทธิภาพการสร้างไลบรารี คุณสามารถใช้ -repackageclasses ที่มีชื่อแพ็กเกจภายใน เช่น <your.library.package>.internal ใน ไฟล์กฎการเก็บรักษาบิลด์ของไลบรารี ซึ่งจะช่วยให้คลังมีประสิทธิภาพมากขึ้น แม้ว่าแอปที่ใช้คลังนั้นจะไม่ได้เพิ่มประสิทธิภาพก็ตาม แต่โดยทั่วไปแล้ว ไม่จำเป็นเนื่องจากแอปควรเพิ่มประสิทธิภาพด้วย ดูรายละเอียดเพิ่มเติม เกี่ยวกับการเพิ่มประสิทธิภาพไลบรารีได้ที่การเพิ่มประสิทธิภาพสำหรับผู้เขียนไลบรารี
  • ประกาศแอตทริบิวต์ที่จำเป็นเพื่อให้ห้องสมุดทำงานได้ในไฟล์กฎการเก็บรักษาของห้องสมุด แม้ว่าอาจมีการทับซ้อนกับแอตทริบิวต์ที่กำหนดไว้ใน proguard-android-optimize.txt
  • หากคุณต้องการแอตทริบิวต์ต่อไปนี้ในการเผยแพร่ไลบรารี ให้เก็บไว้ในไฟล์กฎการเก็บรักษาของบิลด์ของไลบรารีและไม่ใช่ในไฟล์กฎการเก็บรักษาของผู้ใช้ของไลบรารี
    • AnnotationDefault
    • EnclosingMethod
    • Exceptions
    • InnerClasses
    • RuntimeInvisibleAnnotations
    • RuntimeInvisibleParameterAnnotations
    • RuntimeInvisibleTypeAnnotations
    • RuntimeVisibleAnnotations
    • RuntimeVisibleParameterAnnotations
    • RuntimeVisibleTypeAnnotations
    • Signature
  • ผู้เขียนไลบรารีควรเก็บแอตทริบิวต์ RuntimeVisibleAnnotations ไว้ใน กฎการเก็บรักษาของผู้บริโภคหากใช้คำอธิบายประกอบในรันไทม์
  • ผู้เขียนไลบรารีไม่ควรใช้ตัวเลือกส่วนกลางต่อไปนี้ในกฎการเก็บรักษาของผู้บริโภค
    • -include
    • -basedirectory
    • -injars
    • -outjars
    • -libraryjars
    • -repackageclasses
    • -flattenpackagehierarchy
    • -allowaccessmodification
    • -overloadaggressively
    • -renamesourcefileattribute
    • -ignorewarnings
    • -addconfigurationdebugging
    • -printconfiguration
    • -printmapping
    • -printusage
    • -printseeds
    • -applymapping
    • -obfuscationdictionary
    • -classobfuscationdictionary
    • -packageobfuscationdictionary

ไลบรารี AAR

หากต้องการเพิ่มกฎสำหรับผู้ใช้ไลบรารี AAR ให้ใช้ตัวเลือก consumerProguardFiles ในสคริปต์บิลด์ของโมดูลไลบรารี Android ดูข้อมูลเพิ่มเติมได้ที่คำแนะนำในการสร้างโมดูลไลบรารี

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

ไลบรารี JAR

หากต้องการรวมกฎกับไลบรารี Kotlin/Java ที่จัดส่งเป็น JAR ให้วางไฟล์กฎไว้ในไดเรกทอรี META-INF/proguard/ ของ JAR สุดท้าย โดยใช้ชื่อไฟล์ใดก็ได้ เช่น หากโค้ดของคุณอยู่ใน <libraryroot>/src/main/kotlin ให้วางไฟล์กฎสำหรับผู้บริโภคไว้ที่ <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro และระบบจะรวมกฎไว้ในตำแหน่งที่ถูกต้องใน JAR เอาต์พุต

ยืนยันว่ากฎของแพ็กเกจ JAR สุดท้ายถูกต้องโดยตรวจสอบว่ากฎอยู่ในไดเรกทอรี META-INF/proguard

เพิ่มประสิทธิภาพการสร้างไลบรารี AAR (ขั้นสูง)

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

หากยังต้องการเพิ่มประสิทธิภาพไลบรารีในเวลาบิลด์ Android Gradle Plugin จะรองรับการดำเนินการนี้

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

โปรดทราบว่าลักษณะการทำงานของ proguardFiles แตกต่างจาก consumerProguardFiles อย่างมาก

  • proguardFiles ใช้ในเวลาบิลด์ มักใช้ร่วมกับ getDefaultProguardFile("proguard-android-optimize.txt") เพื่อกำหนดว่าควรเก็บส่วนใดของไลบรารีไว้ในระหว่างการบิลด์ไลบรารี อย่างน้อยที่สุด นี่คือ API สาธารณะของคุณ
  • consumerProguardFiles ในทางตรงกันข้ามจะรวมอยู่ในไลบรารีเพื่อส่งผลต่อ การเพิ่มประสิทธิภาพที่จะเกิดขึ้นในภายหลัง ระหว่างการสร้างแอปที่ใช้ ไลบรารีของคุณ

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

หากคุณใช้ -repackageclasses ในบิลด์ของไลบรารี ให้จัดแพ็กเกจคลาสใหม่เป็นแพ็กเกจย่อย ภายใน แพ็กเกจของไลบรารี เช่น ใช้ -repackageclasses 'com.example.mylibrary.internal' แทน -repackageclasses 'internal'

รองรับ R8 เวอร์ชันต่างๆ (ขั้นสูง)

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

หากต้องการระบุกฎ R8 ที่กำหนดเป้าหมาย คุณต้องรวมกฎเหล่านั้นไว้ในไดเรกทอรี META-INF/com.android.tools ภายใน classes.jar ของ AAR หรือในไดเรกทอรี META-INF/com.android.tools ของ JAR

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

ในไดเรกทอรี META-INF/com.android.tools อาจมีไดเรกทอรีย่อยหลายรายการที่มีชื่อในรูปแบบ r8-from-<X>-upto-<Y> เพื่อระบุว่ากฎเขียนขึ้นสำหรับ R8 เวอร์ชันใด แต่ละไดเรกทอรีย่อยจะมีไฟล์ที่มีกฎ R8 ได้ 1 ไฟล์ขึ้นไป โดยมีชื่อไฟล์และนามสกุลใดก็ได้

โปรดทราบว่าส่วน -from-<X> และ -upto-<Y> เป็นส่วนที่ไม่บังคับ ส่วนเวอร์ชัน <Y> เป็นเวอร์ชันเฉพาะ และช่วงเวอร์ชันมักจะต่อเนื่องกัน แต่ก็อาจทับซ้อนกันได้เช่นกัน

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

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