การเขียน

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 ที่แสดงข้อความทักทาย

ภาพหน้าจอของโทรศัพท์ที่แสดงข้อความ "Hello World" และโค้ดสำหรับ
ฟังก์ชัน Composable อย่างง่ายที่สร้าง
UI นั้น

รูปที่ 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 ทุกครั้งที่ ข้อมูลที่สังเกตได้มีการอัปเดต

ภาพประกอบโฟลว์ของข้อมูลใน UI ของ Compose จากออบเจ็กต์ระดับสูงลงไปจนถึงออบเจ็กต์ย่อย

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

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

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

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

@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 ได้อย่างมีประสิทธิภาพ

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

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

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

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

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

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

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

วิดีโอ