เมื่อพบคลาสที่ไม่เสถียรซึ่งทำให้เกิดปัญหาด้านประสิทธิภาพ คุณควรทำให้คลาสดังกล่าวเสถียร เอกสารนี้สรุปเทคนิคหลายอย่างที่คุณใช้เพื่อดำเนินการดังกล่าวได้
เปิดใช้การข้ามที่รวดเร็ว
คุณควรลองเปิดใช้โหมดข้ามอย่างรวดเร็วก่อน โหมดข้ามอย่างเข้มงวดช่วยให้ข้าม Composable ที่มีพารามิเตอร์ที่ไม่เสถียรได้ และเป็นวิธีที่ง่ายที่สุดในการ แก้ไขปัญหาด้านประสิทธิภาพที่เกิดจากความเสถียร
ดูข้อมูลเพิ่มเติมได้ที่การข้ามอย่างรวดเร็ว
ทำให้ชั้นเรียนเปลี่ยนแปลงไม่ได้
นอกจากนี้ คุณยังลองทำให้คลาสที่ไม่เสถียรเปลี่ยนแปลงไม่ได้โดยสมบูรณ์ได้ด้วย
- เปลี่ยนแปลงไม่ได้: ระบุประเภทที่ค่าของพร็อพเพอร์ตี้ใดๆ จะเปลี่ยนแปลงไม่ได้
หลังจากสร้างอินสแตนซ์ของประเภทนั้น และเมธอดทั้งหมดจะ
โปร่งใสโดยอ้างอิง
- ตรวจสอบว่าพร็อพเพอร์ตี้ทั้งหมดของคลาสเป็นทั้ง
valแทนที่จะเป็นvarและเป็นประเภทที่เปลี่ยนแปลงไม่ได้ - ประเภทดั้งเดิม เช่น
String, IntและFloatจะเปลี่ยนแปลงไม่ได้เสมอ - หากทำไม่ได้ คุณต้องใช้สถานะ Compose สำหรับพร็อพเพอร์ตี้ที่เปลี่ยนแปลงได้
- ตรวจสอบว่าพร็อพเพอร์ตี้ทั้งหมดของคลาสเป็นทั้ง
- เสถียร: ระบุประเภทที่เปลี่ยนแปลงได้ รันไทม์ของ Compose จะไม่ทราบว่าเมื่อใดที่พร็อพเพอร์ตี้สาธารณะหรือลักษณะการทำงานของเมธอดของประเภทใดๆ จะให้ผลลัพธ์ที่แตกต่างจากการเรียกใช้ก่อนหน้า
คอลเล็กชันที่เปลี่ยนแปลงไม่ได้
สาเหตุที่พบบ่อยที่ทำให้ Compose ถือว่าคลาสไม่เสถียรคือคอลเล็กชัน ดังที่ระบุไว้ในหน้าวินิจฉัยปัญหาด้านความเสถียร คอมไพเลอร์ Compose
ไม่สามารถมั่นใจได้อย่างเต็มที่ว่าคอลเล็กชัน เช่น List, Map และ Set จะ
เปลี่ยนแปลงไม่ได้จริงๆ จึงทําเครื่องหมายว่าไม่เสถียร
คุณสามารถใช้คอลเล็กชันที่ไม่เปลี่ยนแปลงเพื่อแก้ไขปัญหานี้ได้ คอมไพเลอร์ Compose รองรับ Kotlinx Immutable Collections รับประกันได้ว่าคอลเล็กชันเหล่านี้ จะเปลี่ยนแปลงไม่ได้ และคอมไพเลอร์ Compose จะถือว่าคอลเล็กชันเหล่านี้ เป็นเช่นนั้น ไลบรารียังอยู่ในเวอร์ชันอัลฟ่า ดังนั้นโปรดเตรียมพร้อมรับการเปลี่ยนแปลงที่อาจเกิดขึ้นกับ API
พิจารณาคลาสที่ไม่เสถียรนี้อีกครั้งจากคำแนะนำวินิจฉัยปัญหา ความเสถียร
unstable class Snack {
…
unstable val tags: Set<String>
…
}
คุณทำให้ tags เสถียรได้โดยใช้คอลเล็กชันที่ไม่เปลี่ยนแปลง ในชั้นเรียน ให้เปลี่ยน
ประเภทของ tags เป็น ImmutableSet<String> โดยทำดังนี้
data class Snack{
…
val tags: ImmutableSet<String> = persistentSetOf()
…
}
หลังจากนั้น พารามิเตอร์ทั้งหมดของคลาสจะเปลี่ยนแปลงไม่ได้ และคอมไพเลอร์ Compose จะทําเครื่องหมายคลาสเป็นแบบเสถียร
ใส่คำอธิบายประกอบด้วย Stable หรือ Immutable
วิธีหนึ่งในการแก้ปัญหาความเสถียรคือการใส่คำอธิบายประกอบคลาสที่ไม่เสถียรด้วย @Stable หรือ @Immutable
การใส่คำอธิบายประกอบในคลาสเป็นการลบล้างสิ่งที่คอมไพเลอร์จะอนุมานเกี่ยวกับคลาสของคุณ ซึ่งคล้ายกับ!! Operator ใน Kotlin คุณควรระมัดระวังเป็นอย่างยิ่ง
เกี่ยวกับวิธีใช้คำอธิบายประกอบเหล่านี้ การลบล้างลักษณะการทำงานของคอมไพเลอร์
อาจทำให้คุณพบข้อบกพร่องที่ไม่คาดคิด เช่น Composable ไม่ทำการรวมกันใหม่เมื่อ
คุณคาดหวังให้เป็นเช่นนั้น
หากเป็นไปได้ที่จะทำให้คลาสมีเสถียรภาพโดยไม่ต้องใช้คำอธิบายประกอบ คุณควร พยายามทำให้คลาสมีเสถียรภาพด้วยวิธีดังกล่าว
ข้อมูลโค้ดต่อไปนี้เป็นตัวอย่างขั้นต่ำของคลาสข้อมูลที่มีคำอธิบายประกอบเป็น เปลี่ยนแปลงไม่ได้
@Immutable
data class Snack(
…
)
ไม่ว่าคุณจะใช้คำอธิบายประกอบ @Immutable หรือ @Stable คอมไพเลอร์ Compose
จะทำเครื่องหมายคลาส Snack ว่าเป็นคลาสที่เสถียร
ชั้นเรียนที่มีคำอธิบายประกอบในคอลเล็กชัน
พิจารณา Composable ที่มีพารามิเตอร์ประเภท List<Snack>
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
แม้ว่าคุณจะใส่คำอธิบายประกอบ Snack ด้วย @Immutable แต่คอมไพเลอร์ Compose จะยังคงทำเครื่องหมายพารามิเตอร์ snacks ใน HighlightedSnacks ว่าไม่เสถียร
พารามิเตอร์ประสบปัญหาเดียวกันกับคลาสเมื่อพูดถึงประเภทคอลเล็กชัน
คอมไพเลอร์ Compose จะทำเครื่องหมายพารามิเตอร์ประเภท List เป็นไม่เสถียรเสมอ แม้ว่าจะเป็นคอลเล็กชันของประเภทที่เสถียรก็ตาม
คุณทำเครื่องหมายพารามิเตอร์แต่ละรายการว่าเสถียรไม่ได้ และจะใส่คำอธิบายประกอบ Composable ให้ข้ามได้เสมอไม่ได้ มีหลายเส้นทางให้เดินหน้าต่อไป
คุณแก้ปัญหาคอลเล็กชันที่ไม่เสถียรได้หลายวิธี ส่วนย่อยต่อไปนี้จะอธิบายแนวทางที่แตกต่างกันเหล่านี้
ไฟล์การกำหนดค่า
หากคุณยินดีที่จะปฏิบัติตามสัญญาความเสถียรในโค้ดเบส คุณก็เลือกใช้คอลเล็กชัน Kotlin เป็นคอลเล็กชันที่เสถียรได้โดยเพิ่ม kotlin.collections.* ลงในไฟล์การกำหนดค่าความเสถียร
คอลเล็กชันที่เปลี่ยนแปลงไม่ได้
หากต้องการความปลอดภัยของความไม่เปลี่ยนแปลงในเวลาคอมไพล์ คุณสามารถใช้คอลเล็กชัน kotlinx immutable
แทน List ได้
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
หากใช้คอลเล็กชันที่ไม่เปลี่ยนแปลงไม่ได้ คุณก็สร้างคอลเล็กชันของคุณเองได้ โดยทำได้ด้วยการ
ใส่ List ในคลาสที่เสถียรซึ่งมีคำอธิบายประกอบ โดยทั่วไปแล้ว Wrapper ทั่วไปน่าจะเป็นตัวเลือกที่ดีที่สุดสำหรับกรณีนี้ ทั้งนี้ขึ้นอยู่กับข้อกำหนดของคุณ
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
จากนั้นคุณจะใช้ค่านี้เป็นประเภทของพารามิเตอร์ใน Composable ได้
@Composable
private fun HighlightedSnacks(
index: Int,
snacks: SnackCollection,
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
)
โซลูชัน
หลังจากใช้วิธีใดวิธีหนึ่งแล้ว ตอนนี้คอมไพเลอร์ Compose จะทําเครื่องหมาย
HighlightedSnacks Composable เป็นทั้ง skippable และ restartable
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
stable snacks: ImmutableList<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
ในระหว่างการจัดองค์ประกอบใหม่ ตอนนี้ Compose สามารถข้ามHighlightedSnacksได้หากไม่มีการเปลี่ยนแปลงอินพุต
ไฟล์การกำหนดค่าความเสถียร
ตั้งแต่ Compose Compiler 1.5.5 เป็นต้นไป คุณจะระบุไฟล์การกำหนดค่าของคลาสที่
ถือว่าเสถียรได้ในเวลาคอมไพล์ ซึ่งช่วยให้พิจารณาคลาสที่คุณควบคุมไม่ได้ เช่น คลาสไลบรารีมาตรฐานอย่าง LocalDateTime ว่าเป็นคลาสที่เสถียร
ไฟล์การกำหนดค่าคือไฟล์ข้อความธรรมดาที่มี 1 คลาสต่อแถว ระบบรองรับความคิดเห็น ไวลด์การ์ดเดี่ยว และไวลด์การ์ดคู่
ตัวอย่างการกำหนดค่า
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>
หากต้องการเปิดใช้ฟีเจอร์นี้ ให้ส่งเส้นทางของไฟล์กำหนดค่าไปยัง
composeCompiler บล็อกตัวเลือกของปลั๊กอิน Gradle ของคอมไพเลอร์ Compose
composeCompiler {
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}
เนื่องจากคอมไพเลอร์ Compose จะทำงานในแต่ละโมดูลของโปรเจ็กต์แยกกัน คุณจึง ระบุการกำหนดค่าที่แตกต่างกันให้กับโมดูลต่างๆ ได้หากต้องการ หรือ มีการกำหนดค่าเดียวที่ระดับรูทของโปรเจ็กต์และส่งเส้นทางนั้นไปยัง แต่ละโมดูล
โมดูลหลายรายการ
ปัญหาที่พบบ่อยอีกอย่างหนึ่งเกี่ยวข้องกับสถาปัตยกรรมแบบหลายโมดูล คอมไพเลอร์ Compose จะอนุมานได้ว่าคลาสมีเสถียรภาพหรือไม่ก็ต่อเมื่อประเภทที่ไม่ใช่ประเภทดั้งเดิมทั้งหมดที่ คอมไพเลอร์อ้างอิงมีการทำเครื่องหมายอย่างชัดเจนว่ามีเสถียรภาพ หรืออยู่ในโมดูลที่สร้างด้วยคอมไพเลอร์ Compose ด้วยเช่นกัน
หากชั้นข้อมูลอยู่ในโมดูลแยกต่างหากจากชั้น UI ซึ่งเป็นแนวทางที่แนะนํา คุณอาจพบปัญหานี้
โซลูชัน
หากต้องการแก้ปัญหานี้ คุณสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้
- เพิ่มคลาสลงในไฟล์การกำหนดค่าคอมไพเลอร์
- เปิดใช้คอมไพเลอร์ Compose ในโมดูลเลเยอร์ข้อมูล หรือติดแท็กคลาสด้วย
@Stableหรือ@Immutableตามความเหมาะสม- ซึ่งเกี่ยวข้องกับการเพิ่มการอ้างอิง Compose ลงในเลเยอร์ข้อมูล อย่างไรก็ตาม
เป็นเพียงการขึ้นต่อกันสำหรับรันไทม์ของ Compose เท่านั้น ไม่ใช่สำหรับ
Compose-UI
- ซึ่งเกี่ยวข้องกับการเพิ่มการอ้างอิง Compose ลงในเลเยอร์ข้อมูล อย่างไรก็ตาม
เป็นเพียงการขึ้นต่อกันสำหรับรันไทม์ของ Compose เท่านั้น ไม่ใช่สำหรับ
- ในโมดูล UI ให้รวมคลาสเลเยอร์ข้อมูลไว้ในคลาส Wrapper เฉพาะ UI
ปัญหาเดียวกันนี้จะเกิดขึ้นเมื่อใช้ไลบรารีภายนอกด้วย หากไม่ได้ใช้ คอมไพเลอร์ Compose
Composable บางรายการไม่ควรข้ามได้
เมื่อพยายามแก้ไขปัญหาเกี่ยวกับความเสถียร คุณไม่ควรพยายามทำให้ Composable ทุกรายการข้ามได้ การพยายามทำเช่นนั้นอาจนำไปสู่การเพิ่มประสิทธิภาพก่อนเวลาอันควร ซึ่งจะทำให้เกิดปัญหามากกว่าที่แก้ไข
มีหลายกรณีที่การข้ามไม่ได้ไม่ได้มีประโยชน์ใดๆ และอาจทำให้โค้ดดูแลรักษายาก เช่น
- Composable ที่ไม่ได้รับการ Recompose บ่อยๆ หรือไม่ได้รับการ Recompose เลย
- Composable ที่เรียกใช้เฉพาะ Composable ที่ข้ามได้
- Composable ที่มีพารามิเตอร์จํานวนมากซึ่งมีการติดตั้งใช้งานเท่ากับที่ใช้ทรัพยากรมาก ในกรณีนี้ ต้นทุนในการตรวจสอบว่าพารามิเตอร์มีการเปลี่ยนแปลงหรือไม่ อาจสูงกว่าต้นทุนในการจัดองค์ประกอบใหม่ราคาถูก
เมื่อ Composable ข้ามได้ จะมีการเพิ่มค่าใช้จ่ายเล็กน้อยซึ่งอาจไม่คุ้มค่า คุณยังใส่คำอธิบายประกอบให้ Composable เป็น non-restartable ในกรณีที่คุณพิจารณาแล้วว่าการเป็น restartable มีค่าใช้จ่ายสูงกว่าคุณค่าที่ได้รับ