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