Jetpack Compose เป็นชุดเครื่องมือ UI สมัยใหม่ที่อธิบายอย่างชัดเจนสำหรับ Android Compose ช่วยให้เขียนและดูแลรักษา UI ของแอปได้ง่ายขึ้นด้วยการจัดหา Declarative API ที่ช่วยให้คุณแสดงผล UI ของแอปได้โดยไม่ต้องเปลี่ยนแปลงมุมมองส่วนหน้าอย่างจำเป็น คำศัพท์นี้ต้องมีการอธิบาย แต่ผลกระทบที่เกิดขึ้นมีความสำคัญต่อ การออกแบบแอปของคุณ
กระบวนทัศน์การเขียนโปรแกรมเชิงประกาศ
ในอดีต ลำดับชั้นของมุมมอง Android สามารถแสดงเป็นโครงสร้างแบบต้นไม้ของวิดเจ็ต UI ได้
เมื่อสถานะของแอปเปลี่ยนแปลงเนื่องจากสิ่งต่างๆ เช่น การโต้ตอบของผู้ใช้
ระบบจะต้องอัปเดตลำดับชั้น UI เพื่อแสดงข้อมูลปัจจุบัน
วิธีที่พบบ่อยที่สุดในการอัปเดต UI คือการเดินผ่านโครงสร้างโดยใช้ฟังก์ชันต่างๆ เช่น
findViewById()
และเปลี่ยน
โหนดโดยการเรียกใช้เมธอดต่างๆ เช่น button.setText(String)
,
container.addChild(View)
หรือ img.setImageBitmap(Bitmap)
เมธอดเหล่านี้
เปลี่ยนสถานะภายในของวิดเจ็ต
การปรับแต่งมุมมองด้วยตนเองจะเพิ่มโอกาสเกิดข้อผิดพลาด หากมีการแสดงข้อมูลในหลายที่ คุณอาจลืมอัปเดตมุมมองใดมุมมองหนึ่งที่แสดงข้อมูลนั้นได้ง่าย นอกจากนี้ยังสร้างสถานะที่ผิดกฎหมายได้ง่ายเมื่อการอัปเดต 2 รายการ ขัดแย้งกันในลักษณะที่ไม่คาดคิด เช่น การอัปเดตอาจพยายามตั้งค่า ของโหนดที่เพิ่งนำออกจาก UI โดยทั่วไป ความซับซ้อนในการบำรุงรักษาซอฟต์แวร์จะเพิ่มขึ้นตามจำนวนมุมมองที่ต้องอัปเดต
ในช่วงหลายปีที่ผ่านมา อุตสาหกรรมทั้งหมดเริ่มเปลี่ยนไปใช้ รูปแบบ UI เชิงประกาศ ซึ่งช่วยลดความซับซ้อนด้านวิศวกรรมที่เกี่ยวข้องกับ การสร้างและอัปเดตอินเทอร์เฟซผู้ใช้อย่างมาก เทคนิคนี้ทำงานโดยการสร้างหน้าจอทั้งหมดขึ้นมาใหม่ตั้งแต่ต้นในเชิงแนวคิด แล้วใช้เฉพาะการเปลี่ยนแปลงที่จำเป็นเท่านั้น วิธีนี้ช่วยลดความซับซ้อนของการอัปเดตลำดับชั้นของมุมมองที่มีสถานะด้วยตนเอง Compose เป็นเฟรมเวิร์ก UI แบบประกาศสิ่งที่ต้องการ
ความท้าทายอย่างหนึ่งในการสร้างหน้าจอทั้งหมดใหม่คืออาจต้องใช้เวลา กำลังประมวลผล และการใช้แบตเตอรี่มาก Compose จะเลือกส่วนต่างๆ ของ UI ที่ต้องวาดใหม่ในเวลาใดก็ตามอย่างชาญฉลาดเพื่อลดต้นทุนนี้ ซึ่งส่งผลต่อวิธีออกแบบ คอมโพเนนต์ UI ตามที่อธิบายไว้ในการประกอบใหม่
ฟังก์ชันที่ประกอบกันได้แบบง่าย
เมื่อใช้ Compose คุณจะสร้างอินเทอร์เฟซผู้ใช้ได้โดยการกำหนดชุดฟังก์ชัน Composable ที่รับข้อมูลและส่งองค์ประกอบ UI ตัวอย่างง่ายๆ
คือวิดเจ็ต Greeting
ซึ่งรับ String
และส่งวิดเจ็ต Text
ที่แสดงข้อความทักทาย
รูปที่ 1 ฟังก์ชันที่ประกอบได้แบบง่ายๆ ซึ่งส่งข้อมูลและใช้ข้อมูลนั้นเพื่อ แสดงผลวิดเจ็ตข้อความบนหน้าจอ
ข้อควรทราบเกี่ยวกับฟังก์ชันนี้มีดังนี้
ฟังก์ชันนี้มีคำอธิบายประกอบด้วยคำอธิบายประกอบ
@Composable
ฟังก์ชัน Composable ทั้งหมดต้องมีคำอธิบายประกอบนี้ คำอธิบายประกอบนี้จะแจ้งให้คอมไพเลอร์ Compose ทราบว่าฟังก์ชันนี้มีไว้เพื่อแปลงข้อมูลเป็น UIฟังก์ชันจะรับข้อมูล ฟังก์ชันที่ใช้ร่วมกันได้จะยอมรับพารามิเตอร์ ซึ่งช่วยให้ตรรกะของแอปอธิบาย UI ได้ ในกรณีนี้ วิดเจ็ตของเรา ยอมรับ
String
เพื่อทักทายผู้ใช้ตามชื่อฟังก์ชันจะแสดงข้อความใน UI โดยทำได้ด้วยการเรียกใช้ฟังก์ชัน
Text()
ที่ใช้ร่วมกันได้ ซึ่งจะสร้างองค์ประกอบ UI ของข้อความจริงๆ ฟังก์ชันที่ประกอบกันได้ จะส่งลำดับชั้น UI โดยการเรียกฟังก์ชันที่ประกอบกันได้อื่นๆฟังก์ชันจะไม่แสดงผลสิ่งใด ฟังก์ชัน Compose ที่ปล่อย UI ไม่จำเป็นต้องแสดงผลอะไร เนื่องจากฟังก์ชันจะอธิบายสถานะหน้าจอที่ต้องการแทนที่จะสร้างวิดเจ็ต UI
ฟังก์ชันนี้รวดเร็ว ทำซ้ำได้ และไม่มีผลข้างเคียง
- ฟังก์ชันจะทํางานในลักษณะเดียวกันเมื่อเรียกใช้หลายครั้งด้วยอาร์กิวเมนต์เดียวกัน และจะไม่ใช้ค่าอื่นๆ เช่น ตัวแปรส่วนกลางหรือการเรียกใช้
random()
- ฟังก์ชันจะอธิบาย UI โดยไม่มีผลข้างเคียง เช่น การแก้ไขพร็อพเพอร์ตี้หรือตัวแปรส่วนกลาง
โดยทั่วไป ฟังก์ชันที่ประกอบได้ทั้งหมดควรเขียนด้วยพร็อพเพอร์ตี้เหล่านี้ด้วยเหตุผลที่กล่าวถึงในการประกอบใหม่
- ฟังก์ชันจะทํางานในลักษณะเดียวกันเมื่อเรียกใช้หลายครั้งด้วยอาร์กิวเมนต์เดียวกัน และจะไม่ใช้ค่าอื่นๆ เช่น ตัวแปรส่วนกลางหรือการเรียกใช้
การเปลี่ยนกระบวนทัศน์แบบประกาศ
เมื่อใช้ชุดเครื่องมือ UI แบบออบเจ็กต์ที่จำเป็นหลายชุด คุณจะเริ่มต้น UI ได้โดย การสร้างอินสแตนซ์ของแผนผังวิดเจ็ต โดยมักจะทำได้ด้วยการขยายไฟล์เลย์เอาต์ XML วิดเจ็ตแต่ละรายการจะรักษาสถานะภายในของตัวเอง และแสดงเมธอด Getter และ Setter ที่ช่วยให้ตรรกะของแอปโต้ตอบกับวิดเจ็ตได้
ในแนวทางแบบประกาศของ Compose วิดเจ็ตจะไม่มีสถานะและไม่แสดงฟังก์ชัน Setter หรือ Getter ในความเป็นจริงแล้ว ระบบไม่ได้แสดงวิดเจ็ตเป็นออบเจ็กต์
คุณอัปเดต UI ได้โดยการเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้เดียวกันด้วยอาร์กิวเมนต์ที่แตกต่างกัน
ซึ่งช่วยให้ระบุสถานะในรูปแบบสถาปัตยกรรม เช่น ViewModel
ได้ง่ายตามที่อธิบายไว้ในคู่มือสถาปัตยกรรมแอป จากนั้น Composable จะ
มีหน้าที่แปลงสถานะแอปพลิเคชันปัจจุบันเป็น UI ทุกครั้งที่
ข้อมูลที่สังเกตได้มีการอัปเดต
รูปที่ 2 ตรรกะของแอปจะให้ข้อมูลแก่ฟังก์ชันที่ใช้ร่วมกันได้ระดับบนสุด ฟังก์ชันดังกล่าวใช้ข้อมูลเพื่ออธิบาย UI โดยการเรียก Composable อื่นๆ และ ส่งข้อมูลที่เหมาะสมไปยัง Composable เหล่านั้นและลงไปตามลำดับชั้น
เมื่อผู้ใช้โต้ตอบกับ UI ทาง UI จะสร้างเหตุการณ์ต่างๆ เช่น onClick
เหตุการณ์เหล่านั้นควรแจ้งตรรกะของแอป ซึ่งจะเปลี่ยนสถานะของแอปได้
เมื่อสถานะเปลี่ยนแปลง ระบบจะเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้อีกครั้งพร้อมข้อมูลใหม่
ซึ่งจะทำให้องค์ประกอบ UI ถูกวาดใหม่ กระบวนการนี้เรียกว่าการประกอบใหม่
รูปที่ 3 ผู้ใช้โต้ตอบกับองค์ประกอบ UI ซึ่งทําให้เกิดการทริกเกอร์เหตุการณ์ ตรรกะของแอปจะตอบสนองต่อเหตุการณ์ จากนั้นระบบจะเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้อีกครั้งโดยอัตโนมัติพร้อมพารามิเตอร์ใหม่ หากจำเป็น
เนื้อหาแบบไดนามิก
เนื่องจากฟังก์ชันที่ใช้ได้เขียนด้วย Kotlin แทน XML จึงมีความ ไดนามิกได้เช่นเดียวกับโค้ด Kotlin อื่นๆ ตัวอย่างเช่น สมมติว่าคุณต้องการสร้าง UI ที่ทักทายรายชื่อผู้ใช้
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
ฟังก์ชันนี้จะรับรายการชื่อและสร้างคำทักทายสำหรับผู้ใช้แต่ละราย
ฟังก์ชันที่ประกอบกันได้อาจมีความซับซ้อนมาก คุณใช้คำสั่ง if
เพื่อ
ตัดสินใจว่าต้องการแสดงองค์ประกอบ UI ที่เฉพาะเจาะจงหรือไม่ คุณใช้ลูปได้ คุณสามารถ
เรียกใช้ฟังก์ชันตัวช่วยได้ คุณจะมีความยืดหยุ่นอย่างเต็มที่ในการใช้ภาษาพื้นฐาน
ความสามารถและความยืดหยุ่นนี้เป็นข้อดีที่สำคัญอย่างหนึ่งของ Jetpack Compose
การจัดองค์ประกอบใหม่
ในโมเดล UI แบบคำสั่ง หากต้องการเปลี่ยนวิดเจ็ต คุณจะต้องเรียกใช้ Setter ในวิดเจ็ต เพื่อเปลี่ยนสถานะภายใน ใน Compose คุณจะเรียกใช้ฟังก์ชันที่ใช้ Compose ได้อีกครั้ง ด้วยข้อมูลใหม่ การทำเช่นนี้จะทำให้ฟังก์ชันประกอบใหม่ ซึ่งหมายความว่าระบบจะวาดวิดเจ็ต ที่ฟังก์ชันปล่อยออกมาใหม่พร้อมข้อมูลใหม่ หากจำเป็น เฟรมเวิร์ก Compose สามารถจัดองค์ประกอบใหม่ได้อย่างชาญฉลาดเฉพาะคอมโพเนนต์ที่มีการเปลี่ยนแปลงเท่านั้น
ตัวอย่างเช่น ลองดูฟังก์ชันที่ใช้ร่วมกันได้นี้ซึ่งแสดงปุ่ม
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
ทุกครั้งที่มีการคลิกปุ่ม ผู้โทรจะอัปเดตค่าของ clicks
Compose จะเรียกใช้ Lambda ด้วยฟังก์ชัน Text
อีกครั้งเพื่อแสดงค่าใหม่
กระบวนการนี้เรียกว่าการประกอบใหม่ ฟังก์ชันอื่นๆ ที่ไม่ได้ขึ้นอยู่กับ
ค่าจะไม่ได้รับการจัดรูปแบบใหม่
ดังที่เราได้กล่าวไปแล้ว การสร้างโครงสร้างใหม่ของทั้งทรี UI อาจมีค่าใช้จ่ายในการประมวลผลสูง ซึ่งจะใช้กำลังประมวลผลและอายุการใช้งานแบตเตอรี่ Compose แก้ปัญหานี้ด้วยการจัดองค์ประกอบใหม่ที่ชาญฉลาด
การประกอบใหม่คือกระบวนการเรียกใช้ฟังก์ชันที่ประกอบได้อีกครั้งเมื่อ อินพุตมีการเปลี่ยนแปลง ซึ่งจะเกิดขึ้นเมื่ออินพุตของฟังก์ชันมีการเปลี่ยนแปลง เมื่อ Compose ทำการคอมโพสซ้ำตามอินพุตใหม่ ระบบจะเรียกใช้เฉพาะฟังก์ชันหรือแลมบ์ดาที่ อาจมีการเปลี่ยนแปลง และข้ามส่วนที่เหลือ การข้ามฟังก์ชันหรือ Lambda ทั้งหมด ที่ไม่มีการเปลี่ยนแปลงพารามิเตอร์จะช่วยให้ Compose สามารถทำการ Recompose ได้อย่างมีประสิทธิภาพ
อย่าพึ่งพาผลข้างเคียงจากการเรียกใช้ฟังก์ชันที่ประกอบได้ เนื่องจากอาจมีการข้ามการจัดองค์ประกอบใหม่ของฟังก์ชัน หากทำเช่นนั้น ผู้ใช้อาจพบพฤติกรรมที่แปลกประหลาด และคาดเดาไม่ได้ในแอปของคุณ ผลข้างเคียงคือการเปลี่ยนแปลงใดๆ ที่ มองเห็นได้ในส่วนอื่นๆ ของแอป ตัวอย่างเช่น การดำเนินการต่อไปนี้เป็นผลข้างเคียงที่อันตราย ทั้งหมด
- การเขียนไปยังพร็อพเพอร์ตี้ของออบเจ็กต์ที่แชร์
- การอัปเดต Observable ใน
ViewModel
- การอัปเดตค่ากำหนดที่แชร์
ฟังก์ชันที่ใช้ร่วมกันได้อาจมีการเรียกใช้ซ้ำบ่อยเท่ากับทุกเฟรม เช่น เมื่อมีการแสดงผลภาพเคลื่อนไหว ฟังก์ชันที่ใช้ร่วมกันได้ควรทำงานอย่างรวดเร็วเพื่อหลีกเลี่ยง อาการกระตุกระหว่างภาพเคลื่อนไหว หากคุณต้องการดำเนินการที่มีค่าใช้จ่ายสูง เช่น การอ่านจากค่ากำหนดที่ใช้ร่วมกัน ให้ดำเนินการในโครูทีนเบื้องหลังและส่งผลลัพธ์ค่าไปยังฟังก์ชันที่ใช้ร่วมกันได้เป็นพารามิเตอร์
ตัวอย่างเช่น โค้ดนี้จะสร้าง Composable เพื่ออัปเดตค่าใน SharedPreferences
Composable ไม่ควรอ่านหรือเขียนจาก Shared
Preferences เอง แต่โค้ดนี้จะย้ายการอ่านและการเขียนไปยัง ViewModel
ในโครูทีนเบื้องหลังแทน ตรรกะของแอปจะส่งค่าปัจจุบันพร้อม
การเรียกกลับเพื่อทริกเกอร์การอัปเดต
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
เอกสารนี้จะกล่าวถึงสิ่งต่างๆ ที่ควรทราบเมื่อใช้ Compose
- การประกอบใหม่จะข้ามฟังก์ชันที่ใช้ร่วมกันได้และ Lambda มากที่สุดเท่าที่จะเป็นไปได้
- การประกอบใหม่เป็นการดำเนินการแบบมองโลกในแง่ดีและอาจถูกยกเลิก
- ฟังก์ชันที่ใช้ร่วมกันได้อาจทำงานค่อนข้างบ่อย ซึ่งอาจบ่อยเท่ากับทุกเฟรม ของภาพเคลื่อนไหว
- ฟังก์ชันที่ประกอบกันได้จะดำเนินการแบบขนานได้
- ฟังก์ชันที่ใช้ร่วมกันได้จะทำงานตามลำดับใดก็ได้
ส่วนต่อไปนี้จะครอบคลุมวิธีสร้างฟังก์ชันที่ใช้ร่วมกันได้เพื่อรองรับ การรวมกันใหม่ ในทุกกรณี แนวทางปฏิบัติแนะนำคือการทำให้ฟังก์ชันที่ประกอบได้ ทำงานได้อย่างรวดเร็ว มีการดำเนินการซ้ำได้ และไม่มีผลข้างเคียง
ข้ามการจัดองค์ประกอบใหม่ให้ได้มากที่สุด
เมื่อส่วนต่างๆ ของ UI ไม่ถูกต้อง Compose จะพยายามอย่างเต็มที่เพื่อทำการประกอบใหม่เฉพาะส่วนที่ต้องอัปเดต ซึ่งหมายความว่าระบบอาจข้ามไปเรียกใช้ Composable ของปุ่มเดียวโดยไม่ต้องเรียกใช้ Composable ใดๆ ที่อยู่เหนือหรือใต้ Composable นั้น ในแผนผัง UI
ฟังก์ชันที่ใช้ร่วมกันได้และ Lambda ทุกรายการอาจทำการรวมกันใหม่ได้ด้วยตัวเอง ต่อไปนี้เป็น ตัวอย่างที่แสดงให้เห็นว่าการประกอบใหม่จะข้ามองค์ประกอบบางอย่างได้อย่างไรเมื่อ แสดงรายการ
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
ขอบเขตแต่ละรายการอาจเป็นสิ่งเดียวที่ต้องดำเนินการในระหว่างการจัดองค์ประกอบใหม่
Compose อาจข้ามไปยัง Lambda Column
โดยไม่เรียกใช้ Lambda ระดับบน
เมื่อ header
เปลี่ยนแปลง และเมื่อเรียกใช้ Column
, Compose อาจเลือกที่จะ
ข้ามรายการของ LazyColumn
หาก names
ไม่มีการเปลี่ยนแปลง
อีกครั้งที่การเรียกใช้ฟังก์ชันหรือ Lambda ที่ประกอบได้ทั้งหมดไม่ควรมีผลข้างเคียง เมื่อต้องการดำเนินการที่มีผลข้างเคียง ให้ทริกเกอร์จากโค้ดเรียกกลับ
การจัดองค์ประกอบใหม่เป็นแบบมองในแง่ดี
การเขียนใหม่จะเริ่มเมื่อใดก็ตามที่ Compose คิดว่าพารามิเตอร์ของ Composable อาจมีการเปลี่ยนแปลง การประกอบใหม่เป็นแบบมองในแง่ดี ซึ่งหมายความว่า Compose คาดหวัง ที่จะประกอบใหม่ให้เสร็จก่อนที่พารามิเตอร์จะเปลี่ยนแปลงอีกครั้ง หากพารามิเตอร์มีการเปลี่ยนแปลงก่อนที่การจัดองค์ประกอบใหม่จะเสร็จสมบูรณ์ Compose อาจยกเลิกการจัดองค์ประกอบใหม่และรีสตาร์ทด้วยพารามิเตอร์ใหม่
เมื่อยกเลิกการจัดองค์ประกอบใหม่ Compose จะทิ้งแผนผัง UI จากการจัดองค์ประกอบใหม่ หากคุณมีผลข้างเคียงที่ขึ้นอยู่กับการแสดง UI ผลข้างเคียงจะยังคงมีผลแม้ว่าจะยกเลิกการจัดองค์ประกอบแล้วก็ตาม ซึ่งอาจส่งผลให้สถานะของแอปไม่สอดคล้องกัน
ตรวจสอบว่าฟังก์ชันและแลมดาทั้งหมดที่ประกอบได้นั้นเป็นแบบ Idempotent และไม่มีผลข้างเคียง เพื่อจัดการการประกอบใหม่แบบมองโลกในแง่ดี
ฟังก์ชันที่ประกอบกันได้อาจทำงานบ่อยครั้ง
ในบางกรณี ฟังก์ชันที่ใช้ร่วมกันได้อาจทำงานสำหรับทุกเฟรมของภาพเคลื่อนไหว UI หากฟังก์ชันดำเนินการที่มีค่าใช้จ่ายสูง เช่น การอ่านจาก ที่เก็บข้อมูลของอุปกรณ์ ฟังก์ชันอาจทำให้ UI กระตุก
เช่น หากวิดเจ็ตพยายามอ่านการตั้งค่าอุปกรณ์ วิดเจ็ตอาจอ่านการตั้งค่าเหล่านั้นหลายร้อยครั้งต่อวินาที ซึ่งจะส่งผลเสียต่อประสิทธิภาพของแอป
หากฟังก์ชันที่ใช้ร่วมกันได้ต้องใช้ข้อมูล ฟังก์ชันนั้นควรจะกำหนดพารามิเตอร์สำหรับข้อมูล
จากนั้นคุณสามารถย้ายงานที่ใช้ทรัพยากรมากไปยังเธรดอื่นนอกmutableStateOf
CompositionLiveData
และส่งข้อมูลไปยัง Compose โดยใช้ mutableStateOf
หรือ LiveData
ฟังก์ชันที่ประกอบกันได้จะทำงานแบบคู่ขนานได้
Compose สามารถเพิ่มประสิทธิภาพการจัดองค์ประกอบใหม่ได้ด้วยการเรียกใช้ฟังก์ชันที่จัดองค์ประกอบได้แบบ ขนานกัน ซึ่งจะช่วยให้ Compose ใช้ประโยชน์จากหลายแกนได้ และเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้ซึ่งไม่ได้อยู่บนหน้าจอด้วยลำดับความสำคัญที่ต่ำกว่า
การเพิ่มประสิทธิภาพนี้หมายความว่าฟังก์ชันที่ใช้ร่วมกันได้อาจทำงานภายในพูลของเธรดเบื้องหลัง
หากฟังก์ชันที่ประกอบกันได้เรียกฟังก์ชันใน ViewModel
,
Compose อาจเรียกฟังก์ชันนั้นจากหลายเธรดพร้อมกัน
ฟังก์ชันที่ใช้ร่วมกันได้ทั้งหมดไม่ควรมีผลข้างเคียงเพื่อให้แน่ใจว่าแอปพลิเคชันทำงานได้อย่างถูกต้อง
แต่ให้ทริกเกอร์ผลข้างเคียงจากโค้ดเรียกกลับ เช่น
onClick
ที่จะเรียกใช้ใน UI เธรดเสมอ
เมื่อเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้ การเรียกใช้อาจเกิดขึ้นใน เธรดที่แตกต่างจากผู้เรียก ซึ่งหมายความว่าควรหลีกเลี่ยงโค้ดที่แก้ไขตัวแปรใน Composable Lambda ทั้งนี้เนื่องจากโค้ดดังกล่าวไม่ปลอดภัยต่อเธรด และเนื่องจาก เป็นผลข้างเคียงที่ไม่ได้รับอนุญาตของ Composable Lambda
ต่อไปนี้เป็นตัวอย่างที่แสดง Composable ที่แสดงรายการและจำนวนของรายการ
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
โค้ดนี้ไม่มีผลข้างเคียงและแปลงรายการอินพุตเป็น UI นี่คือ โค้ดที่ยอดเยี่ยมสำหรับการแสดงรายการขนาดเล็ก อย่างไรก็ตาม หากฟังก์ชันเขียนไปยัง ตัวแปรภายใน โค้ดนี้จะไม่ปลอดภัยต่อเธรดหรือถูกต้อง
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
ในตัวอย่างนี้ items
จะได้รับการแก้ไขทุกครั้งที่มีการจัดองค์ประกอบใหม่ ซึ่งอาจเป็น
ทุกเฟรมของภาพเคลื่อนไหว หรือเมื่อรายการอัปเดต ไม่ว่าจะด้วยวิธีใด UI จะ
แสดงจำนวนที่ไม่ถูกต้อง ด้วยเหตุนี้ Compose จึงไม่รองรับการเขียนแบบนี้ การห้ามการเขียนดังกล่าวช่วยให้เฟรมเวิร์กเปลี่ยนเธรดเพื่อเรียกใช้แลมบ์ดาที่ใช้ร่วมกันได้
ฟังก์ชันที่ประกอบกันได้จะทำงานตามลำดับใดก็ได้
หากดูโค้ดของฟังก์ชันที่ใช้ร่วมกันได้ คุณอาจคิดว่า โค้ดจะทำงานตามลำดับที่ปรากฏ แต่ก็ไม่รับประกันว่าจะเป็นเช่นนั้น หากฟังก์ชันที่ใช้ร่วมกันได้มีคำสั่งเรียกฟังก์ชันที่ใช้ร่วมกันได้อื่นๆ ฟังก์ชันเหล่านั้นอาจทำงานตามลำดับใดก็ได้ Compose มีตัวเลือกในการรับรู้ว่าองค์ประกอบ UI บางอย่างมีความสำคัญสูงกว่าองค์ประกอบอื่นๆ และวาดองค์ประกอบเหล่านั้นก่อน
ตัวอย่างเช่น สมมติว่าคุณมีโค้ดลักษณะนี้เพื่อวาดหน้าจอ 3 หน้าในเลย์เอาต์แท็บ
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
การโทรหา StartScreen
, MiddleScreen
และ EndScreen
อาจเกิดขึ้นตามลำดับใดก็ได้ ซึ่งหมายความว่าคุณไม่สามารถ เช่น StartScreen()
ตั้งค่าตัวแปรส่วนกลางบางอย่าง (ผลข้างเคียง) และให้ MiddleScreen()
ใช้ประโยชน์จากการเปลี่ยนแปลงนั้นได้ แต่ฟังก์ชันแต่ละอย่างต้องมีข้อมูลที่จำเป็นทั้งหมดในตัว
ดูข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีคิดใน Compose และฟังก์ชันที่ใช้ร่วมกันได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้
วิดีโอ
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- Kotlin สำหรับ Jetpack Compose
- สถานะและ Jetpack Compose
- การแบ่งเลเยอร์สถาปัตยกรรมของ Jetpack Compose