การเขียน

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

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

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

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

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

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

ฟังก์ชันที่ประกอบกันได้จะดำเนินการตามลำดับใดก็ได้

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

ตัวอย่างเช่น สมมติว่าคุณมีโค้ดลักษณะนี้เพื่อวาด 3 หน้าจอในเลย์เอาต์แท็บ

@Composable
fun ButtonRow() {
    MyFancyNavigation {
        StartScreen()
        MiddleScreen()
        EndScreen()
    }
}

การโทรหา StartScreen, MiddleScreen และ EndScreen อาจเกิดขึ้นตามลำดับใดก็ได้ ซึ่งหมายความว่าคุณไม่สามารถ เช่น StartScreen() ตั้งค่าตัวแปรส่วนกลาง บางอย่าง (ผลข้างเคียง) และให้ MiddleScreen() ใช้ประโยชน์จากการเปลี่ยนแปลงนั้น ได้ แต่ฟังก์ชันแต่ละอย่างต้องมีข้อมูลที่จำเป็นทั้งหมดในตัว

ดูข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีคิดใน Compose และฟังก์ชันที่ใช้ร่วมกันได้ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

วิดีโอ