การเขียน

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

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

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

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

  • การเขียนลงในพร็อพเพอร์ตี้ของออบเจ็กต์ที่แชร์
  • การอัปเดตออบเจ็กต์ที่สังเกตได้ใน ViewModel
  • การอัปเดตค่ากำหนดที่แชร์

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

ตัวอย่างเช่น โค้ดนี้จะสร้างคอมโพสเซเบิลเพื่ออัปเดตค่าใน SharedPreferences คอมโพสเซเบิลไม่ควรอ่านหรือเขียนจากค่ากำหนดที่แชร์ด้วยตัวเอง แต่โค้ดนี้จะย้ายการอ่านและการเขียนไปยัง ViewModel ในโครูทีนเบื้องหลัง ตรรกะของแอปจะส่งค่าปัจจุบันพร้อม Callback เพื่อทริกเกอร์การอัปเดต

@Composable
fun SharedPrefsToggle(
    text: String,
    value: Boolean,
    onValueChanged: (Boolean) -> Unit
) {
    Row {
        Text(text)
        Checkbox(checked = value, onCheckedChange = onValueChanged)
    }
}

เอกสารนี้กล่าวถึงสิ่งต่างๆ ที่ควรทราบเมื่อใช้ Compose ดังนี้

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

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

การจัดองค์ประกอบใหม่จะข้ามให้ได้มากที่สุด

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

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

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

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

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

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

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

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

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

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

ฟังก์ชันที่ประกอบกันได้สามารถเรียกใช้แบบขนาน

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

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

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

เมื่อมีการเรียกใช้ฟังก์ชันที่ประกอบกันได้ การเรียกใช้อาจเกิดขึ้นในเทรดอื่นที่ไม่ใช่เทรดของผู้เรียก ซึ่งหมายความว่าคุณควรหลีกเลี่ยงโค้ดที่แก้ไขตัวแปรใน Lambda ที่ประกอบกันได้ ทั้งเนื่องจากโค้ดดังกล่าวไม่ปลอดภัยต่อเทรด และเนื่องจากเป็นผลข้างเคียงที่ไม่อนุญาตของ Lambda ที่ประกอบกันได้

ตัวอย่างต่อไปนี้แสดงคอมโพสเซเบิลที่แสดงรายการและจำนวนรายการ

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

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

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

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

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

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

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

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

วิดีโอ