ความเสถียรในการเขียน

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สถานะเปลี่ยนไป ดังนี้

  1. Compose จะประเมินว่าควรจัดองค์ประกอบโค้ดภายใน ContactRow ใหม่หรือไม่
  2. โดยจะเห็นว่าอาร์กิวเมนต์เดียวสำหรับ ContactDetails คือประเภท Contact
  3. เนื่องจาก Contact เป็นคลาสข้อมูลที่ไม่เปลี่ยนแปลง Compose จึงมั่นใจได้ว่าอาร์กิวเมนต์สำหรับ ContactDetails จะไม่มีการเปลี่ยนแปลง
  4. ดังนั้น Compose จึงข้าม ContactDetails และไม่แต่งใหม่
  5. ในทางกลับกัน อาร์กิวเมนต์สำหรับ 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 จะทำเครื่องหมายประเภทเป็นเปลี่ยนแปลงไม่ได้หากค่าของพร็อพเพอร์ตี้ไม่สามารถเปลี่ยนแปลงได้ และเมธอดทั้งหมดมีความโปร่งใสเชิงอ้างอิง
    • โปรดทราบว่าระบบจะทําเครื่องหมายประเภทดั้งเดิมทั้งหมดว่าเปลี่ยนแปลงไม่ได้ ซึ่งรวมถึงString Int และ 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() มีราคาถูก

อ่านเพิ่มเติม