แก้ปัญหาความเสถียร

เมื่อเจอชั้นเรียนที่ไม่เสถียรซึ่งทำให้ประสิทธิภาพ คุณควรทำให้เวอร์ชันเสถียร เอกสารนี้ได้สรุปเทคนิคต่างๆ ที่คุณ ที่สามารถทำได้

เปิดใช้การข้ามอย่างเข้มงวด

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

ดูข้อมูลเพิ่มเติมที่การข้ามอย่างแรง

ทำให้ชั้นเรียนดำเนินไปไม่ได้

นอกจากนี้ คุณยังสามารถพยายามทำให้ชั้นเรียนที่ไม่เสถียรนั้นเปลี่ยนแปลงไม่ได้เลยก็ได้

  • เปลี่ยนแปลงไม่ได้: ระบุประเภทที่ค่าของพร็อพเพอร์ตี้ใดๆ ต้องไม่มี เปลี่ยนหลังสร้างอินสแตนซ์ของประเภทนั้นๆ และเมธอดทั้งหมด มีความโปร่งใสในการอ้างอิง
    • ตรวจสอบว่าพร็อพเพอร์ตี้ทั้งหมดของชั้นเรียนเป็นทั้ง val ไม่ใช่ var และเปลี่ยนแปลงไม่ได้
    • ประเภทพื้นฐาน เช่น String, Int และ Float จะเปลี่ยนแปลงไม่ได้เสมอ
    • หากเป็นไปไม่ได้ คุณต้องใช้สถานะ Compose สำหรับ พร็อพเพอร์ตี้ที่เปลี่ยนแปลงได้
  • เสถียร: ระบุประเภทที่เปลี่ยนแปลงได้ รันไทม์ของ Compose จะไม่ ตระหนักถึงคุณสมบัติหรือวิธีการสาธารณะประเภทใดและเมื่อใด พฤติกรรมจะให้ผลลัพธ์ที่แตกต่างจากการเรียกใช้ก่อนหน้า

คอลเล็กชันที่เปลี่ยนแปลงไม่ได้

สาเหตุทั่วไปที่ Compose ถือว่าชั้นเรียนไม่เสถียรคือคอลเล็กชัน ตามที่ระบุไว้ ในหน้าวิเคราะห์ปัญหาความเสถียร ก็ไม่สามารถแน่ใจได้เต็มร้อยว่าคอลเล็กชัน เช่น List, Map และ Set จะเปลี่ยนแปลงไม่ได้จริงๆ จึงทำเครื่องหมายว่าไม่เสถียร

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

ลองพิจารณาคลาสที่ไม่เสถียรนี้อีกครั้งจากวิเคราะห์ความเสถียร ของ Google ด้วยตนเอง

unstable class Snack {
  
  unstable val tags: Set<String>
  
}

คุณสามารถทำให้ tags เสถียรโดยใช้คอลเล็กชันที่เปลี่ยนแปลงไม่ได้ เปลี่ยนชั้นเรียน ประเภทของ tags เป็น ImmutableSet<String>:

data class Snack{
    
    val tags: ImmutableSet<String> = persistentSetOf()
    
}

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

ใส่คำอธิบายประกอบด้วย Stable หรือ Immutable

เส้นทางที่เป็นไปได้ในการแก้ปัญหาความเสถียรคือการใส่คำอธิบายประกอบชั้นเรียนที่ไม่เสถียร ด้วย @Stable หรือ @Immutable

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

หากทำให้ชั้นเรียนของคุณเสถียรโดยไม่ต้องมีคำอธิบายประกอบ คุณควร จงพยายามทำให้มีเสถียรภาพ

ข้อมูลโค้ดต่อไปนี้ให้ตัวอย่างเล็กน้อยของคลาสข้อมูลที่มีคำอธิบายประกอบเป็น เปลี่ยนแปลงไม่ได้:

@Immutable
data class Snack(

)

ไม่ว่าคุณจะใช้คำอธิบายประกอบ @Immutable หรือ @Stable คอมไพเลอร์ Compose ทำเครื่องหมายคลาส Snack ว่าเสถียร

ชั้นเรียนที่มีคำอธิบายประกอบในคอลเล็กชัน

พิจารณา Composable ที่มีพารามิเตอร์ประเภท List<Snack>:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  
  unstable snacks: List<Snack>
  
)

แม้ว่าคุณจะใส่คำอธิบายประกอบ Snack ด้วย @Immutable แล้ว คอมไพเลอร์ Compose จะยังคงทำเครื่องหมาย พารามิเตอร์ snacks ใน HighlightedSnacks ไม่เสถียร

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

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

คุณสามารถหลีกเลี่ยงปัญหาคอลเล็กชันที่ไม่เสถียรได้หลายวิธี ส่วนย่อยต่อไปนี้จะแสดงวิธีการที่แตกต่างกัน

ไฟล์การกำหนดค่า

หากคุณยินดีจะปฏิบัติตามสัญญาความเสถียรในฐานของโค้ด ก็เลือกใช้เพื่อพิจารณาให้คอลเล็กชัน Kotlin เสถียรได้โดยการเพิ่ม kotlin.collections.* ไปยัง ไฟล์การกำหนดค่าความเสถียร

คอลเล็กชันที่เปลี่ยนแปลงไม่ได้

เพื่อความปลอดภัยของเวลาคอมไพล์สำหรับการเปลี่ยนแปลงไม่ได้ คุณสามารถ ใช้คอลเล็กชันที่เปลี่ยนแปลงไม่ได้ของ kotlinx แทน List

@Composable
private fun HighlightedSnacks(
    
    snacks: ImmutableList<Snack>,
    
)

Wrapper

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

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

จากนั้นคุณสามารถใช้พารามิเตอร์นี้เป็นประเภทของพารามิเตอร์ใน Composable

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

โซลูชัน

หลังจากใช้ทั้งสองวิธี คอมไพเลอร์ Compose จะทำเครื่องหมาย HighlightedSnacks เขียนได้ทั้ง skippable และ restartable

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  stable snacks: ImmutableList<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

ระหว่างการจัดองค์ประกอบใหม่ Compose สามารถข้าม HighlightedSnacks ได้แล้ว หากไม่มี มีการเปลี่ยนแปลง

ไฟล์การกำหนดค่าความเสถียร

เริ่มต้นด้วย Compose Compiler 1.5.5 ซึ่งเป็นไฟล์การกำหนดค่าของคลาส ควรพิจารณาว่าสามารถระบุ "เวอร์ชันเสถียร" ได้ในเวลาคอมไพล์ ซึ่งช่วยให้สามารถพิจารณา ชั้นเรียนที่คุณไม่ได้ควบคุม เช่น ชั้นเรียนห้องสมุดมาตรฐาน เช่น LocalDateTime เสถียร

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

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>

หากต้องการเปิดใช้งานฟีเจอร์นี้ ให้ส่งเส้นทางของไฟล์การกำหนดค่าไปยังส่วน "เขียน" คอมไพเลอร์

ดึงดูด

kotlinOptions {
    freeCompilerArgs += [
            "-P",
            "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
                    project.absolutePath + "/compose_compiler_config.conf"
    ]
}

Kotlin

kotlinOptions {
  freeCompilerArgs += listOf(
      "-P",
      "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
      "${project.absolutePath}/compose_compiler_config.conf"
  )
}

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

หลายโมดูล

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

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

โซลูชัน

ในการแก้ไขปัญหานี้ คุณสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้

  1. เพิ่มคลาสลงในไฟล์การกำหนดค่าคอมไพเลอร์
  2. เปิดใช้งานคอมไพเลอร์ Compose ในโมดูลชั้นข้อมูลหรือแท็กชั้นเรียนของคุณ กับ @Stable หรือ @Immutable ตามความเหมาะสม
    • ซึ่งรวมถึงการเพิ่มทรัพยากร Dependency ของ Compose ลงในชั้นข้อมูลของคุณ อย่างไรก็ตาม นี่เป็นเพียงการพึ่งพารันไทม์ Compose ไม่ใช่สำหรับ Compose-UI
  3. ภายในโมดูล UI ให้รวมคลาสชั้นข้อมูลไว้ใน Wrapper สำหรับ UI โดยเฉพาะ ใหม่

ปัญหาเดียวกันนี้ยังเกิดขึ้นเมื่อใช้ไลบรารีภายนอกหากไม่ใช้ไลบรารี เขียนคอมไพเลอร์

Composable บางรายการไม่ควรข้ามได้

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

มีหลายกรณีที่การข้ามได้ไม่ได้เกิดประโยชน์อย่างแท้จริง และอาจทำให้โค้ดจัดการได้ยาก เช่น

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

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