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