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