ในหน้านี้ คุณจะได้เรียนรู้เกี่ยวกับวงจรของ Composable และวิธีที่ Compose ตัดสินใจว่า Composable ใดจำเป็นต้องมีการจัดองค์ประกอบใหม่
ภาพรวมของวงจร
ดังที่กล่าวไว้ในเอกสารประกอบเกี่ยวกับการจัดการสถานะ การจัดองค์ประกอบจะอธิบาย UI ของแอปและสร้างขึ้นโดยการเรียกใช้ Composable การจัดองค์ประกอบเป็นโครงสร้างแบบต้นไม้ของ Composable ที่อธิบาย UI
เมื่อ Jetpack Compose เรียกใช้ Composable เป็นครั้งแรกในระหว่าง การจัดองค์ประกอบ เริ่มต้น ระบบจะติดตาม Composable ที่คุณเรียกใช้เพื่ออธิบาย UI ในการจัดองค์ประกอบ จากนั้นเมื่อสถานะของแอปเปลี่ยนแปลง Jetpack Compose จะกำหนดเวลา การจัดองค์ประกอบใหม่ การจัดองค์ประกอบใหม่คือเมื่อ Jetpack Compose เรียกใช้ Composable อีกครั้งซึ่งอาจมีการเปลี่ยนแปลงเพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ แล้วอัปเดตการจัดองค์ประกอบเพื่อแสดงการเปลี่ยนแปลง
การจัดองค์ประกอบจะสร้างขึ้นได้จากการจัดองค์ประกอบเริ่มต้นเท่านั้น และอัปเดตได้จากการจัดองค์ประกอบใหม่ วิธีเดียวที่จะแก้ไขการจัดองค์ประกอบได้คือผ่านการจัดองค์ประกอบใหม่
โดยปกติแล้วการจัดองค์ประกอบใหม่จะเกิดขึ้นเมื่อมีการเปลี่ยนแปลงออบเจ็กต์
State<T> Compose
จะติดตามการเปลี่ยนแปลงเหล่านี้และเรียกใช้ Composable ทั้งหมดในการจัดองค์ประกอบที่อ่าน
State<T> นั้นๆ รวมถึง Composable ใดๆ ที่ Composable เหล่านั้นเรียกใช้ซึ่งข้ามไม่ได้
หากมีการเรียกใช้ Composable หลายครั้ง ระบบจะวางอินสแตนซ์หลายรายการไว้ในการจัดองค์ประกอบ การเรียกใช้แต่ละครั้งจะมีวงจรของตัวเองในการจัดองค์ประกอบ
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
MyComposable ในการจัดองค์ประกอบ หากมีการเรียกใช้ Composable หลายครั้ง ระบบจะวางอินสแตนซ์หลายรายการไว้ในการจัดองค์ประกอบ องค์ประกอบที่มีสีต่างกันบ่งบอกว่าเป็นอินสแตนซ์ที่แยกกันโครงสร้างของ Composable ในการจัดองค์ประกอบ
ระบบจะระบุอินสแตนซ์ของ Composable ในการจัดองค์ประกอบตามตำแหน่งที่เรียกใช้ คอมไพเลอร์ Compose จะถือว่าตำแหน่งที่เรียกใช้แต่ละตำแหน่งเป็นตำแหน่งที่แตกต่างกัน การเรียกใช้ Composable จากตำแหน่งที่เรียกใช้หลายตำแหน่งจะสร้างอินสแตนซ์ของ Composable หลายรายการในการจัดองค์ประกอบ
หากระหว่างการจัดองค์ประกอบใหม่ 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 จะเรียกใช้ Composable LoginError แบบมีเงื่อนไขและจะเรียกใช้ Composable LoginInput เสมอ การเรียกใช้แต่ละครั้งจะมีตำแหน่งที่เรียกใช้และตำแหน่งซอร์สที่ไม่ซ้ำกัน ซึ่งคอมไพเลอร์จะใช้เพื่อระบุการเรียกใช้แต่ละครั้งอย่างไม่ซ้ำกัน
LoginScreen ในการจัดองค์ประกอบเมื่อสถานะเปลี่ยนแปลงและมีการจัดองค์ประกอบใหม่ สีเดียวกันหมายความว่าไม่มีการจัดองค์ประกอบใหม่แม้ว่า LoginInput จะเปลี่ยนจากการเรียกใช้ครั้งแรกเป็นการเรียกใช้ครั้งที่ 2 แต่ระบบจะเก็บอินสแตนซ์ LoginInput ไว้ตลอดการจัดองค์ประกอบใหม่ นอกจากนี้ เนื่องจาก LoginInput ไม่มีพารามิเตอร์ที่เปลี่ยนแปลงตลอดการจัดองค์ประกอบใหม่ Compose จะข้ามการเรียกใช้ LoginInput
เพิ่มข้อมูลพิเศษเพื่อช่วยในการจัดองค์ประกอบใหม่แบบอัจฉริยะ
การเรียกใช้ Composable หลายครั้งจะเพิ่ม Composable นั้นๆ ลงในการจัดองค์ประกอบหลายครั้งด้วย เมื่อเรียกใช้ 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 จะนำอินสแตนซ์ที่มีอยู่แล้วในการจัดองค์ประกอบกลับมาใช้ซ้ำได้เนื่องจากตำแหน่งในรายการไม่เปลี่ยนแปลง และด้วยเหตุนี้อินพุต movie จึงเหมือนกันสำหรับอินสแตนซ์เหล่านั้น
MoviesScreen ในการจัดองค์ประกอบเมื่อมีการเพิ่มองค์ประกอบใหม่ที่ด้านล่างของรายการ Composable MovieOverview ในการจัดองค์ประกอบสามารถนำกลับมาใช้ซ้ำได้ สีเดียวกันใน MovieOverview หมายความว่าไม่มีการจัดองค์ประกอบใหม่อย่างไรก็ตาม หากรายการ 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 ในการจัดองค์ประกอบเมื่อมีการเพิ่มองค์ประกอบใหม่ลงในรายการ Composable MovieOverview ไม่สามารถนำกลับมาใช้ซ้ำได้และผลข้างเคียงทั้งหมดจะรีสตาร์ท สีที่ต่างกันใน MovieOverview หมายความว่ามีการจัดองค์ประกอบใหม่ในอุดมคติแล้ว เราต้องการให้คิดว่าข้อมูลประจำตัวของอินสแตนซ์ MovieOverview เชื่อมโยงกับข้อมูลประจำตัวของ movie ที่ส่งผ่านไปยังอินสแตนซ์นั้น หากเราจัดลำดับรายการภาพยนตร์ใหม่ ในอุดมคติแล้วเราจะจัดลำดับอินสแตนซ์ใหม่ในแผนผังการจัดองค์ประกอบในลักษณะเดียวกันแทนที่จะจัดองค์ประกอบใหม่ให้กับ Composable MovieOverview แต่ละรายการด้วยอินสแตนซ์ภาพยนตร์ที่แตกต่างกัน Compose มีวิธีให้คุณบอกรันไทม์
ว่าต้องการใช้ค่าใดเพื่อระบุส่วนหนึ่งของแผนผัง นั่นคือ
key
Composable
การใส่บล็อกโค้ดไว้ในการเรียกใช้ Composable key พร้อมค่าอย่างน้อย 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 ในการจัดองค์ประกอบเมื่อมีการเพิ่มองค์ประกอบใหม่ลงในรายการ เนื่องจาก Composable MovieOverview มีคีย์ที่ไม่ซ้ำกัน 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) } } }
การข้ามหากอินพุตไม่เปลี่ยนแปลง
ระหว่างการจัดองค์ประกอบใหม่ ฟังก์ชัน Composable บางฟังก์ชันที่มีสิทธิ์อาจถูกข้ามการดำเนินการทั้งหมดหากอินพุตไม่เปลี่ยนแปลงจากการจัดองค์ประกอบก่อนหน้า
ฟังก์ชันที่ประกอบกันได้ มีสิทธิ์ถูกข้าม เว้นแต่ จะมีลักษณะดังนี้
- ฟังก์ชันมีประเภทการแสดงผลที่ไม่ใช่
Unit - ฟังก์ชันมีคำอธิบายประกอบ
@NonRestartableComposableหรือ@NonSkippableComposable - พารามิเตอร์ที่จำเป็นมีประเภทที่ไม่เสถียร
มีโหมดคอมไพเลอร์ที่เรียกว่า Strong Skipping ซึ่งผ่อนปรนข้อกำหนดสุดท้าย
ประเภทที่จะถือว่าเสถียรต้องเป็นไปตามข้อกำหนดต่อไปนี้
- ผลลัพธ์ของ
equalsสำหรับอินสแตนซ์ 2 รายการจะเหมือนกัน ตลอดไป สำหรับอินสแตนซ์ 2 รายการเดียวกัน - หากพร็อพเพอร์ตี้สาธารณะของประเภทเปลี่ยนแปลง ระบบจะแจ้งให้การจัดองค์ประกอบทราบ
- ประเภทพร็อพเพอร์ตี้สาธารณะทั้งหมดก็เสถียรด้วย
มีประเภททั่วไปที่สำคัญบางประเภทที่เป็นไปตามข้อกำหนดนี้ ซึ่งคอมไพเลอร์ Compose จะถือว่าเป็นประเภทเสถียร แม้ว่าจะไม่ได้ทำเครื่องหมายว่าเป็นประเภทเสถียรอย่างชัดเจนโดยใช้คำอธิบายประกอบ @Stable ก็ตาม
- ประเภทค่าดั้งเดิมทั้งหมด เช่น
Boolean,Int,Long,Float,Charเป็นต้น - สตริง
- ฟังก์ชันทุกประเภท (แลมบ์ดา)
ประเภทเหล่านี้ทั้งหมดเป็นไปตามข้อกำหนดของประเภทเสถียรได้เนื่องจากเป็นประเภทที่ไม่เปลี่ยนแปลง เนื่องจากประเภทที่ไม่เปลี่ยนแปลงจะไม่เปลี่ยนแปลง ดังนั้นจึงไม่จำเป็นต้องแจ้งให้การจัดองค์ประกอบทราบถึงการเปลี่ยนแปลง ทำให้เป็นไปตามข้อกำหนดนี้ได้ง่ายขึ้นมาก
ประเภทที่น่าสังเกตประเภทหนึ่งซึ่งเสถียรแต่ เปลี่ยนแปลงได้ คือประเภท 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
- ผลข้างเคียงใน Compose
- บันทึกสถานะ UI ใน Compose