Compose จะพิจารณาว่าประเภทต่างๆ เสถียรหรือไม่เสถียร ประเภทจะคงที่หาก เปลี่ยนแปลงไม่ได้ หรือหาก Compose สามารถทราบว่าค่ามีการ เปลี่ยนแปลงระหว่างการจัดองค์ประกอบใหม่หรือไม่ ประเภทจะไม่เสถียรหาก Compose ไม่ทราบว่าค่าของประเภทนั้นมีการเปลี่ยนแปลงระหว่างการจัดองค์ประกอบใหม่หรือไม่
Compose ใช้ความเสถียรของพารามิเตอร์ของ Composable เพื่อกำหนดวิธี เปรียบเทียบอินพุตและตัดสินใจว่าจะข้าม Composable ระหว่างการประกอบใหม่ได้หรือไม่ (โดยค่าเริ่มต้นจะเปิดใช้โหมดข้ามอย่างเข้มงวดตั้งแต่ Kotlin 2.0.20 เป็นต้นไป)
- พารามิเตอร์ที่เสถียร: Compose จะเปรียบเทียบพารามิเตอร์ที่เสถียรโดยใช้ความเท่าเทียมกันเชิงโครงสร้าง (
Object.equals()) หากพารามิเตอร์ที่เสถียรของ Composable เท่ากับค่าก่อนหน้า Compose จะข้ามพารามิเตอร์นั้น - พารามิเตอร์ที่ไม่เสถียร: เมื่อเปิดใช้โหมดข้ามอย่างเข้มงวด (ค่าเริ่มต้นใน Kotlin 2.0.20 ขึ้นไป) Compose จะเปรียบเทียบพารามิเตอร์ที่ไม่เสถียรโดยใช้ความเท่าเทียมกันของอินสแตนซ์ (
===) และข้าม Composable หากมีการส่งอินสแตนซ์ออบเจ็กต์เดียวกัน ในเวอร์ชันก่อน Kotlin 2.0.20 (หรือเมื่อปิดใช้โหมด Strong skipping) Compose จะทำการ Recompose Composable ที่มีพารามิเตอร์ที่ไม่เสถียรเสมอเมื่อ Composable ระดับบนทำการ Recompose
หากแอปของคุณจัดสรรอินสแตนซ์ใหม่ของพารามิเตอร์ที่ไม่เสถียรบ่อยครั้ง หรือบังคับให้มีการ.equals()ตรวจสอบที่มีค่าใช้จ่ายสูงในคอลเล็กชันขนาดใหญ่โดยการใส่คำอธิบายประกอบโมเดลมากเกินไป คุณอาจเห็นการจัดองค์ประกอบใหม่ที่ไม่จำเป็นหรือค่าใช้จ่ายในการเปรียบเทียบ
เอกสารนี้แสดงรายละเอียดเกี่ยวกับวิธีที่ Compose กำหนดความเสถียรและวิธีเพิ่มประสิทธิภาพเพื่อปรับปรุงประสิทธิภาพและประสบการณ์ของผู้ใช้โดยรวม
ออบเจ็กต์ที่ไม่เปลี่ยนแปลง
ข้อมูลโค้ดต่อไปนี้แสดงหลักการทั่วไปที่อยู่เบื้องหลังความเสถียรและการ ประกอบใหม่
คลาส Contact เป็นคลาสข้อมูลที่เปลี่ยนแปลงไม่ได้ เนื่องจากพารามิเตอร์ทั้งหมดเป็นพารามิเตอร์พื้นฐานที่กำหนดด้วยคีย์เวิร์ด val เมื่อสร้างอินสแตนซ์ของ Contact แล้ว คุณจะเปลี่ยนค่าของพร็อพเพอร์ตี้ของออบเจ็กต์ไม่ได้
หากคุณพยายามทำเช่นนั้น คุณจะสร้างออบเจ็กต์ใหม่
data class Contact(val name: String, val number: String)
Composable ContactRow มีพารามิเตอร์ประเภท Contact
@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
var selected by remember { mutableStateOf(false) }
Row(modifier) {
ContactDetails(contact)
ToggleButton(selected, onToggled = { selected = !selected })
}
}
พิจารณาสิ่งที่จะเกิดขึ้นเมื่อผู้ใช้คลิกปุ่มเปิด/ปิดและselectedสถานะเปลี่ยนไป ดังนี้
- Compose จะประเมินว่าควรจัดองค์ประกอบโค้ดภายใน
ContactRowใหม่หรือไม่ - โดยจะเห็นว่าอาร์กิวเมนต์เดียวสำหรับ
ContactDetailsคือประเภทContact - เนื่องจาก
Contactเป็นคลาสข้อมูลที่ไม่เปลี่ยนแปลง Compose จึงมั่นใจได้ว่าอาร์กิวเมนต์สำหรับContactDetailsจะไม่มีการเปลี่ยนแปลง - ดังนั้น Compose จึงข้าม
ContactDetailsและไม่แต่งใหม่ - ในทางกลับกัน อาร์กิวเมนต์สำหรับ
ToggleButtonมีการเปลี่ยนแปลง และ Compose จะทำการรวมคอมโพเนนต์นั้นอีกครั้ง
ออบเจ็กต์ที่เปลี่ยนแปลงได้
แม้ว่าตัวอย่างก่อนหน้าจะใช้ออบเจ็กต์ที่ไม่เปลี่ยนแปลง แต่ก็สามารถสร้างออบเจ็กต์ที่เปลี่ยนแปลงได้ ลองพิจารณาข้อมูลโค้ดต่อไปนี้
data class Contact(var name: String, var number: String)
เนื่องจากตอนนี้พารามิเตอร์แต่ละรายการของ Contact เป็น var แล้ว คลาสจึงเปลี่ยนแปลงได้
หากพร็อพเพอร์ตี้มีการเปลี่ยนแปลง Compose ก็จะไม่รับรู้ เนื่องจาก Compose จะติดตามเฉพาะการเปลี่ยนแปลงออบเจ็กต์ State ของ Compose
Compose ถือว่าคลาสดังกล่าวไม่เสถียร เมื่อใช้โหมดข้ามอย่างเข้มงวด
(Kotlin 2.0.20 ขึ้นไป) Compose จะเปรียบเทียบ Contact โดยใช้ความเท่ากันของอินสแตนซ์ (===) ดังนี้
การเปลี่ยนแปลง contact.name ในที่เดิมจะไม่ทริกเกอร์การจัดองค์ประกอบใหม่ ในขณะที่การส่งอินสแตนซ์ Contact ที่เพิ่งจัดสรรใหม่ซึ่งมีค่าเหมือนกันจะยังคงบังคับให้ ContactDetails จัดองค์ประกอบใหม่ (และหากไม่มีโหมดข้ามอย่างเข้มงวด ContactDetails จะจัดองค์ประกอบใหม่ทุกครั้งที่ selected เปลี่ยนแปลง)
การใช้งานใน Compose
การพิจารณาว่า Compose จะกำหนดฟังก์ชันที่จะข้ามระหว่างการจัดองค์ประกอบใหม่ได้อย่างไรนั้นอาจเป็นประโยชน์ แต่ก็ไม่ใช่สิ่งจำเป็น
เมื่อคอมไพเลอร์ Compose ทำงานในโค้ดของคุณ คอมไพเลอร์จะทำเครื่องหมายแต่ละฟังก์ชันและประเภท ด้วยแท็กใดแท็กหนึ่ง แท็กเหล่านี้แสดงให้เห็นว่า Compose จัดการฟังก์ชันหรือ ประเภทอย่างไรในระหว่างการเขียนใหม่
ฟังก์ชัน
Compose สามารถทำเครื่องหมายฟังก์ชันเป็น skippable หรือ restartable ได้ โปรดทราบว่าฟีเจอร์นี้อาจ
ทำเครื่องหมายฟังก์ชันเป็นอย่างใดอย่างหนึ่ง ทั้ง 2 อย่าง หรือไม่เป็นอย่างใดเลย ดังนี้
- ข้ามได้: หากคอมไพเลอร์ทําเครื่องหมาย Composable เป็นข้ามได้ Compose จะ ข้าม Composable นั้นระหว่างการประกอบใหม่ได้ หากอาร์กิวเมนต์ทั้งหมดมีค่าเท่ากับค่าก่อนหน้า (เมื่อใช้โหมดข้ามอย่างเข้มงวดใน Kotlin 2.0.20 ขึ้นไป คอมโพสเซเบิลที่รีสตาร์ทได้ทั้งหมดจะข้ามได้โดยอัตโนมัติ)
- Restartable: Composable ที่รีสตาร์ทได้จะทำหน้าที่เป็น "ขอบเขต" ที่ การประกอบใหม่จะเริ่มต้นได้ กล่าวคือ ฟังก์ชันนี้อาจเป็นจุดเริ่มต้นที่ Compose สามารถเริ่มเรียกใช้โค้ดอีกครั้งสำหรับการจัดองค์ประกอบใหม่หลังจากมีการเปลี่ยนแปลงสถานะ
ประเภท
ประเภทเครื่องหมาย Compose จะเป็นแบบเปลี่ยนแปลงไม่ได้หรือเสถียร แต่ละประเภทจะเป็นอย่างใดอย่างหนึ่งต่อไปนี้
- เปลี่ยนแปลงไม่ได้: Compose จะทำเครื่องหมายประเภทเป็นเปลี่ยนแปลงไม่ได้หากค่าของพร็อพเพอร์ตี้ไม่สามารถเปลี่ยนแปลงได้ และเมธอดทั้งหมดมีความโปร่งใสเชิงอ้างอิง
- โปรดทราบว่าระบบจะทําเครื่องหมายประเภทดั้งเดิมทั้งหมดว่าเปลี่ยนแปลงไม่ได้ ซึ่งรวมถึง
StringIntและFloat
- โปรดทราบว่าระบบจะทําเครื่องหมายประเภทดั้งเดิมทั้งหมดว่าเปลี่ยนแปลงไม่ได้ ซึ่งรวมถึง
- เสถียร: ระบุประเภทที่มีพร็อพเพอร์ตี้ที่เปลี่ยนแปลงได้หลังจากสร้าง หากพร็อพเพอร์ตีเหล่านั้นมีการเปลี่ยนแปลงในระหว่างรันไทม์ Compose จะรับรู้ถึงการเปลี่ยนแปลงเหล่านั้น
ความเสถียรในการแก้ไขข้อบกพร่อง
หากแอปของคุณกำลังเขียน Composable ใหม่ซึ่งพารามิเตอร์ไม่ได้เปลี่ยนแปลง ให้ตรวจสอบก่อนว่ามีการจัดสรรอินสแตนซ์ใหม่ของประเภทที่ไม่เสถียรในการเขียนใหม่ทุกครั้งหรือไม่ (หรือหากปิดใช้โหมดข้ามอย่างเข้มงวด ให้ตรวจสอบว่า Composable มีพารามิเตอร์ที่มีพร็อพเพอร์ตี้ var หรือพร็อพเพอร์ตี้ val ของประเภทที่ไม่เสถียรหรือไม่)
ดูข้อมูลโดยละเอียดเกี่ยวกับวิธีวินิจฉัยปัญหาที่ซับซ้อนเกี่ยวกับความเสถียรใน Compose ได้ที่คู่มือแก้ไขข้อบกพร่องด้านความเสถียร
แก้ไขปัญหาด้านความเสถียร
ดูข้อมูลเกี่ยวกับวิธีเพิ่มความเสถียรให้กับการใช้งาน Compose ได้ที่คู่มือแก้ไขปัญหาด้านความเสถียร
สรุป
โดยรวมแล้ว คุณควรทราบประเด็นต่อไปนี้
- พารามิเตอร์: Compose จะกำหนดความเสถียรของพารามิเตอร์แต่ละรายการของ
Composable เพื่อตัดสินใจว่าจะเปรียบเทียบโดยใช้ความเท่าเทียมเชิงโครงสร้าง
(
.equals()) หรือความเท่าเทียมของอินสแตนซ์ (===) ระหว่างการจัดองค์ประกอบใหม่ - การแก้ไขทันที: หากสังเกตเห็นว่าระบบไม่ได้ข้าม Composable และ
ทำให้เกิดปัญหาด้านประสิทธิภาพ ให้ตรวจสอบว่าระบบกำลังสร้างอินสแตนซ์ใหม่ของพารามิเตอร์ที่ไม่เสถียร
ในแต่ละรอบหรือไม่ หรือกำลังใช้พร็อพเพอร์ตี้
varแทนStateหรือไม่ - รายงานคอมไพเลอร์: คุณสามารถใช้รายงานคอมไพเลอร์เพื่อ พิจารณาว่ามีการอนุมานความเสถียรเกี่ยวกับคลาสของคุณอย่างไร
- คอลเล็กชัน: Compose ถือว่าอินเทอร์เฟซคอลเล็กชันมาตรฐาน (
List,SetและMap) ไม่เสถียรเนื่องจากการติดตั้งใช้งานพื้นฐานอาจมีการเปลี่ยนแปลง เมื่อใช้โหมดข้ามอย่างเข้มงวด (เปิดใช้โดยค่าเริ่มต้นใน Kotlin 2.0.20 ขึ้นไป) คอลเล็กชันที่ไม่เสถียรจะยังคงข้ามการประกอบใหม่โดยใช้O(1)ความเท่ากันของอินสแตนซ์อย่างรวดเร็ว (===) เมื่อการอ้างอิงคอลเล็กชันไม่เปลี่ยนแปลง หลีกเลี่ยงการแปลงคอลเล็กชันขนาดใหญ่เป็น Kotlinx immutable collections หรือ การใส่คำอธิบายประกอบในคลาสที่เก็บคอลเล็กชันด้วย@Immutableหรือ@Stableเว้นแต่ จะต้องใช้ความเท่าเทียมกันเชิงโครงสร้าง (O(N).equals()) โดยเฉพาะ เนื่องจาก การเปรียบเทียบองค์ประกอบทุกรายการในการประกอบใหม่แต่ละครั้งอาจมีค่าใช้จ่ายมากกว่า การประกอบใหม่เอง - โมดูลอื่นๆ: Compose จะถือว่าคลาสจากโมดูลที่คอมไพเลอร์ Compose
ไม่ได้เรียกใช้เป็นคลาสที่ไม่เสถียร (เปรียบเทียบโดยใช้
===ในโหมดข้ามอย่างเข้มงวด) หากแหล่งข้อมูลสร้างอินสแตนซ์ใหม่ของโมเดลขนาดเล็กแบบเรียบ จากโมดูลที่ไม่ใช่ Compose ที่มีค่าเหมือนกันบ่อยๆ คุณสามารถกำหนดค่า ไฟล์การกำหนดค่าความเสถียรหรือใช้@Stableหรือ@Immutableได้ โดยที่.equals()มีราคาถูก
อ่านเพิ่มเติม
- ประสิทธิภาพ: ดูเคล็ดลับการแก้ไขข้อบกพร่องเพิ่มเติมเกี่ยวกับประสิทธิภาพของ Compose ได้ที่ คู่มือแนวทางปฏิบัติแนะนำและการพูดคุยใน I/O