การเขียน

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 โดยไม่เรียกใช้ฟังก์ชันที่ประกอบกันได้ระดับบนสุดเมื่อ 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 และฟังก์ชันที่ประกอบกันได้

วิดีโอ