โหมดการข้ามแบบเข้มงวด

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

  • คอมโพสิเบิลที่มีพารามิเตอร์ที่ไม่เสถียรจะข้ามได้
  • ระบบจะจดจำ Lambda ที่มีการบันทึกที่ไม่เสถียร

เปิดใช้โหมดการข้ามที่เข้มงวด

การข้ามแบบเข้มงวดจะเปิดใช้โดยค่าเริ่มต้นใน Kotlin 2.0.20

หากต้องการเปิดใช้การข้ามที่เข้มงวดสำหรับโมดูล Gradle ในรุ่นก่อน 2.0.20 ให้ใส่ตัวเลือกต่อไปนี้ในบล็อก composeCompiler ของการกำหนดค่า Gradle

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

ความสามารถในการข้ามที่ประกอบกันได้

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

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

กรณีที่ควรข้าม

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

  • พารามิเตอร์ที่ไม่เสถียรจะเปรียบเทียบโดยใช้ความเท่ากันของอินสแตนซ์ (===)
  • ระบบจะเปรียบเทียบพารามิเตอร์ที่เสถียรโดยใช้ความเท่ากันของออบเจ็กต์ (Object.equals())

หากพารามิเตอร์ทั้งหมดเป็นไปตามข้อกำหนดเหล่านี้ Compose จะข้าม Composable ในระหว่างการจัดองค์ประกอบใหม่

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

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

ใส่คำอธิบายประกอบคลาสว่าเสถียร

หากต้องการเปรียบเทียบออบเจ็กต์โดยใช้ความเท่ากันของออบเจ็กต์ (Object.equals()) แทนความเท่ากันของอินสแตนซ์ (===) ให้ใส่คำอธิบายประกอบคลาสที่ระบุด้วย @Stable หรือ @Immutable ต่อไป

โปรดคำนึงถึงข้อดีข้อเสียต่อไปนี้ก่อนเพิ่ม @Stable หรือ @Immutable

  • คลาสที่มีพร็อพเพอร์ตี้ที่เสถียรในโมดูล Compose มีความเสถียรอยู่แล้ว: คลาสที่มีพร็อพเพอร์ตี้ val ของประเภทที่เสถียรในโมดูลที่คอมไพล์ด้วยคอมไพเลอร์ Compose จะได้รับการอนุมานโดยอัตโนมัติว่าเสถียรและใช้ .equals() โดยไม่ต้องใช้ @Immutable หรือ @Stable
  • ใส่คำอธิบายประกอบเฉพาะคลาสที่มีการตรวจสอบ .equals() ที่ใช้ทรัพยากรน้อย: เพิ่ม @Stable หรือ @Immutable เมื่อคอมไพเลอร์ไม่สามารถอนุมานความเสถียรได้ (เช่น คลาสจากโมดูลที่ไม่ใช่ Compose) และมีการจัดสรรอินสแตนซ์ใหม่ที่มีค่าเหมือนกันบ่อยครั้ง โดยที่การตรวจสอบ .equals() ของคลาสใช้ทรัพยากรน้อย
  • โปรดระมัดระวังเมื่อใส่คำอธิบายประกอบโมเดลด้วยคอลเล็กชัน: การเพิ่ม @Stable หรือ @Immutable ลงในโมเดลที่มีคอลเล็กชัน (หรือแทนที่ List ด้วย ImmutableList) จะทำให้ Compose เปรียบเทียบองค์ประกอบทุกรายการในคอลเล็กชันกับ .equals() ในการส่งผ่านการจัดองค์ประกอบใหม่แต่ละครั้ง สำหรับคอลเล็กชันหรือโมเดลขนาดใหญ่ที่ใช้ในเลย์เอาต์แบบ Lazy (LazyColumn, LazyRow, LazyGrid) การเปรียบเทียบทุกองค์ประกอบอาจช้ากว่าการใช้ความเท่ากันของอินสแตนซ์ (===) หรือการจัดองค์ประกอบใหม่โดยตรง

การจดจำ Lambda

โหมดการข้ามที่เข้มงวดยังช่วยให้จดจำ Lambda ภายใน Composable ได้มากขึ้นด้วย เมื่อเปิดใช้การข้ามแบบเข้มงวด ระบบจะจดจำ Lambda ทุกรายการภายในฟังก์ชันที่ประกอบกันได้โดยอัตโนมัติ

ตัวอย่าง

เพื่อให้เกิดการจดจำ Lambda ภายใน Composable เมื่อใช้การข้ามที่เข้มงวด คอมไพเลอร์จะรวม Lambda ของคุณด้วยการเรียก remember โดยจะใช้คีย์เป็นแคปเจอร์ของ Lambda

ลองพิจารณากรณีที่คุณมี Lambda ดังตัวอย่างต่อไปนี้

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

เมื่อเปิดใช้การข้ามที่เข้มงวด คอมไพเลอร์จะจดจำ Lambda โดยรวมไว้ในการเรียก remember ดังนี้

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

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

การจดจำและการจัดองค์ประกอบใหม่

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

หลีกเลี่ยงการใช้ Memoization

หากคุณมี Lambda ที่ไม่ต้องการจดจำ ให้ใช้คำอธิบายประกอบ @DontMemoize

val lambda = @DontMemoize {
    ...
}

ขนาด APK

เมื่อคอมไพล์แล้ว Composable ที่ข้ามได้จะส่งผลให้มีการสร้างโค้ดมากกว่า Composable ที่ข้ามไม่ได้ เมื่อเปิดใช้การข้ามแบบเข้มงวด คอมไพเลอร์จะทำเครื่องหมายคอมโพสิเบิลเกือบทั้งหมดว่าข้ามได้ และรวม Lambda ทั้งหมดไว้ใน remember{...} ด้วยเหตุนี้ การเปิดใช้โหมดการข้ามที่เข้มงวดจึงส่งผลต่อขนาด APK ของแอปพลิเคชันน้อยมาก

การเปิดใช้การข้ามที่มีประสิทธิภาพใน Now In Android เพิ่มขนาด APK ขึ้น 4 KB ความแตกต่างของขนาดขึ้นอยู่กับจำนวน Composable ที่ข้ามไม่ได้ก่อนหน้านี้ซึ่งมีอยู่ในแอปที่ระบุ แต่ควรมีขนาดค่อนข้างเล็ก