ในหน้านี้ คุณจะได้เรียนรู้เกี่ยวกับวงจรของ Composable และวิธีที่ Compose ตัดสินว่า Composable ต้องมีการจัดองค์ประกอบใหม่หรือไม่
ภาพรวมวงจร
ดังที่กล่าวไว้ในเอกสารประกอบเกี่ยวกับการจัดการสถานะ องค์ประกอบ การจัดองค์ประกอบจะอธิบาย UI ของแอปและสร้างขึ้นโดยการเรียกใช้ Composable Composition คือโครงสร้างแบบต้นไม้ของ Composable ที่อธิบาย UI
เมื่อ Jetpack Compose เรียกใช้ Composable เป็นครั้งแรกในระหว่างการ คอมโพสครั้งแรก ระบบจะติดตาม Composable ที่คุณเรียกใช้เพื่ออธิบาย UI ใน Composition จากนั้นเมื่อสถานะของแอปเปลี่ยนแปลง Jetpack Compose จะกำหนดเวลาการจัดองค์ประกอบใหม่ การประกอบใหม่คือเมื่อ Jetpack Compose เรียกใช้ Composable อีกครั้งซึ่งอาจมีการเปลี่ยนแปลงเพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ แล้วอัปเดต Composition เพื่อแสดงการเปลี่ยนแปลง
โดยผลงานจะสร้างได้จากผลงานเริ่มต้นเท่านั้น และจะอัปเดตได้โดยการเรียบเรียงใหม่ วิธีเดียวในการแก้ไของค์ประกอบคือการจัดองค์ประกอบใหม่
โดยปกติแล้ว การประกอบใหม่จะเกิดขึ้นเมื่อมีการเปลี่ยนแปลงออบเจ็กต์
State<T> Compose
จะติดตามสิ่งเหล่านี้และเรียกใช้ Composable ทั้งหมดใน Composition ที่อ่านState<T>นั้นๆ และ Composable ใดๆ ที่เรียกใช้ Composable นั้นซึ่งข้ามไม่ได้
หากมีการเรียกใช้ Composable หลายครั้ง ระบบจะวางอินสแตนซ์หลายรายการไว้ใน Composition การเรียกใช้แต่ละครั้งจะมีวงจรของตัวเองในการเรียบเรียง
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
MyComposable ในผลงาน หากเรียกใช้
Composable หลายครั้ง ระบบจะวางอินสแตนซ์หลายรายการไว้ใน
Composition องค์ประกอบที่มีสีแตกต่างกันบ่งบอกว่าองค์ประกอบนั้นเป็นอินสแตนซ์แยกกันโครงสร้างของ Composable ใน Composition
อินสแตนซ์ของ Composable ใน Composition จะระบุโดยตำแหน่งที่เรียกใช้ คอมไพเลอร์ Compose จะถือว่าแต่ละตำแหน่งที่เรียกใช้เป็นตำแหน่งที่แตกต่างกัน การเรียกใช้ Composable จากตำแหน่งการเรียกใช้หลายตำแหน่งจะสร้างอินสแตนซ์หลายรายการของ Composable ใน Composition
หากในระหว่างการจัดองค์ประกอบใหม่ Composable เรียก Composable อื่นๆ ที่แตกต่างจากที่เรียก ในระหว่างการจัดองค์ประกอบก่อนหน้า Compose จะระบุว่า Composable ใดถูกเรียก หรือไม่ถูกเรียก และสำหรับ Composable ที่ถูกเรียกในการจัดองค์ประกอบทั้ง 2 รายการ Compose จะหลีกเลี่ยงการจัดองค์ประกอบใหม่หากอินพุตของ Composable นั้นไม่เปลี่ยนแปลง
การรักษาข้อมูลระบุตัวตนเป็นสิ่งสำคัญในการเชื่อมโยงผลข้างเคียงกับองค์ประกอบที่ใช้ร่วมกัน เพื่อให้ดำเนินการเสร็จสมบูรณ์ได้แทนที่จะต้องรีสตาร์ททุกครั้ง ที่มีการจัดองค์ประกอบใหม่
ลองดูตัวอย่างต่อไปนี้
@Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() // This call site affects where LoginInput is placed in Composition } @Composable fun LoginInput() { /* ... */ } @Composable fun LoginError() { /* ... */ }
ในข้อมูลโค้ดด้านบน LoginScreen จะเรียกใช้
LoginError Composable แบบมีเงื่อนไข และจะเรียกใช้ LoginInput Composable เสมอ การเรียกใช้แต่ละครั้งมีตำแหน่งการเรียกใช้และตำแหน่งแหล่งที่มาที่ไม่ซ้ำกัน ซึ่งคอมไพเลอร์จะใช้เพื่อระบุการเรียกใช้ดังกล่าวแบบไม่ซ้ำกัน
LoginScreen ใน Composition เมื่อสถานะ
เปลี่ยนและเกิดการจัดองค์ประกอบใหม่ สีเดียวกันหมายความว่ายังไม่ได้จัดองค์ประกอบใหม่แม้ว่า LoginInput จะเปลี่ยนจากถูกเรียกใช้เป็นอันดับแรกไปเป็นอันดับที่สอง
อินสแตนซ์ LoginInput จะยังคงอยู่ในการจัดองค์ประกอบใหม่ นอกจากนี้ เนื่องจาก LoginInput ไม่มีพารามิเตอร์ใดๆ ที่เปลี่ยนแปลงในการจัดองค์ประกอบใหม่ Compose จึงจะข้ามการเรียก LoginInput
เพิ่มข้อมูลเพิ่มเติมเพื่อช่วยในการจัดองค์ประกอบใหม่แบบอัจฉริยะ
การเรียกใช้ Composable หลายครั้งจะเพิ่มลงใน Composition หลายครั้งด้วย เมื่อเรียกใช้ Composable หลายครั้งจากตำแหน่งการเรียกเดียวกัน Compose จะไม่มีข้อมูลใดๆ ที่จะระบุการเรียกแต่ละครั้งไปยัง Composable นั้นๆ ได้อย่างไม่ซ้ำกัน ดังนั้นจึงใช้ลำดับการดำเนินการนอกเหนือจากตำแหน่งการเรียกเพื่อรักษา อินสแตนซ์ให้แตกต่างกัน บางครั้งลักษณะการทำงานนี้ก็เพียงพอแล้ว แต่ในบางกรณีอาจทำให้เกิดลักษณะการทำงานที่ไม่ต้องการ
@Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { // MovieOverview composables are placed in Composition given its // index position in the for loop MovieOverview(movie) } } }
ในตัวอย่างด้านบน Compose ใช้ลำดับการดำเนินการนอกเหนือจากเว็บไซต์การโทร
เพื่อรักษาอินสแตนซ์ให้แตกต่างกันในการจัดองค์ประกอบ หากมีการเพิ่ม movie ใหม่ที่ด้านล่างของรายการ Compose จะใช้ซ้ำอินสแตนซ์ที่มีอยู่แล้วใน Composition ได้เนื่องจากตำแหน่งในรายการไม่เปลี่ยนแปลง และอินพุต movie จึงเหมือนกันสำหรับอินสแตนซ์เหล่านั้น
MoviesScreen ในการจัดองค์ประกอบเมื่อเพิ่มองค์ประกอบใหม่ที่ด้านล่างของรายการ MovieOverview ที่ใช้ใน
Composition สามารถนำกลับมาใช้ใหม่ได้ สีเดียวกันใน MovieOverview หมายความว่า Composable
ยังไม่ได้ถูก Recomposeอย่างไรก็ตาม หากmoviesมีการเปลี่ยนแปลงโดยการเพิ่มลงในส่วนบนหรือส่วนกลางของรายการ การนำออก หรือการจัดเรียงรายการใหม่ จะทำให้เกิดการจัดองค์ประกอบใหม่
ในการเรียก MovieOverview ทั้งหมดซึ่งพารามิเตอร์อินพุตมีการเปลี่ยนตำแหน่งในรายการ
ซึ่งเป็นสิ่งสำคัญอย่างยิ่งหาก MovieOverview ดึงข้อมูลรูปภาพภาพยนตร์โดยใช้ผลข้างเคียง หากมีการจัดองค์ประกอบใหม่ขณะที่เอฟเฟกต์กำลัง
ดำเนินการอยู่ ระบบจะยกเลิกและเริ่มอีกครั้ง
@Composable fun MovieOverview(movie: Movie) { Column { // Side effect explained later in the docs. If MovieOverview // recomposes, while fetching the image is in progress, // it is cancelled and restarted. val image = loadNetworkImage(movie.url) MovieHeader(image) /* ... */ } }
MoviesScreen ในการจัดองค์ประกอบเมื่อมีการเพิ่มองค์ประกอบใหม่ลงในรายการ MovieOverview Composable จะนำกลับมาใช้ใหม่ไม่ได้และ
ผลข้างเคียงทั้งหมดจะรีสตาร์ท สีที่แตกต่างใน MovieOverview หมายความว่า
Composable ได้รับการประกอบใหม่ในอุดมคติ เราต้องการพิจารณาตัวตนของMovieOverviewอินสแตนซ์เป็น
ลิงก์กับตัวตนของmovieที่ส่งไปยังอินสแตนซ์ หากเราเรียงลำดับรายการภาพยนตร์ใหม่ เราควรเรียงลำดับอินสแตนซ์ใน
Composition tree ใหม่ในลักษณะเดียวกันแทนที่จะสร้าง MovieOverview composable แต่ละรายการใหม่ด้วยอินสแตนซ์ภาพยนตร์ที่แตกต่างกัน
Compose ช่วยให้คุณบอกรันไทม์ได้ว่า
ต้องการใช้ค่าใดเพื่อระบุส่วนที่ต้องการของทรี ซึ่งก็คือ
key
Composable
การครอบบล็อกโค้ดด้วยการเรียกใช้คีย์ที่ใช้ร่วมกันได้โดยมีค่าอย่างน้อย 1 ค่าที่ส่งเข้ามา ระบบจะรวมค่าเหล่านั้นเพื่อใช้ระบุอินสแตนซ์นั้นในการจัดองค์ประกอบ ค่าสำหรับ key ไม่จำเป็นต้องไม่ซ้ำกันทั่วโลก แต่ต้องไม่ซ้ำกันเฉพาะในหมู่การเรียกใช้
Composable ที่ตำแหน่งการเรียก ดังนั้นในตัวอย่างนี้ movie แต่ละรายการต้องมี key ที่ไม่ซ้ำกันในกลุ่ม movies และใช้ key ร่วมกับ
Composable อื่นๆ ในแอปได้
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
ด้วยวิธีข้างต้น แม้ว่าองค์ประกอบในรายการจะเปลี่ยนแปลง แต่ Compose จะจดจำการเรียกแต่ละรายการไปยัง MovieOverview และนำกลับมาใช้ใหม่ได้
MoviesScreen ในการจัดองค์ประกอบเมื่อมีการเพิ่มองค์ประกอบใหม่ลงในรายการ เนื่องจาก MovieOverview Composable มีคีย์ที่ไม่ซ้ำกัน
Compose จึงจดจำอินสแตนซ์ MovieOverview ที่ไม่มีการเปลี่ยนแปลงได้ และ
นำกลับมาใช้ใหม่ได้ โดยเอฟเฟกต์ข้างเคียงจะยังคงทำงานต่อไปComposable บางรายการมีการรองรับ Composable key ในตัว เช่น
LazyColumn ยอมรับการระบุ key ที่กำหนดเองใน DSL ของ items
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
ข้ามหากอินพุตไม่มีการเปลี่ยนแปลง
ในระหว่างการประกอบใหม่ ฟังก์ชันที่ประกอบกันได้ที่มีสิทธิ์บางฟังก์ชันอาจข้ามการดำเนินการทั้งหมดได้หากอินพุตไม่เปลี่ยนแปลงจากการประกอบก่อนหน้า
ฟังก์ชันที่ประกอบกันได้จะมีสิทธิ์ข้ามเว้นแต่
- ฟังก์ชันมีประเภทการคืนค่าที่ไม่ใช่
Unit - ฟังก์ชันมีคำอธิบายประกอบด้วย
@NonRestartableComposableหรือ@NonSkippableComposable - พารามิเตอร์ที่จำเป็นมีประเภทที่ไม่เสถียร
มีโหมดคอมไพเลอร์เวอร์ชันทดลอง Strong Skipping ซึ่งผ่อนปรนข้อกำหนดสุดท้าย
ประเภทต้องเป็นไปตามสัญญาต่อไปนี้จึงจะถือว่าเสถียร
- ผลลัพธ์ของ
equalsสำหรับ 2 อินสแตนซ์จะเหมือนกันเสมอสำหรับ 2 อินสแตนซ์เดียวกัน - หากมีการเปลี่ยนแปลงพร็อพเพอร์ตี้สาธารณะของประเภท ระบบจะแจ้งให้ Composition ทราบ
- นอกจากนี้ ประเภทพร็อพเพอร์ตี้สาธารณะทั้งหมดก็มีความเสถียรเช่นกัน
มีประเภททั่วไปที่สำคัญบางประเภทที่อยู่ในสัญญาฉบับนี้ ซึ่งคอมไพเลอร์ Compose จะถือว่ามีเสถียรภาพ แม้ว่าจะไม่ได้ทำเครื่องหมายอย่างชัดเจนว่ามีเสถียรภาพโดยใช้คำอธิบายประกอบ @Stable ก็ตาม
- ประเภทค่าดั้งเดิมทั้งหมด:
Boolean,Int,Long,Float,Charฯลฯ - เครื่องสาย
- ฟังก์ชันทุกประเภท (Lambda)
ประเภททั้งหมดนี้สามารถปฏิบัติตามสัญญาของความเสถียรได้เนื่องจากเป็น แบบแก้ไขไม่ได้ เนื่องจากประเภทที่เปลี่ยนแปลงไม่ได้จะไม่เปลี่ยนแปลง จึงไม่จำเป็นต้องแจ้งให้ทราบถึงการเปลี่ยนแปลง ดังนั้นจึงปฏิบัติตามสัญญานี้ได้ง่ายกว่ามาก
ประเภทที่น่าสนใจประเภทหนึ่งซึ่งมีความเสถียรแต่เปลี่ยนแปลงได้คือ MutableState
ของ Compose หากค่าอยู่ใน MutableState ระบบจะถือว่าออบเจ็กต์สถานะโดยรวมมีความเสถียร เนื่องจาก Compose จะได้รับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงใดๆ ในพร็อพเพอร์ตี้ .value ของ State
เมื่อประเภททั้งหมดที่ส่งเป็นพารามิเตอร์ไปยัง Composable มีความเสถียร ระบบจะเปรียบเทียบค่าพารามิเตอร์ เพื่อหาความเท่ากันตามตำแหน่งของ Composable ในทรี UI ระบบจะข้ามการประกอบใหม่หากค่าทั้งหมดไม่เปลี่ยนแปลงตั้งแต่การเรียกครั้งก่อน
Compose จะถือว่าประเภทหนึ่งๆ เสถียรก็ต่อเมื่อพิสูจน์ได้ เช่น โดยทั่วไปแล้ว อินเทอร์เฟซจะถือว่าไม่เสถียร และประเภทที่มีพร็อพเพอร์ตี้สาธารณะที่เปลี่ยนแปลงได้ ซึ่งการใช้งานอาจเปลี่ยนแปลงไม่ได้ก็ไม่เสถียรเช่นกัน
หาก Compose ไม่สามารถสรุปได้ว่าประเภทใดเสถียร แต่คุณต้องการบังคับให้ Compose ถือว่าประเภทนั้นเสถียร ให้ทำเครื่องหมายด้วยคำอธิบายประกอบ @Stable
// Marking the type as stable to favor skipping and smart recompositions. @Stable interface UiState<T : Result<T>> { val value: T? val exception: Throwable? val hasError: Boolean get() = exception != null }
ในข้อมูลโค้ดด้านบน เนื่องจาก UiState เป็นอินเทอร์เฟซ Compose จึงอาจ
พิจารณาว่าประเภทนี้ไม่เสถียร การเพิ่ม@Stable
คำอธิบายประกอบจะบอก Compose ว่าประเภทนี้เสถียร ซึ่งจะช่วยให้ Compose
ใช้การเปลี่ยนคอมโพสใหม่แบบอัจฉริยะ ซึ่งหมายความว่า Compose จะถือว่าการใช้งานทั้งหมดมีเสถียรภาพหากใช้อินเทอร์เฟซเป็นประเภทพารามิเตอร์
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- สถานะและ Jetpack Compose
- ผลข้างเคียงในฟีเจอร์เขียน
- บันทึกสถานะ UI ใน Compose