เมื่อพบคลาสที่ไม่เสถียรซึ่งทำให้เกิดปัญหาด้านประสิทธิภาพ คุณควรทำให้เวอร์ชันดังกล่าวเสถียร เอกสารนี้จะพูดถึงเทคนิคหลายอย่าง ที่คุณนำไปใช้ได้
เปิดใช้การข้ามที่มีประสิทธิภาพ
คุณควรลองเปิดใช้โหมดการข้ามขั้นสูงก่อน โหมดการข้ามแบบเข้มงวดช่วยให้ข้าม 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
การใส่คำอธิบายประกอบชั้นเรียนจะเป็นการลบล้างสิ่งที่คอมไพเลอร์จะอนุมานเกี่ยวกับชั้นเรียนของคุณ ซึ่งคล้ายกับโอเปอเรเตอร์ !!
ใน Kotlin คุณควรใช้คำอธิบายประกอบเหล่านี้อย่างระมัดระวัง การลบล้างลักษณะการทำงานของคอมไพเลอร์อาจทำให้คุณพบข้อบกพร่องที่ไม่คาดคิด เช่น Composable ไม่ปรับแต่งใหม่ตามที่คุณคาดหวัง
หากทำให้ชั้นเรียนมีความเสถียรได้โดยไม่ต้องใช้คำอธิบายประกอบ คุณควรพยายามทำให้ชั้นเรียนมีความเสถียรด้วยวิธีนี้
ข้อมูลโค้ดต่อไปนี้ให้ตัวอย่างเล็กน้อยของคลาสข้อมูลที่มีคำอธิบายประกอบว่าเปลี่ยนแปลงไม่ได้
@Immutable
data class Snack(
…
)
ไม่ว่าคุณจะใช้คำอธิบายประกอบ @Immutable
หรือ @Stable
คอมไพเลอร์ Compose จะทําเครื่องหมายคลาส Snack
เป็น "เสถียร"
ชั้นเรียนที่มีคำอธิบายประกอบในคอลเล็กชัน
พิจารณาคอมโพสิเบิลที่มีพารามิเตอร์ประเภท List<Snack>
ดังนี้
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
แม้ว่าคุณจะกำกับเนื้อหา Snack
ด้วย @Immutable
แต่คอมไพเลอร์คอมโพซก็ยังคงทำเครื่องหมายพารามิเตอร์ snacks
ใน HighlightedSnacks
ว่าไม่เสถียร
พารามิเตอร์จะพบปัญหาเดียวกันกับคลาสเมื่อพูดถึงประเภทคอลเล็กชัน คอมไพเลอร์ Compose จะทําเครื่องหมายพารามิเตอร์ประเภท List
เป็น "ไม่เสถียร" เสมอ แม้ว่าจะเป็นคอลเล็กชันประเภทที่เสถียรก็ตาม
คุณจะทําเครื่องหมายพารามิเตอร์แต่ละรายการว่าเสถียรไม่ได้ และไม่สามารถใส่คำอธิบายประกอบคอมโพสิเบิลให้ข้ามได้เสมอ มีหลายเส้นทางที่มุ่งหน้าไปข้างหน้า
คุณสามารถแก้ปัญหาเกี่ยวกับคอลเล็กชันที่ไม่เสถียรได้หลายวิธี ส่วนย่อยต่อไปนี้จะอธิบายแนวทางต่างๆ เหล่านี้
ไฟล์การกําหนดค่า
หากยินดีปฏิบัติตามสัญญาความเสถียรในโค้ดเบส คุณสามารถเลือกให้ถือว่าคอลเล็กชัน Kotlin เสถียรได้โดยเพิ่ม kotlin.collections.*
ลงในไฟล์การกําหนดค่าความเสถียร
คอลเล็กชันแบบคงที่
คุณสามารถใช้คอลเล็กชันที่เปลี่ยนแปลงไม่ได้ของ kotlinx แทน List
เพื่อความปลอดภัยของเวลาที่คอมไพล์ไม่ได้
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
หากใช้คอลเล็กชันแบบแก้ไขไม่ได้ไม่ได้ คุณก็สร้างคอลเล็กชันของคุณเองได้ โดยให้ใส่ List
ในคลาสที่เสถียรซึ่งมีคำอธิบายประกอบ Wrapper ทั่วไปน่าจะเป็นตัวเลือกที่ดีที่สุด ทั้งนี้ขึ้นอยู่กับข้อกำหนดของคุณ
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
จากนั้นคุณจะใช้ข้อมูลนี้เป็นประเภทของพารามิเตอร์ในคอมโพสิเบิลได้
@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 ของคอมไพเลอร์
composeCompiler {
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}
เนื่องจากคอมไพเลอร์ Compose ทำงานอยู่ในแต่ละโมดูลในโปรเจ็กต์ของคุณแยกกัน คุณจึงสามารถระบุการกำหนดค่าที่แตกต่างกันให้กับโมดูลต่างๆ ได้หากต้องการ หรือจะกำหนดค่า 1 รายการที่ระดับรูทของโปรเจ็กต์และส่งเส้นทางนั้นไปยังแต่ละโมดูลก็ได้
หลายโมดูล
ปัญหาที่พบบ่อยอีกอย่างหนึ่งเกี่ยวข้องกับสถาปัตยกรรมแบบหลายโมดูล คอมไพเลอร์ Compose จะอนุมานได้ว่าคลาสมีเสถียรหรือไม่ก็ต่อเมื่อมีการทําเครื่องหมายประเภทที่ไม่ใช่ประเภทพื้นฐานทั้งหมดที่คลาสอ้างอิงว่าเสถียรอย่างชัดเจน หรืออยู่ในโมดูลที่สร้างขึ้นด้วยคอมไพเลอร์ Compose ด้วย
หากชั้นข้อมูลอยู่ในโมดูลแยกต่างหากจากชั้น UI ซึ่งเป็นแนวทางที่แนะนํา คุณอาจพบปัญหานี้
โซลูชัน
ในการแก้ไขปัญหานี้ คุณสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้
- เพิ่มคลาสลงในไฟล์การกําหนดค่าคอมไพเลอร์
- เปิดใช้คอมไพเลอร์ Compose ในโมดูลชั้นข้อมูลหรือติดแท็กชั้นเรียนด้วย
@Stable
หรือ@Immutable
ตามความเหมาะสม- ซึ่งเกี่ยวข้องกับการเพิ่มการพึ่งพา Compose ลงในเลเยอร์ข้อมูล อย่างไรก็ตาม เป็นเพียงข้อกําหนดของรันไทม์ Compose ไม่ใช่ของ
Compose-UI
- ซึ่งเกี่ยวข้องกับการเพิ่มการพึ่งพา Compose ลงในเลเยอร์ข้อมูล อย่างไรก็ตาม เป็นเพียงข้อกําหนดของรันไทม์ Compose ไม่ใช่ของ
- ภายในโมดูล UI ให้รวมคลาสชั้นข้อมูลไว้ในคลาส Wrapper เฉพาะสำหรับ UI
ปัญหาเดียวกันนี้ยังเกิดขึ้นเมื่อใช้ไลบรารีภายนอกด้วยหากไม่ได้ใช้คอมไพเลอร์ Compose
เนื้อหาคอมโพสพอยต์บางรายการไม่ควรข้ามได้
เมื่อพยายามแก้ไขปัญหาเกี่ยวกับความเสถียร คุณไม่ควรพยายามทำให้องค์ประกอบแบบคอมโพสิเบิลทุกรายการข้ามได้ การพยายามทำเช่นนั้นอาจนำไปสู่การเพิ่มประสิทธิภาพล่วงหน้า ที่ทำให้เกิดปัญหามากกว่าที่จะแก้ไขได้
มีหลายกรณีที่การข้ามโฆษณาไม่มีประโยชน์จริง และอาจทําให้โค้ดดูแลรักษาได้ยาก เช่น
- Composable ที่ไม่ได้เขียนใหม่บ่อยครั้งหรือไม่ได้เลย
- คอมโพสิเบิลที่เรียกใช้คอมโพสิเบิลที่ข้ามได้
- Composable ที่มีพารามิเตอร์จำนวนมากซึ่งมีการติดตั้งใช้งานราคาแพงเท่ากัน ในกรณีนี้ ค่าใช้จ่ายในการตรวจสอบว่าพารามิเตอร์ใดมีการเปลี่ยนแปลงหรือไม่อาจมากกว่าค่าใช้จ่ายในการเปลี่ยนองค์ประกอบใหม่ในราคาไม่แพง
เมื่อคอมโพสิเบิลข้ามได้ ระบบจะเพิ่มค่าใช้จ่ายเล็กน้อยซึ่งอาจไม่คุ้มค่า คุณยังกำกับเนื้อหาคอมโพสิเบิลให้ไม่สามารถรีสตาร์ทได้ในกรณีที่คุณพิจารณาว่าการรีสตาร์ทมีค่าใช้จ่ายมากกว่าที่ควรจะเป็น