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

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 (หรือเมื่อปิดใช้โหมดการข้ามที่เข้มงวด) Compose จะจัดองค์ประกอบ Composable ที่มีพารามิเตอร์ที่ไม่เสถียรใหม่เสมอเมื่อองค์ประกอบหลักจัดองค์ประกอบใหม่

หากแอปของคุณจัดสรรอินสแตนซ์ใหม่ของพารามิเตอร์ที่ไม่เสถียรบ่อยครั้ง หรือบังคับให้มีการตรวจสอบ .equals() ที่มีค่าใช้จ่ายสูงในคอลเล็กชันขนาดใหญ่โดยการใส่คำอธิบายประกอบโมเดลมากเกินไป คุณอาจพบการจัดองค์ประกอบใหม่หรือโอเวอร์เฮดการเปรียบเทียบที่ไม่จำเป็น

เอกสารนี้แสดงรายละเอียดเกี่ยวกับวิธีที่ Compose กำหนดความเสถียรและวิธีเพิ่มประสิทธิภาพเพื่อปรับปรุงประสิทธิภาพและประสบการณ์ของผู้ใช้โดยรวม

ออบเจ็กต์ที่เปลี่ยนแปลงไม่ได้

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

คลาส Contact เป็นคลาสข้อมูลที่เปลี่ยนแปลงไม่ได้ เนื่องจากพารามิเตอร์ทั้งหมดเป็นค่าพื้นฐานที่กำหนดด้วยคีย์เวิร์ด val เมื่อสร้างอินสแตนซ์ของ Contact แล้ว คุณจะเปลี่ยนค่าของพร็อพเพอร์ตี้ของออบเจ็กต์ไม่ได้ หากพยายามทำเช่นนั้น คุณจะสร้างออบเจ็กต์ใหม่

data class Contact(val name: String, val number: String)

คอมโพสิเบิล 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 ทำงานกับโค้ดของคุณ ระบบจะทำเครื่องหมายแต่ละฟังก์ชันและประเภทด้วยแท็กอย่างน้อย 1 แท็ก แท็กเหล่านี้แสดงให้เห็นว่า Compose จัดการฟังก์ชันหรือประเภทอย่างไรในระหว่างการจัดองค์ประกอบใหม่

ฟังก์ชัน

Compose สามารถทำเครื่องหมายฟังก์ชันเป็น skippable หรือ restartable ได้ โปรดทราบว่าระบบอาจทำเครื่องหมายฟังก์ชันเป็นอย่างใดอย่างหนึ่ง ทั้ง 2 อย่าง หรือไม่ทำเครื่องหมายเลย ดังนี้

  • ข้ามได้: หากคอมไพเลอร์ทำเครื่องหมาย Composable ว่าข้ามได้ Compose จะข้าม Composable นั้นในระหว่างการจัดองค์ประกอบใหม่ได้หากอาร์กิวเมนต์ทั้งหมดเท่ากับค่าก่อนหน้า (เมื่อใช้โหมดการข้ามที่เข้มงวดใน Kotlin 2.0.20 ขึ้นไป คอมโพสิเบิลที่รีสตาร์ทได้ทั้งหมดจะข้ามได้โดยอัตโนมัติ)
  • รีสตาร์ทได้: 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 ขึ้นไป) คอลเล็กชันที่ไม่เสถียรจะยังคงข้ามการจัดองค์ประกอบใหม่โดยใช้ความเท่ากันของอินสแตนซ์ (===) เมื่อการอ้างอิงคอลเล็กชันไม่เปลี่ยนแปลง โปรดระมัดระวังในการแทนที่คอลเล็กชันมาตรฐานด้วยคอลเล็กชันที่เปลี่ยนแปลงไม่ได้ของ Kotlinx หรือการใส่คำอธิบายประกอบคลาสที่เก็บคอลเล็กชันด้วย @Immutable หรือ @Stable เนื่องจากการเปรียบเทียบทุกองค์ประกอบกับ .equals() ในการส่งผ่านการจัดองค์ประกอบใหม่แต่ละครั้งอาจมีค่าใช้จ่ายสูงกว่าการจัดองค์ประกอบใหม่เอง
  • โมดูลอื่นๆ: Compose จะพิจารณาคลาสจากโมดูลที่คอมไพเลอร์ Compose ไม่ทำงานว่าไม่เสถียร (เปรียบเทียบโดยใช้ === ภายใต้โหมดการข้ามแบบเข้มงวด) หากแหล่งข้อมูลสร้างอินสแตนซ์โมเดลขนาดเล็กแบบแบนซ้ำๆ จากโมดูลที่ไม่ใช่ Compose ที่มีค่าเหมือนกัน คุณสามารถกำหนดค่าไฟล์การกำหนดค่าความเสถียรหรือใช้ @Stable หรือ @Immutable โดยที่ .equals() มีค่าใช้จ่ายต่ำ

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