ใน Jetpack Compose ฟังก์ชันที่ประกอบกันได้มักจะเก็บสถานะโดยใช้ฟังก์ชัน remember ค่าที่จดจำไว้สามารถนำมาใช้ซ้ำในการจัดองค์ประกอบใหม่ได้ตามที่อธิบายไว้ในสถานะและ Jetpack Compose
แม้ว่า remember จะทำหน้าที่เป็นเครื่องมือในการคงค่าไว้ตลอดการจัดองค์ประกอบใหม่ แต่สถานะมักจะต้องคงอยู่ได้นานกว่าอายุการใช้งานขององค์ประกอบ หน้านี้อธิบายความแตกต่างระหว่าง API remember, retain, rememberSaveable และ rememberSerializable, กรณีที่ควรเลือก API ใด และแนวทางปฏิบัติแนะนำในการจัดการค่าที่จดจำและเก็บไว้ใน Compose
เลือกอายุการใช้งานที่ถูกต้อง
ใน Compose มีฟังก์ชันหลายอย่างที่คุณสามารถใช้เพื่อคงสถานะไว้ใน Composition และอื่นๆ ได้ เช่น remember, retain, rememberSaveable และ rememberSerializable ฟังก์ชันเหล่านี้มีอายุการใช้งานและความหมายที่แตกต่างกัน และแต่ละฟังก์ชันเหมาะสำหรับการจัดเก็บสถานะบางประเภท ตารางต่อไปนี้จะอธิบายความแตกต่างดังกล่าว
|
|
|
|
|---|---|---|---|
ค่าจะยังคงอยู่หลังจากการจัดองค์ประกอบใหม่ไหม |
✅ |
✅ |
✅ |
ค่าต่างๆ จะยังคงอยู่เมื่อมีการสร้างกิจกรรมใหม่หรือไม่ |
❌ |
✅ ระบบจะแสดงผลอินสแตนซ์ ( |
✅ ระบบจะแสดงผลออบเจ็กต์ที่เทียบเท่า ( |
ค่าต่างๆ จะยังคงอยู่หลังจากการสิ้นสุดการประมวลผลไหม |
❌ |
❌ |
✅ |
ประเภทข้อมูลที่รองรับ |
ทั้งหมด |
ต้องไม่อ้างอิงออบเจ็กต์ใดๆ ที่จะรั่วไหลหากกิจกรรมถูกทำลาย |
ต้องทำให้เป็นอนุกรมได้ |
กรณีการใช้งาน |
|
|
|
remember
remember เป็นวิธีที่ใช้กันมากที่สุดในการจัดเก็บสถานะใน Compose เมื่อมีการเรียกใช้ remember เป็นครั้งแรก ระบบจะดำเนินการคำนวณที่ระบุและจดจำไว้ ซึ่งหมายความว่า Compose จะจัดเก็บไว้เพื่อให้ Composable นำมาใช้ซ้ำในอนาคต เมื่อ Composable ทำการคอมโพสใหม่ ระบบจะเรียกใช้โค้ดอีกครั้ง แต่การเรียกใช้ remember จะแสดงผลลัพธ์จากคอมโพสิชันก่อนหน้าแทนที่จะทำการคำนวณอีกครั้ง
อินสแตนซ์แต่ละรายการของฟังก์ชันที่ประกอบกันได้จะมีชุดค่าที่จดจำไว้ของตัวเอง ซึ่งเรียกว่าการจดจำตำแหน่ง เมื่อค่าที่จดจำไว้ได้รับการบันทึกเพื่อใช้ในการจัดองค์ประกอบใหม่ ค่าเหล่านั้นจะเชื่อมโยงกับตำแหน่งในลำดับชั้นขององค์ประกอบ หากใช้ Composable ในตำแหน่งที่ต่างกัน แต่ละอินสแตนซ์ในลำดับชั้นของ Composition จะมีชุดค่าที่จดจำไว้เป็นของตัวเอง
เมื่อไม่ได้ใช้ค่าที่จดจำไว้แล้ว ระบบจะลืมและทิ้งบันทึกของค่านั้น ระบบจะลืมค่าที่จดจำไว้เมื่อนำค่าเหล่านั้นออกจากลำดับชั้นของ Composition (รวมถึงเมื่อนำค่าออกและเพิ่มกลับเข้าไปใหม่เพื่อย้ายไปยังตำแหน่งอื่นโดยไม่ใช้ Composable key หรือ MovableContent) หรือเรียกใช้ด้วยพารามิเตอร์ key อื่น
ในบรรดาตัวเลือกที่มี remember มีอายุการใช้งานสั้นที่สุดและลืมค่าเร็วที่สุดในบรรดาฟังก์ชันการจดจำทั้ง 4 ฟังก์ชันที่อธิบายไว้ในหน้านี้
ซึ่งทำให้เหมาะที่สุดสำหรับสิ่งต่อไปนี้
- การสร้างออบเจ็กต์สถานะภายใน เช่น ตำแหน่งการเลื่อนหรือสถานะภาพเคลื่อนไหว
- การหลีกเลี่ยงการสร้างออบเจ็กต์ที่มีค่าใช้จ่ายสูงซ้ำในการจัดองค์ประกอบใหม่แต่ละครั้ง
แต่ควรหลีกเลี่ยงสิ่งต่อไปนี้
- การจัดเก็บข้อมูลที่ผู้ใช้ป้อนด้วย
rememberเนื่องจากระบบจะลืมออบเจ็กต์ที่จดจำไว้เมื่อมีการเปลี่ยนแปลงการกำหนดค่ากิจกรรมและการสิ้นสุดการประมวลผลที่ระบบเริ่มต้น
rememberSaveable และ rememberSerializable
rememberSaveable และ rememberSerializable สร้างขึ้นจาก remember ซึ่งมีอายุการใช้งานยาวนานที่สุดในบรรดาฟังก์ชันการจดจำที่กล่าวถึงในคู่มือนี้
นอกเหนือจากการจดจำออบเจ็กต์ตามตำแหน่งในการจัดองค์ประกอบใหม่แล้ว ยังสามารถบันทึกค่าเพื่อให้กู้คืนค่าเหล่านั้นได้ในการสร้างกิจกรรมใหม่ ซึ่งรวมถึงจากการเปลี่ยนแปลงการกำหนดค่าและการสิ้นสุดการประมวลผล (เมื่อระบบหยุดกระบวนการของแอปขณะที่แอปทำงานอยู่เบื้องหลัง โดยปกติแล้วเพื่อเพิ่มหน่วยความจำสำหรับแอปที่ทำงานอยู่เบื้องหน้า หรือหากผู้ใช้เพิกถอนสิทธิ์จากแอปขณะที่แอปทำงานอยู่)
rememberSerializable ทำงานในลักษณะเดียวกับ rememberSaveable แต่รองรับการคงประเภทที่ซับซ้อนซึ่งทำให้เป็นอนุกรมได้ด้วยไลบรารี kotlinx.serialization โดยอัตโนมัติ เลือก rememberSerializable หากประเภทของคุณมีการทำเครื่องหมาย (หรือสามารถทำเครื่องหมาย) ด้วย @Serializable และเลือก rememberSaveable ในกรณีอื่นๆ ทั้งหมด
ซึ่งทำให้ทั้ง rememberSaveable และ rememberSerializable เหมาะอย่างยิ่งสำหรับการจัดเก็บสถานะที่เชื่อมโยงกับข้อมูลที่ผู้ใช้ป้อน รวมถึงรายการในช่องข้อความ ตำแหน่งการเลื่อน สถานะการสลับ ฯลฯ คุณควรบันทึกสถานะนี้เพื่อให้ผู้ใช้ไม่หลงตำแหน่ง โดยทั่วไป คุณควรใช้ rememberSaveable หรือ rememberSerializable เพื่อจดจำสถานะที่แอปไม่สามารถดึงข้อมูลจากแหล่งข้อมูลถาวรอื่น เช่น ฐานข้อมูล
โปรดทราบว่า rememberSaveable และ rememberSerializable จะบันทึกค่าที่จดจำไว้โดยการแปลงค่าเป็นอนุกรมใน Bundle ซึ่งจะส่งผล 2 ประการดังนี้
- ค่าที่คุณจดจำต้องแสดงได้ด้วยประเภทข้อมูลต่อไปนี้อย่างน้อย 1 ประเภท ได้แก่ ข้อมูลพื้นฐาน (รวมถึง
Int,Long,Float,Double),Stringหรืออาร์เรย์ของประเภทเหล่านี้ - เมื่อกู้คืนค่าที่บันทึกไว้ ค่าดังกล่าวจะเป็นอินสแตนซ์ใหม่ที่เท่ากับ (
==) แต่ไม่ใช่การอ้างอิงเดียวกัน (===) ที่ Composition ใช้ก่อนหน้านี้
หากต้องการจัดเก็บประเภทข้อมูลที่ซับซ้อนมากขึ้นโดยไม่ใช้ kotlinx.serialization คุณสามารถใช้ Saver ที่กำหนดเองเพื่อจัดเรียงและยกเลิกการจัดเรียงออบเจ็กต์เป็นประเภทข้อมูลที่รองรับ โปรดทราบว่า Compose เข้าใจประเภทข้อมูลทั่วไป เช่น State, List, Map, Set ฯลฯ ได้ทันที และจะแปลงข้อมูลเหล่านี้เป็นประเภทที่รองรับโดยอัตโนมัติในนามของคุณ ต่อไปนี้เป็นตัวอย่างของ Saver สำหรับคลาส Size ซึ่งดำเนินการโดยการรวมพร็อพเพอร์ตี้ทั้งหมดของ Size ไว้ในรายการโดยใช้ listSaver
data class Size(val x: Int, val y: Int) { object Saver : androidx.compose.runtime.saveable.Saver<Size, Any> by listSaver( save = { listOf(it.x, it.y) }, restore = { Size(it[0], it[1]) } ) } @Composable fun rememberSize(x: Int, y: Int) { rememberSaveable(x, y, saver = Size.Saver) { Size(x, y) } }
retain
retain API อยู่ระหว่าง remember และ rememberSaveable/rememberSerializable ในแง่ของระยะเวลาที่จดจำค่าไว้ โดยมีชื่อแตกต่างกันเนื่องจากค่าที่เก็บไว้มีวงจรที่แตกต่างจากค่าที่จดจำไว้
เมื่อค่าถูกเก็บรักษาไว้ ค่าดังกล่าวจะได้รับการจดจำตามตำแหน่งและบันทึกไว้ในโครงสร้างข้อมูลรองซึ่งมีอายุการใช้งานแยกต่างหากที่เชื่อมโยงกับอายุการใช้งานของแอป ค่าที่เก็บรักษาไว้จะคงอยู่ได้แม้จะมีการเปลี่ยนแปลงการกำหนดค่าโดยไม่ต้องทำให้เป็นอนุกรม แต่จะคงอยู่ไม่ได้หากการประมวลผลสิ้นสุดลง หากไม่มีการใช้ค่าหลังจากสร้างลำดับชั้นของ Composition ขึ้นมาใหม่ ค่าที่เก็บไว้จะเลิกใช้งาน (ซึ่งเทียบเท่ากับการถูกลืมของ retain)
เพื่อแลกกับวงจรที่สั้นกว่า rememberSaveable นี้ retain จะสามารถคงค่าที่ไม่สามารถทำให้เป็นอนุกรมได้ เช่น นิพจน์ Lambda, โฟลว์ และออบเจ็กต์ขนาดใหญ่ เช่น บิตแมป ตัวอย่างเช่น คุณสามารถใช้ retain เพื่อจัดการมีเดียเพลเยอร์ (เช่น ExoPlayer) เพื่อป้องกันไม่ให้การเล่นสื่อหยุดชะงักระหว่างการเปลี่ยนแปลงการกำหนดค่า
@Composable fun MediaPlayer() { // Use the application context to avoid a memory leak val applicationContext = LocalContext.current.applicationContext val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { /* ... */ }.build() } // ... }
retain พบกับ ViewModel
โดยพื้นฐานแล้ว ทั้ง retain และ ViewModel มีฟังก์ชันการทำงานที่คล้ายกันในความสามารถที่ใช้กันโดยทั่วไปในการคงอินสแตนซ์ออบเจ็กต์ไว้ตลอดการเปลี่ยนแปลงการกำหนดค่า การเลือกว่าจะใช้ retain หรือ ViewModel ขึ้นอยู่กับ
ประเภทของค่าที่คุณเก็บไว้ ขอบเขตของค่า และคุณ
ต้องการฟังก์ชันการทำงานเพิ่มเติมหรือไม่
ViewModel คือออบเจ็กต์ที่มักจะรวมการสื่อสารระหว่าง UI ของแอปกับชั้นข้อมูล ซึ่งช่วยให้คุณย้ายตรรกะออกจากฟังก์ชันที่ประกอบกันได้ ซึ่งจะช่วยปรับปรุงความสามารถในการทดสอบ ViewModel ได้รับการจัดการเป็นซิงเกิลตันภายใน ViewModelStore และมีอายุการใช้งานที่แตกต่างจากค่าที่เก็บไว้ แม้ว่า ViewModel จะยังคงทำงานอยู่จนกว่า ViewModelStore จะถูกทำลาย แต่ค่าที่เก็บไว้จะเลิกใช้งานเมื่อเนื้อหาถูกนำออกจาก Composition อย่างถาวร (ตัวอย่างเช่น สำหรับการเปลี่ยนแปลงการกำหนดค่า หมายความว่าค่าที่เก็บไว้จะเลิกใช้งานหากมีการสร้างลำดับชั้น UI ขึ้นมาใหม่และไม่ได้ใช้ค่าที่เก็บไว้หลังจากสร้าง Composition ขึ้นมาใหม่)
ViewModel ยังรวมถึงการผสานรวมที่พร้อมใช้งานทันทีสำหรับการแทรกทรัพยากร Dependency ด้วย Dagger และ Hilt, การผสานรวมกับ SavedState และการรองรับ Coroutine ในตัวสำหรับการเปิดใช้งานงานเบื้องหลัง ซึ่งทำให้ ViewModel เป็นตำแหน่งที่เหมาะสำหรับการเปิดใช้งานงานเบื้องหลังและคำขอเครือข่าย โต้ตอบกับแหล่งข้อมูลอื่นๆ ในโปรเจ็กต์ และเลือกที่จะบันทึกและคงสถานะ UI ที่สำคัญต่อภารกิจซึ่งควรเก็บไว้ตลอดการเปลี่ยนแปลงการกำหนดค่าใน ViewModel และคงอยู่แม้จะมีการสิ้นสุดการประมวลผล
retain เหมาะที่สุดสำหรับออบเจ็กต์ที่กำหนดขอบเขตไว้สำหรับอินสแตนซ์ Composable ที่เฉพาะเจาะจง และไม่จำเป็นต้องนำมาใช้ซ้ำหรือแชร์ระหว่าง Composable ที่เป็นพี่น้องกัน ในขณะที่ ViewModel เป็นที่ที่เหมาะสำหรับการจัดเก็บสถานะ UI และทำงานในเบื้องหลัง retain ก็เป็นตัวเลือกที่ดีสำหรับการจัดเก็บออบเจ็กต์สำหรับโครงสร้างพื้นฐานของ UI เช่น แคช เครื่องมือติดตามการแสดงผลและการวิเคราะห์ ทรัพยากร Dependency ใน AndroidView และออบเจ็กต์อื่นๆ ที่โต้ตอบกับระบบปฏิบัติการ Android หรือจัดการไลบรารีของบุคคลที่สาม เช่น ผู้ประมวลผลการชำระเงินหรือการกระจายข้อมูล
สำหรับผู้ใช้ขั้นสูงที่ออกแบบรูปแบบสถาปัตยกรรมแอปที่กำหนดเองนอกเหนือจากคำแนะนำเกี่ยวกับสถาปัตยกรรมแอป Android สมัยใหม่: คุณยังใช้ retain เพื่อสร้าง API "ที่คล้ายกับ ViewModel" ภายในองค์กรได้ด้วย แม้ว่าการรองรับโครูทีนและสถานะที่บันทึกไว้จะไม่มีให้ใช้งานตั้งแต่แรก แต่ retain สามารถทำหน้าที่เป็นองค์ประกอบพื้นฐานสำหรับวงจรของ ViewModel ที่มีลักษณะคล้ายกันซึ่งมีฟีเจอร์เหล่านี้อยู่ด้านบน รายละเอียดเกี่ยวกับวิธีออกแบบคอมโพเนนต์ดังกล่าวอยู่นอกขอบเขตของคู่มือนี้
|
|
|
|---|---|---|
การกำหนดขอบเขต |
ไม่มีค่าที่ใช้ร่วมกัน โดยระบบจะเก็บค่าแต่ละค่าไว้และเชื่อมโยงกับจุดที่เฉพาะเจาะจงในลำดับชั้นของ Composition การคงประเภทเดียวกันไว้ในตำแหน่งอื่นจะดำเนินการกับอินสแตนซ์ใหม่เสมอ |
|
การทำลาย |
เมื่อออกจากลำดับชั้นของ Composition อย่างถาวร |
เมื่อมีการล้างหรือทำลาย |
ฟังก์ชันการทำงานเพิ่มเติม |
รับ Callback ได้เมื่อออบเจ็กต์อยู่ในลำดับชั้นขององค์ประกอบหรือไม่ |
|
เป็นของ |
|
|
กรณีการใช้งาน |
|
|
รวม retain และ rememberSaveable หรือ rememberSerializable
บางครั้งออบเจ็กต์ต้องมีอายุการใช้งานแบบผสมทั้ง retained และ rememberSaveable หรือ rememberSerializable ซึ่งอาจเป็นสัญญาณบ่งบอกว่าออบเจ็กต์ของคุณควรเป็น ViewModel ซึ่งรองรับสถานะที่บันทึกไว้ตามที่อธิบายไว้ในคู่มือโมดูลสถานะที่บันทึกไว้สำหรับ ViewModel
คุณใช้ retain และ rememberSaveable หรือ rememberSerializable พร้อมกันได้ การรวมทั้ง 2 วงจรอย่างถูกต้องจะเพิ่มความซับซ้อนอย่างมาก
เราขอแนะนำให้ใช้รูปแบบนี้เป็นส่วนหนึ่งของรูปแบบสถาปัตยกรรมขั้นสูงและแบบกำหนดเอง และเฉพาะในกรณีที่ตรงตามเงื่อนไขต่อไปนี้ทั้งหมด
- คุณกำลังกำหนดออบเจ็กต์ที่ประกอบด้วยค่าต่างๆ ที่ต้องเก็บรักษาหรือบันทึกไว้ (เช่น ออบเจ็กต์ที่ติดตามข้อมูลจากผู้ใช้และแคชหน่วยความจำที่เขียนลงในดิสก์ไม่ได้)
- สถานะของคุณกำหนดขอบเขตไว้ที่ Composable และไม่เหมาะสำหรับการกำหนดขอบเขต Singleton หรืออายุการใช้งานของ
ViewModel
ในกรณีที่เกิดเหตุการณ์เหล่านี้ทั้งหมด เราขอแนะนำให้แบ่งคลาสออกเป็น 3 ส่วน ได้แก่ ข้อมูลที่บันทึกไว้ ข้อมูลที่เก็บไว้ และออบเจ็กต์ "สื่อกลาง" ที่ไม่มีสถานะของตนเอง และมอบสิทธิ์ให้กับออบเจ็กต์ที่เก็บไว้และบันทึกไว้เพื่ออัปเดตสถานะตามความเหมาะสม รูปแบบนี้จะมีลักษณะดังนี้
@Composable fun rememberAndRetain(): CombinedRememberRetained { val saveData = rememberSerializable(serializer = serializer<ExtractedSaveData>()) { ExtractedSaveData() } val retainData = retain { ExtractedRetainData() } return remember(saveData, retainData) { CombinedRememberRetained(saveData, retainData) } } @Serializable data class ExtractedSaveData( // All values that should persist process death should be managed by this class. var savedData: AnotherSerializableType = defaultValue() ) class ExtractedRetainData { // All values that should be retained should appear in this class. // It's possible to manage a CoroutineScope using RetainObserver. // See the full sample for details. var retainedData = Any() } class CombinedRememberRetained( private val saveData: ExtractedSaveData, private val retainData: ExtractedRetainData, ) { fun doAction() { // Manipulate the retained and saved state as needed. } }
การแยกสถานะตามอายุการใช้งานจะทำให้การแยกความรับผิดชอบและพื้นที่เก็บข้อมูลมีความชัดเจนมาก เราตั้งใจไม่ให้ข้อมูลที่บันทึกไว้ถูกจัดการโดยข้อมูลที่เก็บรักษาไว้ เนื่องจากวิธีนี้จะป้องกันสถานการณ์ที่มีการพยายามอัปเดตข้อมูลที่บันทึกไว้เมื่อมีการบันทึกแพ็กเกจ savedInstanceState แล้วและไม่สามารถอัปเดตได้ นอกจากนี้ยังช่วยให้ทดสอบสถานการณ์การสร้างใหม่ได้โดยการทดสอบคอนสตรัคเตอร์โดยไม่ต้องเรียกใช้ Compose หรือจำลองการสร้างกิจกรรมใหม่
ดูตัวอย่างที่เสร็จสมบูรณ์ (RetainAndSaveSample.kt) เพื่อดูตัวอย่างที่สมบูรณ์ของวิธีใช้รูปแบบนี้
การจดจำตำแหน่งและเลย์เอาต์แบบปรับขนาดได้
แอปพลิเคชัน Android รองรับรูปแบบของอุปกรณ์ได้หลายประเภท เช่น โทรศัพท์ อุปกรณ์แบบพับจอได้ แท็บเล็ต และเดสก์ท็อป แอปพลิเคชันมักจะต้องเปลี่ยนรูปแบบของอุปกรณ์เหล่านี้โดยใช้เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ ตัวอย่างเช่น แอปที่ทำงานบนแท็บเล็ตอาจแสดงมุมมองรายละเอียดรายการแบบ 2 คอลัมน์ได้ แต่จะไปยังส่วนต่างๆ ระหว่างหน้ารายการและหน้ารายละเอียดเมื่อแสดงบนหน้าจอโทรศัพท์ที่มีขนาดเล็กกว่า
เนื่องจากค่าที่จดจำและเก็บไว้จะได้รับการจดจำตามตำแหน่ง จึงจะนำกลับมาใช้ใหม่ได้ก็ต่อเมื่อปรากฏในจุดเดียวกันในลำดับชั้นของ Composition เมื่อเลย์เอาต์ปรับให้เข้ากับอุปกรณ์รูปแบบต่างๆ เลย์เอาต์อาจเปลี่ยนโครงสร้างของลำดับชั้นองค์ประกอบและทำให้ค่าถูกลืม
สำหรับคอมโพเนนต์ที่พร้อมใช้งานทันที เช่น ListDetailPaneScaffold และ NavDisplay (จาก Jetpack Navigation 3) ปัญหานี้จะไม่เกิดขึ้นและสถานะจะยังคงอยู่ตลอดการเปลี่ยนแปลงเลย์เอาต์ สำหรับคอมโพเนนต์ที่กำหนดเองซึ่งปรับให้เข้ากับรูปแบบของอุปกรณ์ ให้ตรวจสอบว่าสถานะไม่ได้รับผลกระทบจากการเปลี่ยนแปลงเลย์เอาต์โดยทำอย่างใดอย่างหนึ่งต่อไปนี้
- ตรวจสอบว่ามีการเรียกใช้ Composable ที่เก็บสถานะในตำแหน่งเดียวกันในลำดับชั้นของ Composition เสมอ ใช้เลย์เอาต์แบบปรับอัตโนมัติโดยเปลี่ยนตรรกะเลย์เอาต์แทนการย้ายตำแหน่งออบเจ็กต์ในลำดับชั้นขององค์ประกอบ
- ใช้
MovableContentเพื่อย้ายตำแหน่ง Composable ที่เก็บสถานะได้อย่างราบรื่น อินสแตนซ์ของMovableContentสามารถย้ายค่าที่จดจำและเก็บรักษาไว้จากตำแหน่งเดิมไปยังตำแหน่งใหม่ได้
จดจำฟังก์ชันโรงงาน
แม้ว่า UI ของ Compose จะประกอบด้วยฟังก์ชันที่ประกอบกันได้ แต่วัตถุจำนวนมากก็มีส่วนในการสร้างและจัดระเบียบองค์ประกอบ ตัวอย่างที่พบบ่อยที่สุดคือออบเจ็กต์ Composable ที่ซับซ้อนซึ่งกำหนดสถานะของตนเอง เช่น LazyList ซึ่งยอมรับ LazyListState
เมื่อกำหนดออบเจ็กต์ที่เน้น Compose เราขอแนะนำให้สร้างฟังก์ชัน remember เพื่อกำหนดลักษณะการทำงานของการจดจำที่ต้องการ ซึ่งรวมถึงทั้งอายุการใช้งานและอินพุตคีย์ ซึ่งช่วยให้ผู้ใช้สถานะของคุณสร้างอินสแตนซ์ในลำดับชั้นขององค์ประกอบได้อย่างมั่นใจ ซึ่งจะคงอยู่และถูกทำให้ใช้งานไม่ได้ตามที่คาดไว้ เมื่อกำหนดฟังก์ชันโรงงานที่ประกอบกันได้ ให้ทำตามหลักเกณฑ์ต่อไปนี้
- ใส่
rememberเป็นคำนำหน้าชื่อฟังก์ชัน หรือหากการใช้งานฟังก์ชันขึ้นอยู่กับออบเจ็กต์ที่เป็นretainedและ API จะไม่มีการพัฒนาให้ใช้rememberรูปแบบอื่น ให้ใช้คำนำหน้าretainแทน - ใช้
rememberSaveableหรือrememberSerializableหากเลือกการคงสถานะไว้และสามารถเขียนการติดตั้งใช้งานSaverที่ถูกต้องได้ - หลีกเลี่ยงผลข้างเคียงหรือการเริ่มต้นค่าตาม
CompositionLocalที่อาจไม่เกี่ยวข้องกับการใช้งาน โปรดทราบว่าสถานที่ที่สร้างสถานะ อาจไม่ใช่สถานที่ที่ใช้สถานะ
@Composable fun rememberImageState( imageUri: String, initialZoom: Float = 1f, initialPanX: Int = 0, initialPanY: Int = 0 ): ImageState { return rememberSaveable(imageUri, saver = ImageState.Saver) { ImageState( imageUri, initialZoom, initialPanX, initialPanY ) } } data class ImageState( val imageUri: String, val zoom: Float, val panX: Int, val panY: Int ) { object Saver : androidx.compose.runtime.saveable.Saver<ImageState, Any> by listSaver( save = { listOf(it.imageUri, it.zoom, it.panX, it.panY) }, restore = { ImageState(it[0] as String, it[1] as Float, it[2] as Int, it[3] as Int) } ) }