ในหน้านี้ คุณจะได้เรียนรู้เกี่ยวกับวงจรของ Composable และ วิธีที่ Compose ใช้ในการตัดสินว่าจะต้องจัดองค์ประกอบใหม่สำหรับ Composable หรือไม่
ภาพรวมวงจรของลูกค้า
ตามที่ระบุไว้ในเอกสารประกอบของการจัดการสถานะ การเรียบเรียงอธิบายถึง UI ของแอปและสร้างขึ้นโดยการเรียกใช้ Composable การเรียบเรียงเป็นโครงสร้างแบบต้นไม้ของ Composable ที่อธิบาย UI ของคุณ
เมื่อ Jetpack Compose เรียกใช้ Composable เป็นครั้งแรกระหว่างครั้งแรก ของคอมโพเนนต์ ระบบจะติดตาม Composable ที่คุณเรียกใช้เพื่ออธิบาย UI ในการเรียบเรียง จากนั้นเมื่อสถานะของแอปเปลี่ยนไป Jetpack การเขียนจะกำหนดเวลาการจัดองค์ประกอบใหม่ การคอมโพสิชันใหม่คือเมื่อ Jetpack Compose เรียกใช้คอมโพสิชันที่อาจเปลี่ยนแปลงเพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ แล้วอัปเดตคอมโพสิชันให้แสดงการเปลี่ยนแปลง
การเรียบเรียงเพลงจะสร้างโดยการเรียบเรียงเพลงเริ่มต้นและอัปเดตโดย การจัดองค์ประกอบใหม่ วิธีเดียวในการแก้ไขการเรียบเรียงคือผ่านการจัดองค์ประกอบใหม่
รูปที่ 1 วงจรของ Composable ในการเรียบเรียง รายการดังกล่าวจะเข้าสู่การคอมโพสิชัน ได้รับการคอมโพสิชันใหม่อย่างน้อย 0 ครั้ง และออกจากการคอมโพสิชัน
โดยทั่วไปการจัดองค์ประกอบใหม่จะทริกเกอร์โดยการเปลี่ยนแปลง
State<T>
เขียน
ติดตามสิ่งเหล่านี้และเรียกใช้ Composable ทั้งหมดในการเรียบเรียงที่อ่านค่า
State<T>
โดยเฉพาะ และ Composable ที่เรียกใช้ซึ่งไม่สามารถ
ข้าม
หากมีการเรียกใช้ Composable หลายครั้ง จะมีหลายอินสแตนซ์อยู่ในพารามิเตอร์ การเรียบเรียง การเรียกแต่ละรายการมีวงจรการใช้งานของตัวเองในการเรียบเรียง
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
รูปที่ 2 การแทนค่า MyComposable
ในการเรียบเรียง หากเรียกใช้คอมโพสิเบิลหลายครั้ง ระบบจะวางอินสแตนซ์หลายรายการไว้ในคอมโพสิชัน องค์ประกอบที่มีสีต่างกันจะบ่งชี้ว่าเป็น
แยกอินสแตนซ์
กายวิภาคของ Composable ในการเรียบเรียง
อินสแตนซ์ของ Composable ในการเรียบเรียงจะระบุโดยไซต์การเรียกใช้ คอมไพเลอร์ Compose จะถือว่าแต่ละตำแหน่งการเรียกใช้แตกต่างกัน กำลังเรียก Composable จากเว็บไซต์การเรียกใช้หลายแห่งจะสร้างอินสแตนซ์ Composable หลายอินสแตนซ์ใน การเรียบเรียง
หากในระหว่างการคอมโพสิชันใหม่ คอมโพสิชันหนึ่งเรียกคอมโพสิชันอื่นที่แตกต่างจากการคอมโพสิชันก่อนหน้า Compose จะระบุคอมโพสิชันที่มีการเรียกใช้หรือไม่เรียกใช้ และสำหรับคอมโพสิชันที่มีการเรียกใช้ในการคอมโพสิชันทั้ง 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 เสมอ ชิ้น
มีไซต์การเรียกใช้และตำแหน่งแหล่งที่มาที่ไม่ซ้ำกัน ซึ่งคอมไพเลอร์จะใช้เพื่อ
เป็นเอกลักษณ์ของตัวเองได้
รูปที่ 3 การนําเสนอ LoginScreen
ในองค์ประกอบเมื่อสถานะเปลี่ยนแปลงและเกิดการจัดองค์ประกอบใหม่ สีเดียวกันหมายความว่ายังไม่ได้ปรับองค์ประกอบใหม่
แม้ว่า LoginInput
จะเปลี่ยนจากเรียกครั้งแรกเป็นเรียกครั้งที่ 2 แต่ระบบจะเก็บอินสแตนซ์ LoginInput
ไว้ในการคอมโพสิชันใหม่ นอกจากนี้
เนื่องจาก LoginInput
ไม่มีพารามิเตอร์ที่มีการเปลี่ยนแปลงใน
การจัดองค์ประกอบใหม่ การเขียนจะข้ามการเรียกไปยัง LoginInput
เพิ่มข้อมูลเพิ่มเติมเพื่อช่วยในการปรับเปลี่ยนองค์ประกอบใหม่อย่างชาญฉลาด
การเรียก 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
ใหม่ลงที่ด้านล่างของรายการ คอมโพสิชันจะใช้อินสแตนซ์ที่มีอยู่แล้วในคอมโพสิชันซ้ำได้ เนื่องจากตำแหน่งของอินสแตนซ์ในรายการไม่เปลี่ยนแปลง อินพุต movie
จึงเหมือนกันสำหรับอินสแตนซ์เหล่านั้น
รูปที่ 4 การนําเสนอ 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) /* ... */ } }
รูปที่ 5 การนําเสนอ MoviesScreen
ในองค์ประกอบเมื่อเพิ่มองค์ประกอบใหม่ลงในรายการ MovieOverview
คอมโพสิเบิลจะใช้ซ้ำไม่ได้ และผลข้างเคียงทั้งหมดจะเริ่มต้นใหม่ สีอื่นใน MovieOverview
หมายความว่าคอมโพสิเบิลได้รับการคอมโพสใหม่
โดยหลักการแล้ว เราต้องการคิดว่าตัวตนของอินสแตนซ์ MovieOverview
นั้นเชื่อมโยงกับตัวตนของ movie
ที่ส่งผ่านมา หากเราจัดเรียงรายการภาพยนตร์ใหม่ เราควรจัดเรียงอินสแตนซ์ในต้นไม้องค์ประกอบใหม่ด้วยเช่นกันแทนที่จะจัดเรียงMovieOverview
แต่ละรายการใหม่โดยประกอบกับอินสแตนซ์ภาพยนตร์อื่น การเขียนช่วยให้คุณบอกรันไทม์ได้
ค่าที่คุณต้องการใช้เพื่อระบุส่วนที่กำหนดของต้นไม้ ซึ่งได้แก่
key
Composable
การรวมบล็อกโค้ดด้วยการเรียกใช้คีย์ที่คอมโพสิเบิลกับค่าอย่างน้อย 1 ค่าที่ส่งเข้ามาจะรวมค่าเหล่านั้นเพื่อใช้ระบุอินสแตนซ์นั้นในคอมโพสิชัน ค่าของ key
ไม่จำเป็นต้องไม่ซ้ำกันทั่วโลก แต่ต้องไม่ซ้ำกันเฉพาะในบรรดาการเรียกใช้คอมโพสิเบิลที่ตำแหน่งการเรียกเท่านั้น ดังนั้นในตัวอย่างนี้ movie
แต่ละรายการต้องมี key
ที่ไม่ซ้ำกันสำหรับ movies
แต่ละรายการ แต่แชร์ key
กับคอมโพสิเบิลอื่นๆ ในส่วนอื่นๆ ของแอปได้
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
ด้วยเงื่อนไขข้างต้น แม้ว่าองค์ประกอบในรายการจะมีการเปลี่ยนแปลง แต่ Compose จะจดจำได้
การโทรแต่ละครั้งไปยัง MovieOverview
และสามารถใช้ซ้ำได้
รูปที่ 6 การแสดง MoviesScreen
ในการเรียบเรียงเมื่อ
ลงในรายการ เนื่องจากคอมโพสิเบิล MovieOverview
มีคีย์ที่ไม่ซ้ำกัน Compose จึงจะจดจำอินสแตนซ์ MovieOverview
ที่ไม่มีการเปลี่ยนแปลงและนํากลับมาใช้ซ้ำได้ เอฟเฟกต์ข้างเคียงของคอมโพสิเบิลจะยังคงทํางานต่อไป
คอมโพสิเบิลบางรายการรองรับคอมโพสิเบิล key
ในตัว ตัวอย่างเช่น
LazyColumn
ยอมรับการระบุ key
ที่กำหนดเองใน items
DSL
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
การข้ามหากข้อมูลป้อนไม่มีการเปลี่ยนแปลง
ในระหว่างการจัดโครงสร้างใหม่ ฟังก์ชัน Composable ที่มีสิทธิ์บางรายการสามารถมีฟังก์ชัน ข้ามการดำเนินการทั้งหมดหากอินพุตไม่ได้เปลี่ยนแปลงจาก องค์ประกอบ
ฟังก์ชันที่ประกอบกันได้มีสิทธิ์ข้ามเว้นแต่ว่า
- ฟังก์ชันมีประเภทการแสดงผลที่ไม่ใช่
Unit
- ฟังก์ชันนี้จะมีคำอธิบายประกอบด้วย
@NonRestartableComposable
หรือ@NonSkippableComposable
- พารามิเตอร์ที่ต้องระบุเป็นประเภทที่ไม่เสถียร
มีโหมดคอมไพเลอร์เวอร์ชันทดลองที่เรียกว่าการข้ามแบบเข้มงวด ซึ่งผ่อนปรนข้อกำหนดข้อสุดท้าย
ประเภทจะถือว่าเสถียรก็ต่อเมื่อเป็นไปตามข้อตกลงต่อไปนี้
- ผลลัพธ์ของ
equals
สำหรับ 2 อินสแตนซ์จะตลอดไปเหมือนเดิมสำหรับ อินสแตนซ์สองตัวที่เหมือนกัน - หากพร็อพเพอร์ตี้สาธารณะของประเภทมีการเปลี่ยนแปลง ระบบจะแจ้งเตือนการเรียบเรียง
- นอกจากนี้ พร็อพเพอร์ตี้สาธารณะทุกประเภทก็มีความเสถียรด้วย
มีประเภททั่วไปที่สำคัญจำนวนหนึ่งในสัญญานี้ที่
คอมไพเลอร์ของการเขียนจะถือว่ามีความเสถียร แม้ว่าจะไม่มีการกำหนดอย่างชัดแจ้ง
ทำเครื่องหมายว่าเสถียรแล้วโดยใช้คำอธิบายประกอบ @Stable
:
- ค่าประเภทพื้นฐานทั้งหมด ได้แก่
Boolean
,Int
,Long
,Float
,Char
ฯลฯ - เครื่องสาย
- ฟังก์ชันทุกประเภท (lambda)
ประเภทเหล่านี้ทั้งหมดเป็นไปตามสัญญาของ Stable เนื่องจากเป็นประเภทที่เปลี่ยนแปลงไม่ได้ เนื่องจากประเภทที่เปลี่ยนแปลงไม่ได้ไม่เคยมีการเปลี่ยนแปลง จึงไม่มีความจำเป็นที่จะต้องแจ้งเตือนเลย องค์ประกอบการเปลี่ยนแปลง ทำให้ทำตามสัญญานี้ได้ง่ายขึ้น
ประเภทหนึ่งที่น่าสนใจซึ่งมีความเสถียรแต่เปลี่ยนแปลงได้คือ MutableState
type ของ 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 ปิดอยู่
- State และ Jetpack Compose
- ผลข้างเคียงใน Compose
- บันทึกสถานะ UI ใน Compose