ใน Jetpack Compose ฟังก์ชันที่ใช้ร่วมกันได้มักจะเก็บสถานะโดยใช้ฟังก์ชัน remember
ค่าที่ระบบจดจำได้จะนำกลับมาใช้ใหม่ได้ในการเขียนใหม่ต่างๆ ตามที่อธิบายไว้ในสถานะและ Jetpack Compose
แม้ว่า remember จะเป็นเครื่องมือที่ใช้คงค่าไว้ในการจัดองค์ประกอบใหม่ แต่สถานะ
มักจะต้องมีอยู่ต่อไปหลังจากที่การจัดองค์ประกอบสิ้นสุดลง หน้านี้อธิบายความแตกต่างระหว่าง API ของ remember, retain, rememberSaveable และ rememberSerializable รวมถึงเวลาที่ควรเลือกใช้ API ใด และแนวทางปฏิบัติแนะนำสำหรับการจัดการค่าที่จดจำและเก็บไว้ใน Compose
เลือกอายุการใช้งานที่ถูกต้อง
ใน Compose มีฟังก์ชันหลายอย่างที่คุณใช้เพื่อคงสถานะไว้ใน
การคอมโพสและอื่นๆ ได้แก่ remember, retain, rememberSaveable และ
rememberSerializable ฟังก์ชันเหล่านี้มีอายุการใช้งานและความหมายที่แตกต่างกัน
และแต่ละฟังก์ชันเหมาะสําหรับจัดเก็บสถานะบางประเภท ความแตกต่างมีดังนี้
ตามที่ระบุไว้ในตารางต่อไปนี้
|
|
|
|
|---|---|---|---|
ค่าจะยังคงอยู่หลังจากการจัดองค์ประกอบใหม่หรือไม่ |
✅ |
✅ |
✅ |
ค่าจะยังคงอยู่เมื่อกิจกรรมถูกสร้างขึ้นใหม่หรือไม่ |
❌ |
✅ ระบบจะแสดงอินสแตนซ์เดียวกัน ( |
✅ ระบบจะแสดงผลออบเจ็กต์ที่เทียบเท่า ( |
ค่าจะยังคงอยู่แม้ว่ากระบวนการจะสิ้นสุดลงใช่ไหม |
❌ |
❌ |
✅ |
ประเภทข้อมูลที่รองรับ |
ทั้งหมด |
ต้องไม่มีการอ้างอิงออบเจ็กต์ใดๆ ที่จะรั่วไหลหากมีการทำลายกิจกรรม |
ต้องเป็นแบบอนุกรม |
กรณีการใช้งาน |
|
|
|
remember
remember เป็นวิธีที่นิยมใช้กันมากที่สุดในการจัดเก็บสถานะใน Compose เมื่อมีการเรียกใช้ remember เป็นครั้งแรก ระบบจะดำเนินการคำนวณที่ระบุและจดจำไว้ ซึ่งหมายความว่า Compose จะจัดเก็บไว้เพื่อให้นำไปใช้ซ้ำในอนาคตโดย Composable เมื่อ Composable ทำการประกอบใหม่ ระบบจะเรียกใช้โค้ดอีกครั้ง แต่การเรียกใช้
remember จะแสดงค่าจากการประกอบก่อนหน้าแทนที่จะ
ทำการคำนวณอีกครั้ง
อินสแตนซ์ของฟังก์ชันที่ประกอบกันได้แต่ละรายการจะมีชุดค่าที่จดจำของตัวเอง ซึ่งเรียกว่าการจดจำตามตำแหน่ง เมื่อระบบจดจำค่าที่บันทึกไว้เพื่อใช้ในการ จัดองค์ประกอบใหม่ ค่าเหล่านั้นจะเชื่อมโยงกับตำแหน่งในลำดับชั้นการจัดองค์ประกอบ หากใช้ Composable ในตำแหน่งต่างๆ แต่ละอินสแตนซ์ใน ลำดับชั้นการจัดองค์ประกอบจะมีชุดค่าที่จดจำของตัวเอง
เมื่อไม่ได้ใช้ค่าที่จดจำไว้อีกต่อไป ระบบจะลืมค่าดังกล่าวและทิ้งบันทึกของค่านั้น ระบบจะลืมค่าที่จดจำไว้เมื่อนำค่าออกจาก
ลำดับชั้นการเขียน (รวมถึงเมื่อนำค่าออกและเพิ่มกลับเพื่อย้ายไปยัง
ตำแหน่งอื่นโดยไม่ต้องใช้ key composable หรือ
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 ประการ ดังนี้
- ค่าที่คุณบันทึกต้องแสดงด้วยข้อมูลประเภทใดประเภทหนึ่งต่อไปนี้: ค่าดั้งเดิม (รวมถึง
Int,Long,Float,Double),Stringหรืออาร์เรย์ของประเภทใดประเภทหนึ่งเหล่านี้ - เมื่อกู้คืนค่าที่บันทึกไว้ ค่าดังกล่าวจะเป็นอินสแตนซ์ใหม่ที่เท่ากับ
(
==) แต่ไม่ใช่การอ้างอิงเดียวกัน (===) กับที่การเรียบเรียงใช้ก่อนหน้านี้
หากต้องการจัดเก็บประเภทข้อมูลที่ซับซ้อนมากขึ้นโดยไม่ใช้ 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 ในแง่ของระยะเวลาที่จดจำค่า เราตั้งชื่อแตกต่างกันเนื่องจากค่าที่เก็บไว้จะมีวงจรที่แตกต่างจากค่าที่จดจำไว้
เมื่อเก็บรักษาค่าไว้ ระบบจะบันทึกค่าดังกล่าวทั้งในรูปแบบการจดจำตำแหน่งและใน
โครงสร้างข้อมูลรองที่มีอายุการใช้งานแยกต่างหากซึ่งเชื่อมโยงกับอายุการใช้งานของแอป ค่าที่เก็บไว้จะยังคงอยู่ได้แม้จะมีการเปลี่ยนแปลงการกำหนดค่าโดยไม่ต้อง
ทำการซีเรียลไลซ์ แต่จะยังคงอยู่ไม่ได้หากกระบวนการสิ้นสุดลง หากไม่ได้ใช้ค่าหลังจากสร้างลำดับชั้นขององค์ประกอบใหม่ ค่าที่เก็บไว้จะเลิกใช้งาน (ซึ่งเทียบเท่ากับการลืมของ retain)
เพื่อแลกกับวงจร rememberSaveable ที่สั้นกว่านี้ Retain จึงสามารถ
เก็บค่าที่ทำการซีเรียลไลซ์ไม่ได้ เช่น นิพจน์ Lambda, Flow และ
ออบเจ็กต์ขนาดใหญ่ เช่น บิตแมป ตัวอย่างเช่น คุณสามารถใช้ 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 จะถูกทำลาย แต่ค่าที่เก็บไว้จะเลิกใช้งานเมื่อเนื้อหาถูกนำออกจากองค์ประกอบอย่างถาวร (ตัวอย่างเช่น สำหรับการเปลี่ยนแปลงการกำหนดค่า หมายความว่าค่าที่เก็บไว้จะเลิกใช้งานหากมีการสร้างลำดับชั้น UI ขึ้นใหม่และค่าที่เก็บไว้ไม่ได้ใช้หลังจากสร้างองค์ประกอบขึ้นใหม่)
ViewModel ยังรวมถึงการผสานรวมที่พร้อมใช้งานสำหรับการแทรกการอ้างอิง
ด้วย Dagger และ Hilt, การผสานรวมกับ SavedState และการรองรับโครูทีนในตัว
สำหรับการเปิดใช้งานงานในเบื้องหลัง ซึ่งทำให้ ViewModel เป็นที่ที่เหมาะสำหรับ
เปิดใช้งานงานเบื้องหลังและคำขอเครือข่าย โต้ตอบกับแหล่งข้อมูลอื่นๆ
ในโปรเจ็กต์ และบันทึกและคงสถานะ UI ที่สำคัญต่อภารกิจ (ไม่บังคับ)
ซึ่งควรคงไว้เมื่อมีการเปลี่ยนแปลงการกำหนดค่าใน ViewModel และ
ยังคงอยู่แม้ว่ากระบวนการจะสิ้นสุดลง
retain เหมาะที่สุดสำหรับออบเจ็กต์ที่กำหนดขอบเขตไว้สำหรับอินสแตนซ์ที่เฉพาะเจาะจงของ Composable
และไม่จำเป็นต้องนำกลับมาใช้ซ้ำหรือแชร์ระหว่าง Composable ที่เป็นพี่น้อง ในกรณีที่
ViewModel ทำหน้าที่เป็นที่เก็บสถานะ UI และทำงานในเบื้องหลังได้ดี
retain ก็เป็นตัวเลือกที่ดีในการจัดเก็บออบเจ็กต์สำหรับการเชื่อมต่อ UI เช่น แคช
การติดตามและการวิเคราะห์การแสดงผล การอ้างอิงใน AndroidView และออบเจ็กต์อื่นๆ
ที่โต้ตอบกับระบบปฏิบัติการ Android หรือจัดการไลบรารีของบุคคลที่สาม เช่น
โปรแกรมประมวลผลการชำระเงินหรือการโฆษณา
สำหรับผู้ใช้ขั้นสูงที่ออกแบบรูปแบบสถาปัตยกรรมแอปที่กำหนดเองนอก
คำแนะนำเกี่ยวกับสถาปัตยกรรมแอป Android สมัยใหม่: retain ยังสามารถใช้เพื่อ
สร้าง API "ViewModel" ภายในองค์กรได้ด้วย แม้ว่าเราจะไม่ได้ให้การสนับสนุนโครูทีนและ
สถานะที่บันทึกไว้ตั้งแต่แรก แต่ retain ก็สามารถเป็นบล็อก
การสร้างสำหรับวงจรของ ViewModel ที่คล้ายกันซึ่งมีฟีเจอร์เหล่านี้
สร้างไว้ด้านบน รายละเอียดเกี่ยวกับวิธีออกแบบคอมโพเนนต์ดังกล่าวอยู่นอก
ขอบเขตของคู่มือนี้
|
|
|
|---|---|---|
การกำหนดขอบเขต |
ไม่มีค่าที่แชร์ แต่ละค่าจะยังคงอยู่ในและเชื่อมโยงกับจุดที่เฉพาะเจาะจงในลำดับชั้นขององค์ประกอบ การคงประเภทเดิมไว้ใน ตำแหน่งอื่นจะส่งผลต่ออินสแตนซ์ใหม่เสมอ |
|
การทำลาย |
เมื่อออกจากลำดับชั้นขององค์ประกอบอย่างถาวร |
เมื่อมีการล้างหรือทำลาย |
ฟังก์ชันการทำงานเพิ่มเติม |
รับการเรียกกลับได้เมื่อออบเจ็กต์อยู่ในลำดับชั้นการจัดองค์ประกอบ หรือไม่ |
|
เป็นของ |
|
|
กรณีการใช้งาน |
|
|
รวม retain กับ rememberSaveable หรือ rememberSerializable
บางครั้งออบเจ็กต์ต้องมีอายุการใช้งานแบบผสมทั้ง retained และ
rememberSaveable หรือ rememberSerializable ซึ่งอาจเป็นตัวบ่งชี้ว่าออบเจ็กต์ควรเป็น ViewModel ซึ่งรองรับสถานะที่บันทึกไว้ตามที่อธิบายไว้ในโมดูลสถานะที่บันทึกไว้สำหรับคู่มือ ViewModel
คุณใช้ retain และ rememberSaveable หรือ rememberSerializable
พร้อมกันได้ การรวมวงจรทั้ง 2 อย่างอย่างถูกต้องจะเพิ่มความซับซ้อนอย่างมาก
เราขอแนะนำให้ใช้รูปแบบนี้เป็นส่วนหนึ่งของรูปแบบสถาปัตยกรรมขั้นสูงและกำหนดเองมากขึ้น และเฉพาะในกรณีที่ตรงตามเงื่อนไขต่อไปนี้ทั้งหมด
- คุณกําลังกําหนดออบเจ็กต์ที่ประกอบด้วยค่าผสมที่ต้องเก็บไว้หรือบันทึก (เช่น ออบเจ็กต์ที่ติดตามอินพุตของผู้ใช้และแคชในหน่วยความจําที่เขียนลงดิสก์ไม่ได้)
- สถานะของคุณมีขอบเขตเป็นแบบ Composable และไม่เหมาะกับขอบเขตหรืออายุการใช้งานของ
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 หรือจำลองการสร้างใหม่ของ Activity
ดูตัวอย่างฉบับเต็ม (RetainAndSaveSample.kt) เพื่อดูตัวอย่างที่สมบูรณ์ของ
วิธีใช้รูปแบบนี้
การจดจำตำแหน่งและเลย์เอาต์แบบปรับได้
แอปพลิเคชัน Android รองรับรูปแบบของอุปกรณ์หลายอย่าง เช่น โทรศัพท์ อุปกรณ์พับได้ แท็บเล็ต และเดสก์ท็อป แอปพลิเคชันมักจะต้องเปลี่ยนรูปแบบระหว่างอุปกรณ์เหล่านี้ โดยใช้เลย์เอาต์ที่ปรับเปลี่ยนได้ ตัวอย่างเช่น แอปที่ทำงานบนแท็บเล็ต อาจแสดงมุมมองแบบรายการ-รายละเอียด 2 คอลัมน์ได้ แต่เมื่อแสดงบนหน้าจอโทรศัพท์ขนาดเล็กกว่า อาจนำทางระหว่างรายการกับหน้ารายละเอียด
เนื่องจากค่าที่จดจำและคงไว้จะได้รับการบันทึกตามตำแหน่ง จึงจะนำกลับมาใช้ใหม่ได้ก็ต่อเมื่อค่าเหล่านั้นปรากฏในจุดเดียวกันในลำดับชั้นของการจัดองค์ประกอบ เมื่อเลย์เอาต์ปรับให้เข้ากับอุปกรณ์รูปแบบต่างๆ โครงสร้างของลำดับชั้นการจัดองค์ประกอบอาจเปลี่ยนแปลงและทำให้ค่าบางอย่างถูกลืมไป
สำหรับคอมโพเนนต์สำเร็จรูป เช่น ListDetailPaneScaffold และ NavDisplay
(จาก Jetpack Navigation 3) ปัญหานี้จะไม่เกิดขึ้นและสถานะจะยังคงอยู่
ตลอดการเปลี่ยนแปลงเลย์เอาต์ สําหรับคอมโพเนนต์ที่กําหนดเองซึ่งปรับให้เข้ากับรูปแบบอุปกรณ์
โปรดตรวจสอบว่าการเปลี่ยนแปลงเลย์เอาต์ไม่ส่งผลต่อสถานะโดยทําอย่างใดอย่างหนึ่งต่อไปนี้
- ตรวจสอบว่ามีการเรียกใช้ Composable ที่มีสถานะในที่เดิมเสมอใน ลำดับชั้นการจัดองค์ประกอบ ใช้เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์โดยการเปลี่ยนตรรกะของเลย์เอาต์ แทนการย้ายออบเจ็กต์ในลำดับชั้นของการจัดองค์ประกอบ
- ใช้
MovableContentเพื่อย้าย Composable ที่มีสถานะอย่างราบรื่น อินสแตนซ์ ของMovableContentสามารถย้ายค่าที่จดจำและเก็บรักษาไว้จากตำแหน่งเก่าไปยังตำแหน่งใหม่ได้
จดจำฟังก์ชันจากโรงงาน
แม้ว่า UI ของ Compose จะประกอบด้วยฟังก์ชันที่ประกอบกันได้ แต่ก็มีออบเจ็กต์หลายรายการที่ใช้ในการสร้างและจัดระเบียบ Composition
ตัวอย่างที่พบบ่อยที่สุดของกรณีนี้
คือออบเจ็กต์ที่ซับซ้อนซึ่งกำหนดสถานะของตัวเอง เช่น 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) } ) }