การเขียน

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

โทรศัพท์แสดงข้อความ Hello World และโค้ดสำหรับฟังก์ชันที่ประกอบกันได้ ซึ่งสร้าง UI นั้น
รูปที่ 1 ฟังก์ชันที่ประกอบกันได้ซึ่งรับข้อมูลและใช้ข้อมูลนั้นเพื่อแสดงผลวิดเจ็ตข้อความบนหน้าจอ

ข้อควรทราบเกี่ยวกับฟังก์ชันนี้มีดังนี้

  • คำอธิบายประกอบ: ฟังก์ชันนี้มีคำอธิบายประกอบด้วยคำอธิบายประกอบ @Composable ฟังก์ชันที่ใช้ร่วมกันได้ทั้งหมดต้องมีคำอธิบายประกอบนี้ คำอธิบายประกอบนี้จะแจ้งให้คอมไพเลอร์ Compose ทราบว่าฟังก์ชันนี้มีไว้เพื่อแปลงข้อมูลเป็น UI
  • อินพุตข้อมูล: ฟังก์ชันจะรับข้อมูล ฟังก์ชันที่ประกอบกันได้สามารถรับพารามิเตอร์ ซึ่งช่วยให้ตรรกะของแอปอธิบาย UI ได้ ในกรณีนี้ วิดเจ็ตของเรายอมรับ String เพื่อให้ทักทายผู้ใช้ตามชื่อได้
  • การแสดง UI: ฟังก์ชันจะแสดงข้อความใน UI โดยจะทำเช่นนั้นด้วยการเรียกใช้ ฟังก์ชันที่ประกอบกันได้ Text() ซึ่งจะสร้างองค์ประกอบ UI ของข้อความ จริงๆ ฟังก์ชันที่ประกอบกันได้จะปล่อยลำดับชั้น UI โดยการเรียกฟังก์ชันที่ประกอบกันได้อื่นๆ
  • ไม่มีค่าที่ส่งคืน: ฟังก์ชันจะไม่ส่งคืนค่าใดๆ ฟังก์ชัน Compose ที่ปล่อย UI ไม่จำเป็นต้องแสดงผลอะไร เนื่องจากฟังก์ชันจะอธิบายสถานะหน้าจอเป้าหมายแทนที่จะสร้างวิดเจ็ต UI
  • คุณสมบัติ: ฟังก์ชันนี้รวดเร็ว ไม่เปลี่ยนแปลง และไม่มีผลข้างเคียง

    • ฟังก์ชันจะทํางานในลักษณะเดียวกันเมื่อเรียกใช้หลายครั้งด้วยอาร์กิวเมนต์เดียวกัน และจะไม่ใช้ค่าอื่นๆ เช่น ตัวแปรส่วนกลางหรือการเรียกใช้ random()
    • ฟังก์ชันจะอธิบาย UI โดยไม่มีผลข้างเคียง เช่น การแก้ไขพร็อพเพอร์ตี้หรือตัวแปรส่วนกลาง

    โดยทั่วไป ฟังก์ชันที่ประกอบกันได้ทั้งหมดต้องเขียนด้วยพร็อพเพอร์ตี้เหล่านี้ด้วยเหตุผลที่กล่าวถึงในการจัดองค์ประกอบใหม่

การเปลี่ยนแปลงกระบวนทัศน์แบบประกาศ

ในชุดเครื่องมือ UI แบบออบเจ็กต์เชิงคำสั่งหลายชุด คุณจะเริ่มต้น UI โดย การสร้างอินสแตนซ์ของแผนผังวิดเจ็ต โดยมักจะทำได้ด้วยการขยายไฟล์เลย์เอาต์ XML วิดเจ็ตแต่ละรายการจะรักษาสถานะภายในของตัวเอง และแสดงเมธอด Getter และ Setter ที่ช่วยให้ตรรกะของแอปโต้ตอบกับวิดเจ็ตได้

ในแนวทางแบบประกาศของ Compose วิดเจ็ตจะไม่มีสถานะและไม่ แสดงฟังก์ชัน Setter หรือ Getter ซึ่งในความเป็นจริงแล้ว ระบบไม่ได้แสดงวิดเจ็ตเป็นออบเจ็กต์ คุณอัปเดต UI ได้โดยการเรียกใช้ฟังก์ชันที่ประกอบกันได้เดียวกันด้วยอาร์กิวเมนต์ที่แตกต่างกัน ซึ่งจะช่วยให้การระบุสถานะกับรูปแบบสถาปัตยกรรมต่างๆ เช่น ViewModel เป็นเรื่องง่าย ดังที่อธิบายไว้ใน คู่มือสถาปัตยกรรมแอป จากนั้น Composable จะ มีหน้าที่แปลงสถานะแอปพลิเคชันปัจจุบันเป็น UI ทุกครั้งที่ ข้อมูลที่สังเกตได้มีการอัปเดต

ภาพการไหลของข้อมูลใน UI ของ Compose จากออบเจ็กต์ระดับสูงลงมา
จนถึงออบเจ็กต์ย่อย
รูปที่ 2 ตรรกะของแอปจะให้ข้อมูลแก่ฟังก์ชันที่ประกอบกันได้ระดับบนสุด ฟังก์ชันดังกล่าวใช้ข้อมูลเพื่ออธิบาย UI โดยการเรียกใช้ Composable อื่นๆ และ ส่งข้อมูลที่เหมาะสมไปยัง Composable เหล่านั้นและลงไปตามลำดับชั้น

เมื่อผู้ใช้โต้ตอบกับ UI ทาง UI จะสร้างเหตุการณ์ต่างๆ เช่น onClick เหตุการณ์เหล่านั้นควรแจ้งตรรกะของแอป ซึ่งจะเปลี่ยนสถานะของแอปได้ เมื่อสถานะเปลี่ยนแปลง ระบบจะเรียกใช้ฟังก์ชันที่ประกอบกันได้อีกครั้งพร้อมข้อมูลใหม่ ซึ่งจะทำให้องค์ประกอบ UI วาดใหม่ กระบวนการนี้เรียกว่าการจัดองค์ประกอบใหม่

ภาพแสดงวิธีที่องค์ประกอบ UI ตอบสนองต่อการโต้ตอบโดยการทริกเกอร์เหตุการณ์ที่แอปจัดการ
ตรรกะ
รูปที่ 3 ผู้ใช้โต้ตอบกับองค์ประกอบ UI ซึ่งทําให้ระบบทริกเกอร์เหตุการณ์ ตรรกะของแอปจะตอบสนองต่อเหตุการณ์ จากนั้นระบบจะเรียกใช้ฟังก์ชันที่ใช้ร่วมกันได้อีกครั้งโดยอัตโนมัติพร้อมพารามิเตอร์ใหม่ หากจำเป็น

เนื้อหาแบบไดนามิก

เนื่องจากฟังก์ชันที่ใช้ Compose เขียนด้วย 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 สามารถจัดองค์ประกอบใหม่ได้อย่างชาญฉลาดเฉพาะคอมโพเนนต์ที่มีการเปลี่ยนแปลงเท่านั้น

ตัวอย่างเช่น ลองดูฟังก์ชันที่ประกอบกันได้นี้ซึ่งแสดงปุ่ม

@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 อย่างมีประสิทธิภาพด้วยการข้ามฟังก์ชันหรือ Lambda ที่มีพารามิเตอร์ที่ไม่เปลี่ยนแปลง

อย่าพึ่งพาผลข้างเคียงจากการเรียกใช้ฟังก์ชันที่ประกอบกันได้ เนื่องจากอาจมีการข้ามการจัดองค์ประกอบใหม่ของฟังก์ชัน หากทำเช่นนั้น ผู้ใช้อาจพบพฤติกรรมที่แปลกประหลาด และคาดเดาไม่ได้ในแอปของคุณ ผลข้างเคียงคือการเปลี่ยนแปลงใดๆ ที่ มองเห็นได้ในส่วนอื่นๆ ของแอป ตัวอย่างเช่น การดำเนินการต่อไปนี้เป็นผลข้างเคียงที่อันตราย ทั้งหมด

  • การเขียนไปยังพร็อพเพอร์ตี้ของออบเจ็กต์ที่แชร์
  • การอัปเดตข้อมูลที่สังเกตได้ใน 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 จะพยายามอย่างเต็มที่เพื่อทำการ Recompose เฉพาะส่วนที่ต้องอัปเดต ซึ่งหมายความว่า Compose อาจข้ามไปเรียกใช้ Composable ของ Button รายการเดียวอีกครั้งโดยไม่ต้องเรียกใช้ 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 ไม่มีการเปลี่ยนแปลง

อีกครั้งที่ฟังก์ชันที่ใช้ร่วมกันได้หรือแลมบ์ดาทั้งหมดควรไม่มีผลข้างเคียง เมื่อต้องการดำเนินการที่มีผลข้างเคียง ให้ทริกเกอร์จากโค้ดเรียกกลับ

การจัดองค์ประกอบใหม่เป็นแบบมองในแง่ดี

การจัดองค์ประกอบใหม่จะเริ่มเมื่อใดก็ตามที่ Compose คิดว่าพารามิเตอร์ของ Composable อาจมีการเปลี่ยนแปลง การจัดองค์ประกอบใหม่เป็นแบบมองโลกในแง่ดี ซึ่งหมายความว่า Compose คาดหวัง ที่จะจัดองค์ประกอบใหม่ให้เสร็จก่อนที่พารามิเตอร์จะเปลี่ยนแปลงอีกครั้ง หากพารามิเตอร์มีการเปลี่ยนแปลงก่อนที่การจัดองค์ประกอบใหม่จะเสร็จสมบูรณ์ Compose อาจยกเลิกการจัดองค์ประกอบใหม่และรีสตาร์ทด้วยพารามิเตอร์ใหม่

เมื่อยกเลิกการจัดองค์ประกอบใหม่ Compose จะทิ้งแผนผัง UI จากการจัดองค์ประกอบใหม่ หากคุณมีผลข้างเคียงที่ขึ้นอยู่กับ UI ที่แสดง ผลข้างเคียงจะยังคงมีผลแม้ว่าจะยกเลิกการเขียนก็ตาม ซึ่งอาจทําให้สถานะของแอปไม่สอดคล้องกัน

ตรวจสอบว่าฟังก์ชันที่ประกอบได้และ Lambda ทั้งหมดเป็นแบบ Idempotent และไม่มีผลข้างเคียง เพื่อจัดการการจัดองค์ประกอบใหม่แบบอิงตามการมองในแง่ดี

ฟังก์ชันที่ประกอบกันได้อาจทำงานบ่อยครั้ง

ในบางกรณี ฟังก์ชันที่ประกอบกันได้อาจทำงานสำหรับทุกเฟรมของภาพเคลื่อนไหว UI หากฟังก์ชันดำเนินการที่มีค่าใช้จ่ายสูง เช่น การอ่านจาก ที่เก็บข้อมูลของอุปกรณ์ ฟังก์ชันดังกล่าวอาจทำให้ UI กระตุก

เช่น หากวิดเจ็ตพยายามอ่านการตั้งค่าอุปกรณ์ วิดเจ็ตอาจอ่านการตั้งค่าเหล่านั้นหลายร้อยครั้งต่อวินาที ซึ่งจะส่งผลเสียต่อประสิทธิภาพของแอป

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

วิดีโอ