ใน Jetpack Compose ออบเจ็กต์สามารถใช้ RememberObserver เพื่อรับ
การเรียกกลับเมื่อใช้กับ remember เพื่อให้ทราบเวลาที่เริ่มและหยุด
การจดจำในลำดับชั้นการจัดองค์ประกอบ ในทำนองเดียวกัน คุณสามารถใช้
RetainObserver เพื่อรับข้อมูลเกี่ยวกับสถานะของออบเจ็กต์ที่ใช้
กับ retain
สำหรับออบเจ็กต์ที่ใช้ข้อมูลวงจรนี้จากลำดับชั้นขององค์ประกอบ
เราขอแนะนำแนวทางปฏิบัติแนะนำ 2-3 ข้อเพื่อยืนยันว่าออบเจ็กต์ของคุณทำหน้าที่เป็น
พลเมืองที่ดีในแพลตฟอร์มและป้องกันการใช้งานในทางที่ผิด โดยเฉพาะอย่างยิ่ง ให้ใช้การเรียกกลับ onRemembered (หรือ onRetained) เพื่อเปิดใช้งานแทนตัวสร้าง
ยกเลิกงานทั้งหมดเมื่อระบบหยุดจดจำหรือเก็บออบเจ็กต์ไว้ และ
หลีกเลี่ยงการรั่วไหลของการใช้งาน RememberObserver และ RetainObserver เพื่อ
หลีกเลี่ยงการเรียกโดยไม่ตั้งใจ ส่วนถัดไปจะอธิบายคำแนะนำเหล่านี้อย่างละเอียดมากขึ้น
การเริ่มต้นและการล้างข้อมูลด้วย RememberObserver และ RetainObserver
คู่มือการคิดใน Compose อธิบายโมเดลความคิดเบื้องหลัง
การเขียน เมื่อทำงานกับ RememberObserver และ RetainObserver คุณควรคำนึงถึงลักษณะการทำงาน 2 อย่างของการคอมโพส ดังนี้
- การเขียนใหม่เป็นการดำเนินการแบบมองโลกในแง่ดีและอาจถูกยกเลิก
- ฟังก์ชันที่ใช้ร่วมกันได้ทั้งหมดไม่ควรมีผลข้างเคียง
เรียกใช้ผลข้างเคียงของการเริ่มต้นระหว่าง onRemembered หรือ onRetained ไม่ใช่การสร้าง
เมื่อมีการจดจำหรือเก็บรักษาออบเจ็กต์ไว้ Lambda การคำนวณจะทำงานเป็นส่วนหนึ่งของการประกอบ ด้วยเหตุผลเดียวกันกับที่คุณไม่ควรดำเนินการที่มีผลข้างเคียงหรือ
เปิดใช้โครูทีนระหว่างการคอมโพส คุณจึงไม่ควรดำเนินการที่มีผลข้างเคียง
ใน Lambda การคำนวณที่ส่งไปยัง remember, retain และรูปแบบต่างๆ ของฟังก์ชันเหล่านั้น
ซึ่งรวมถึงเป็นส่วนหนึ่งของตัวสร้างสำหรับออบเจ็กต์ที่จดจำหรือเก็บไว้
แต่เมื่อใช้ RememberObserver หรือ RetainObserver ให้ตรวจสอบว่า
มีการเรียกใช้เอฟเฟกต์และงานที่เปิดตัวทั้งหมดในแฮนเดิล onRemembered
ซึ่งมีเวลาเดียวกันกับ SideEffect API นอกจากนี้ยังรับประกันว่า
เอฟเฟกต์เหล่านี้จะทำงานเมื่อใช้การจัดองค์ประกอบเท่านั้น ซึ่งจะป้องกัน
งานที่ไม่มีการอ้างอิงและหน่วยความจำรั่วหากมีการละทิ้งหรือเลื่อนการจัดองค์ประกอบใหม่
class MyComposeObject : RememberObserver { private val job = Job() private val coroutineScope = CoroutineScope(Dispatchers.Main + job) init { // Not recommended: This will cause work to begin during composition instead of // with other effects. Move this into onRemembered(). coroutineScope.launch { loadData() } } override fun onRemembered() { // Recommended: Move any cancellable or effect-driven work into the onRemembered // callback. If implementing RetainObserver, this should go in onRetained. coroutineScope.launch { loadData() } } private suspend fun loadData() { /* ... */ } // ... }
การถอดแยกชิ้นส่วนเมื่อลืม เลิกใช้ หรือทิ้ง
หากต้องการหลีกเลี่ยงการรั่วไหลของทรัพยากรหรือการทิ้งงานในเบื้องหลังไว้โดยไม่มีเจ้าของ คุณต้องทิ้งออบเจ็กต์ที่จดจำไว้ด้วย สำหรับออบเจ็กต์ที่ใช้ RememberObserver
ซึ่งหมายความว่าทุกอย่างที่เริ่มต้นใน onRemembered ต้องมีการเรียกใช้
การเผยแพร่ที่ตรงกันใน onForgotten
เนื่องจากยกเลิกการคอมโพสได้ ออบเจ็กต์ที่ใช้ RememberObserver
ต้องล้างข้อมูลของตัวเองด้วยหากถูกทิ้งไว้ในคอมโพส ระบบจะทิ้ง
ออบเจ็กต์เมื่อ remember ส่งคืนออบเจ็กต์ในองค์ประกอบที่ถูก
ยกเลิกหรือล้มเหลว (ปัญหานี้มักเกิดขึ้นเมื่อใช้ PausableComposition
และอาจเกิดขึ้นเมื่อใช้การโหลดซ้ำด่วนกับเครื่องมือแสดงตัวอย่างที่ใช้ได้ของ Android Studio ด้วย)
เมื่อทิ้งออบเจ็กต์ที่จดจำไว้ ออบเจ็กต์จะได้รับการเรียกใช้เฉพาะ onAbandoned เท่านั้น (และไม่มีการเรียกใช้ onRemembered) หากต้องการใช้เมธอดทิ้ง ให้ทิ้งทุกอย่างที่สร้างขึ้นระหว่างเวลาที่เริ่มต้นออบเจ็กต์กับเวลาที่ออบเจ็กต์จะได้รับโค้ดเรียกกลับ onRemembered
class MyComposeObject : RememberObserver { private val job = Job() private val coroutineScope = CoroutineScope(Dispatchers.Main + job) // ... override fun onForgotten() { // Cancel work launched from onRemembered. If implementing RetainObserver, onRetired // should cancel work launched from onRetained. job.cancel() } override fun onAbandoned() { // If any work was launched by the constructor as part of remembering the object, // you must cancel that work in this callback. For work done as part of the construction // during retain, this code should will appear in onUnused. job.cancel() } }
เก็บการติดตั้งใช้งาน RememberObserver และ RetainObserver ไว้เป็นส่วนตัว
เมื่อเขียน API สาธารณะ โปรดใช้ความระมัดระวังเมื่อขยาย RememberObserver และ
RetainObserver ในการสร้างคลาสที่ส่งคืนแบบสาธารณะ ผู้ใช้อาจไม่
จดจำออบเจ็กต์ของคุณเมื่อคุณคาดหวัง หรืออาจจดจำออบเจ็กต์ของคุณใน
ลักษณะที่แตกต่างจากที่คุณตั้งใจไว้ ด้วยเหตุนี้ เราจึงขอแนะนำว่าอย่าแสดงตัวสร้างหรือฟังก์ชัน Factory สำหรับออบเจ็กต์ที่ใช้ RememberObserver
หรือ RetainObserver โปรดทราบว่าการดำเนินการนี้ขึ้นอยู่กับประเภทรันไทม์ของคลาส
ไม่ใช่ประเภทที่ประกาศไว้ โดยการจดจำออบเจ็กต์ที่ใช้ RememberObserver
หรือ RetainObserver แต่มีการแคสต์เป็น Any จะยังคงทำให้ออบเจ็กต์ได้รับ
การเรียกกลับ
ไม่แนะนำ
abstract class MyManager
// Not Recommended: Exposing a public constructor (even implicitly) for an object implementing
// RememberObserver can cause unexpected invocations if it is remembered multiple times.
class MyComposeManager : MyManager(), RememberObserver { ... }
// Not Recommended: The return type may be an implementation of RememberObserver and should be
// remembered explicitly.
fun createFoo(): MyManager = MyComposeManager()
แนะนำ
abstract class MyManager class MyComposeManager : MyManager() { // Callers that construct this object must manually call initialize and teardown fun initialize() { /*...*/ } fun teardown() { /*...*/ } } @Composable fun rememberMyManager(): MyManager { // Protect the RememberObserver implementation by never exposing it outside the library return remember { object : RememberObserver { val manager = MyComposeManager() override fun onRemembered() = manager.initialize() override fun onForgotten() = manager.teardown() override fun onAbandoned() { /* Nothing to do if manager hasn't initialized */ } } }.manager }
สิ่งที่ควรพิจารณาเมื่อจดจำออบเจ็กต์
นอกเหนือจากคำแนะนำก่อนหน้านี้เกี่ยวกับ RememberObserver และ
RetainObserver เรายังขอแนะนำให้คุณระมัดระวังและหลีกเลี่ยงการจดจำออบเจ็กต์ซ้ำโดยไม่ตั้งใจทั้งในด้านประสิทธิภาพและความถูกต้อง ส่วนต่อไปนี้จะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับสถานการณ์การจดจำซ้ำที่เฉพาะเจาะจงและเหตุผลที่ควรหลีกเลี่ยง
จดจำออบเจ็กต์เพียงครั้งเดียว
การจดจำออบเจ็กต์อีกครั้งอาจเป็นอันตราย ในกรณีที่ดีที่สุด คุณอาจ
สิ้นเปลืองทรัพยากรในการจดจำค่าที่จดจำไปแล้ว แต่หากออบเจ็กต์ใช้ RememberObserver และระบบจดจำออบเจ็กต์นั้น 2 ครั้งโดยไม่คาดคิด ออบเจ็กต์จะได้รับการเรียกกลับมากกว่าที่คาดไว้ ซึ่งอาจทำให้เกิดปัญหาได้เนื่องจากตรรกะของ onRemembered และ onForgotten จะทำงาน 2 ครั้ง และการติดตั้งใช้งาน RememberObserver ส่วนใหญ่ไม่รองรับกรณีนี้ หากมีการเรียกใช้ฟังก์ชัน remember ครั้งที่ 2 ในขอบเขตอื่นที่มีอายุการใช้งานต่างจาก remember เดิม การใช้งาน RememberObserver.onForgotten หลายอย่างจะทิ้งออบเจ็กต์ก่อนที่จะใช้งานเสร็จ
val first: RememberObserver = rememberFoo()
// Not Recommended: Re-remembered `Foo` now gets double callbacks
val second = remember { first }
คำแนะนำนี้ไม่มีผลกับออบเจ็กต์ที่ระบบจดจำอีกครั้งแบบทรานซิทีฟ (เช่น ออบเจ็กต์ที่จดจำซึ่งใช้ออบเจ็กต์ที่จดจำอีกรายการ) โดยปกติแล้ว จะมีการเขียนโค้ดที่มีลักษณะดังนี้ ซึ่งทำได้เนื่องจากระบบจะจดจำออบเจ็กต์อื่น และจึงไม่ทำให้เกิดการเรียกกลับซ้ำโดยไม่คาดคิด
val foo: Foo = rememberFoo() // Acceptable: val bar: Bar = remember { Bar(foo) } // Recommended key usage: val barWithKey: Bar = remember(foo) { Bar(foo) }
ถือว่าระบบจดจำอาร์กิวเมนต์ของฟังก์ชันแล้ว
ฟังก์ชันไม่ควรจดจำพารามิเตอร์ใดๆ ทั้งนี้เนื่องจากอาจทำให้เกิด
การเรียกใช้ Callback ซ้ำสำหรับ RememberObserver และเนื่องจากไม่จำเป็น หากต้องจดจำพารามิเตอร์อินพุต ให้ตรวจสอบว่าพารามิเตอร์นั้นไม่ได้ใช้ RememberObserver หรือกำหนดให้ผู้โทรจดจำอาร์กิวเมนต์ของตน
@Composable
fun MyComposable(
parameter: Foo
) {
// Not Recommended: Input should be remembered by the caller.
val rememberedParameter = remember { parameter }
}
การดำเนินการนี้ไม่มีผลกับออบเจ็กต์ที่จดจำแบบทรานซิทีฟ เมื่อจดจำออบเจ็กต์ที่ได้จากอาร์กิวเมนต์ของฟังก์ชัน ให้พิจารณาระบุออบเจ็กต์เป็นหนึ่งในคีย์ต่อไปนี้remember:
@Composable fun MyComposable( parameter: Foo ) { // Acceptable: val derivedValue = remember { Bar(parameter) } // Also Acceptable: val derivedValueWithKey = remember(parameter) { Bar(parameter) } }
อย่าเก็บออบเจ็กต์ที่จำได้อยู่แล้ว
เช่นเดียวกับการจดจำออบเจ็กต์อีกครั้ง คุณควรหลีกเลี่ยงการเก็บออบเจ็กต์ที่
จดจำไว้เพื่อพยายามยืดอายุการใช้งาน นี่เป็นผลสืบเนื่องจากคำแนะนำในอายุการใช้งานของสถานะ: retain ไม่ควรใช้กับออบเจ็กต์ที่มีอายุการใช้งานไม่ตรงกับข้อเสนอการคงอายุการใช้งาน เนื่องจากออบเจ็กต์ remembered
มีอายุการใช้งานสั้นกว่าออบเจ็กต์ retained คุณจึงไม่ควรเก็บออบเจ็กต์ที่จดจำไว้ แต่ควรเก็บออบเจ็กต์ไว้ที่เว็บไซต์ต้นทาง
แทนที่จะจดจำ