บันทึกสถานะ UI (มุมมอง)

แนวคิดและการติดตั้งใช้งาน Jetpack Compose

คู่มือนี้จะกล่าวถึงความคาดหวังของผู้ใช้เกี่ยวกับสถานะ UI และตัวเลือกที่มี สำหรับการคงสถานะ

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

หากต้องการลดช่องว่างระหว่างความคาดหวังของผู้ใช้กับลักษณะการทำงานของระบบ ให้ใช้ วิธีการต่อไปนี้ร่วมกัน

  • ออบเจ็กต์ ViewModel
  • สถานะอินสแตนซ์ที่บันทึกไว้ในบริบทต่อไปนี้
  • พื้นที่เก็บข้อมูลในเครื่องเพื่อคงสถานะ UI ไว้ในระหว่างการเปลี่ยนแอปและกิจกรรม

โซลูชันที่เหมาะสมที่สุดขึ้นอยู่กับความซับซ้อนของข้อมูล UI กรณีการใช้งานของแอป และการสร้างสมดุลระหว่างความเร็วในการเข้าถึงข้อมูลและการใช้หน่วยความจำ

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

ความคาดหวังของผู้ใช้และลักษณะการทำงานของระบบ

ผู้ใช้คาดหวังว่าสถานะกิจกรรมจะถูกล้างหรือคงไว้ตามการดำเนินการที่ผู้ใช้ทำ ในบางกรณี ระบบจะดำเนินการโดยอัตโนมัติตามที่ผู้ใช้คาดหวัง ในกรณีอื่นๆ ระบบจะทำในทางตรงกันข้าม

การปิดสถานะ UI ที่ผู้ใช้เริ่ม

ผู้ใช้คาดหวังว่าเมื่อเริ่มกิจกรรม สถานะ UI ชั่วคราวของ กิจกรรมนั้นจะยังคงเหมือนเดิมจนกว่าผู้ใช้จะปิดกิจกรรมโดยสมบูรณ์ ผู้ใช้สามารถปิดกิจกรรมโดยสมบูรณ์ได้โดยทำดังนี้

  • ปัดกิจกรรมออกจากหน้าจอภาพรวม (ล่าสุด)
  • การปิดหรือบังคับปิดแอปจากหน้าจอการตั้งค่า
  • รีบูตอุปกรณ์
  • การดำเนินการ "เสร็จสิ้น" บางอย่าง (ซึ่งได้รับการสนับสนุนจาก Activity.finish())

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

กฎนี้มีข้อยกเว้นบางประการเกี่ยวกับการปิดอย่างสมบูรณ์ เช่น ผู้ใช้อาจคาดหวังให้เบราว์เซอร์นำไปยังหน้าเว็บที่กำลังดูอยู่ก่อนที่จะออกจากเบราว์เซอร์โดยใช้ปุ่มย้อนกลับ

การปิดสถานะ UI ที่ระบบเริ่มดำเนินการ

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

โปรดทราบว่าคุณสามารถลบล้างลักษณะการทำงานเริ่มต้นสำหรับการเปลี่ยนแปลงการกำหนดค่าได้ (แม้ว่าเราจะไม่แนะนำ) ดูรายละเอียดเพิ่มเติมได้ที่การจัดการการเปลี่ยนแปลงการกำหนดค่า

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

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

ตัวเลือกในการเก็บรักษาสถานะ UI

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

ตัวเลือกแต่ละรายการสำหรับการรักษาสถานะ UI จะแตกต่างกันไปตามมิติข้อมูลต่อไปนี้ ซึ่งส่งผลต่อประสบการณ์ของผู้ใช้

ViewModel

สถานะอินสแตนซ์ที่บันทึกไว้

พื้นที่เก็บข้อมูลถาวร

ตำแหน่งของพื้นที่เก็บข้อมูล

ในหน่วยความจำ

ในหน่วยความจำ

ในดิสก์หรือเครือข่าย

ยังคงอยู่เมื่อมีการเปลี่ยนแปลงการกำหนดค่า

ใช่

ได้

ใช่

รอดพ้นจากการสิ้นสุดการประมวลผลที่ระบบเริ่มต้น

ไม่

ใช่

ใช่

ยังคงอยู่เมื่อผู้ใช้ปิด/เสร็จสิ้นกิจกรรม

ไม่

ไม่ได้

ใช่

ข้อจำกัดของข้อมูล

ออบเจ็กต์ที่ซับซ้อนใช้ได้ แต่พื้นที่ถูกจำกัดตามหน่วยความจำที่ใช้ได้

สำหรับประเภทดั้งเดิมและออบเจ็กต์ขนาดเล็กที่เรียบง่ายเท่านั้น เช่น String

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

เวลาในการอ่าน/เขียน

รวดเร็ว (เข้าถึงหน่วยความจำเท่านั้น)

ช้า (ต้องมีการทำให้เป็นอนุกรม/ยกเลิกการทำให้เป็นอนุกรม)

ช้า (ต้องเข้าถึงดิสก์หรือทำธุรกรรมในเครือข่าย)

ใช้ ViewModel เพื่อจัดการการเปลี่ยนแปลงการกำหนดค่า

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

ViewModel จะเก็บข้อมูลไว้ในหน่วยความจำ ซึ่งหมายความว่าการดึงข้อมูลจะถูกกว่า การดึงข้อมูลจากดิสก์หรือเครือข่าย ViewModel เชื่อมโยงกับกิจกรรม (หรือเจ้าของวงจรอื่นๆ) โดยจะอยู่ในหน่วยความจำระหว่างการเปลี่ยนแปลงการกำหนดค่า และระบบจะเชื่อมโยง ViewModel กับอินสแตนซ์กิจกรรมใหม่ โดยอัตโนมัติซึ่งเป็นผลมาจากการเปลี่ยนแปลงการกำหนดค่า

ระบบจะทำลาย ViewModel โดยอัตโนมัติเมื่อผู้ใช้ย้อนกลับจาก กิจกรรมหรือ Fragment หรือหากคุณเรียกใช้ finish() ซึ่งหมายความว่าระบบจะล้างสถานะ ตามที่ผู้ใช้คาดหวังในสถานการณ์เหล่านี้

ViewModel จะถูกทำลายระหว่างการสิ้นสุดกระบวนการที่ระบบเริ่มต้น ซึ่งต่างจากสถานะอินสแตนซ์ที่บันทึกไว้ หากต้องการโหลดข้อมูลซ้ำหลังจากที่ระบบปิดกระบวนการที่เริ่มต้นใน ViewModel ให้ใช้ SavedStateHandle API หรือหากข้อมูล เกี่ยวข้องกับ UI และไม่จำเป็นต้องเก็บไว้ใน ViewModel ให้ใช้ onSaveInstanceState() หากข้อมูลเป็นข้อมูลแอปพลิเคชัน การบันทึกลงในดิสก์อาจเป็นตัวเลือกที่ดีกว่า

หากคุณมีโซลูชันในหน่วยความจำอยู่แล้วสำหรับจัดเก็บสถานะ UI เมื่อมีการเปลี่ยนแปลงการกำหนดค่า คุณอาจไม่จำเป็นต้องใช้ ViewModel

ใช้สถานะอินสแตนซ์ที่บันทึกไว้เป็นข้อมูลสำรองเพื่อจัดการการสิ้นสุดการประมวลผลที่ระบบเริ่มต้น

การเรียกกลับ onSaveInstanceState() ในระบบ View และ SavedStateHandle ใน ViewModel จะจัดเก็บข้อมูลที่จำเป็นในการโหลดสถานะของ ตัวควบคุม UI เช่น กิจกรรมหรือ Fragment ซ้ำ หากระบบทำลายและ สร้างตัวควบคุมนั้นขึ้นมาใหม่ในภายหลัง ดูวิธีใช้สถานะอินสแตนซ์ที่บันทึกไว้ โดยใช้ onSaveInstanceState ได้ที่การบันทึกและกู้คืนสถานะกิจกรรมใน คู่มือวงจรของกิจกรรม

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

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

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

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

ในทั้ง 2 กรณีนี้ คุณยังคงควรใช้ ViewModel เพื่อหลีกเลี่ยง การสิ้นเปลืองรอบการโหลดข้อมูลจากฐานข้อมูลซ้ำๆ ในระหว่างการเปลี่ยนแปลงการกำหนดค่า

ในกรณีที่ข้อมูล UI ที่จะเก็บรักษานั้นเรียบง่ายและมีขนาดเล็ก คุณอาจใช้ API สถานะอินสแตนซ์ที่บันทึกไว้เพียงอย่างเดียวเพื่อเก็บรักษาข้อมูลสถานะ

เชื่อมต่อกับสถานะที่บันทึกไว้โดยใช้ SavedStateRegistry

ตั้งแต่ Fragment 1.1.0 หรือทรัพยากร Dependency แบบทรานซิทีฟ Activity 1.0.0 เป็นต้นไป ตัวควบคุม UI เช่น Activity หรือ Fragment จะใช้ SavedStateRegistryOwner และมี SavedStateRegistry ที่เชื่อมโยงกับตัวควบคุมนั้น SavedStateRegistry ช่วยให้คอมโพเนนต์เชื่อมต่อกับ สถานะที่บันทึกไว้ของตัวควบคุม UI เพื่อใช้หรือมีส่วนร่วมในสถานะดังกล่าว เช่น Saved State module for ViewModel ใช้ SavedStateRegistry เพื่อสร้าง SavedStateHandle และส่งไปยังออบเจ็กต์ ViewModel คุณสามารถดึงข้อมูล SavedStateRegistry จากภายในตัวควบคุม UI ได้โดยการเรียกใช้ getSavedStateRegistry

คอมโพเนนต์ที่ช่วยให้บันทึกสถานะได้ต้องใช้ SavedStateRegistry.SavedStateProvider ซึ่งกำหนดเมธอดเดียวที่ชื่อ saveState เมธอด saveState() ช่วยให้คอมโพเนนต์ของคุณ ส่งคืน Bundle ที่มีสถานะใดๆ ที่ควรบันทึกจากคอมโพเนนต์นั้น SavedStateRegistry จะเรียกใช้เมธอดนี้ในระหว่างเฟสสถานะการบันทึกของวงจรตัวควบคุม UI

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String QUERY = "query";
    private String query = null;
    ...

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }
}

หากต้องการลงทะเบียน SavedStateProvider ให้โทรหา registerSavedStateProvider() ใน SavedStateRegistry โดยส่งคีย์เพื่อเชื่อมโยงกับข้อมูลของผู้ให้บริการ รวมถึงผู้ให้บริการด้วย ระบบจะเรียกข้อมูลที่บันทึกไว้ก่อนหน้านี้สำหรับผู้ให้บริการได้จากสถานะที่บันทึกไว้โดยการเรียก consumeRestoredStateForKey() ใน SavedStateRegistry โดยส่งคีย์ที่เชื่อมโยงกับข้อมูลของผู้ให้บริการ

ภายใน Activity หรือ Fragment คุณสามารถลงทะเบียน SavedStateProvider ใน onCreate() หลังจากโทรหา super.onCreate() หรือคุณจะตั้งค่า LifecycleObserver ใน SavedStateRegistryOwner ซึ่งใช้ LifecycleOwner และลงทะเบียน SavedStateProvider เมื่อ เกิดเหตุการณ์ ON_CREATE การใช้ LifecycleObserver จะช่วยให้คุณแยกการลงทะเบียนและการเรียกข้อมูลสถานะที่บันทึกไว้ก่อนหน้านี้ออกจาก SavedStateRegistryOwner เองได้

Kotlin

class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val PROVIDER = "search_manager"
        private const val QUERY = "query"
    }

    private val query: String? = null

    init {
        // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE
        registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_CREATE) {
                val registry = registryOwner.savedStateRegistry

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this)

                // Get the previously saved state and restore it
                val state = registry.consumeRestoredStateForKey(PROVIDER)

                // Apply the previously saved state
                query = state?.getString(QUERY)
            }
        }
    }

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }

    ...
}

class SearchFragment : Fragment() {
    private var searchManager = SearchManager(this)
    ...
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String PROVIDER = "search_manager";
    private static String QUERY = "query";
    private String query = null;

    public SearchManager(SavedStateRegistryOwner registryOwner) {
        registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
            if (event == Lifecycle.Event.ON_CREATE) {
                SavedStateRegistry registry = registryOwner.getSavedStateRegistry();

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this);

                // Get the previously saved state and restore it
                Bundle state = registry.consumeRestoredStateForKey(PROVIDER);

                // Apply the previously saved state
                if (state != null) {
                    query = state.getString(QUERY);
                }
            }
        });
    }

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }

    ...
}

class SearchFragment extends Fragment {
    private SearchManager searchManager = new SearchManager(this);
    ...
}

ใช้การคงอยู่ของข้อมูลในเครื่องเพื่อจัดการการสิ้นสุดการประมวลผลสำหรับข้อมูลที่ซับซ้อนหรือมีขนาดใหญ่

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

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

การจัดการสถานะ UI: แบ่งและพิชิต

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

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

ตัวอย่างเช่น ลองพิจารณากิจกรรมที่ช่วยให้คุณค้นหาเพลงในคลังได้ วิธีจัดการเหตุการณ์ต่างๆ มีดังนี้

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

เมื่อผู้ใช้ค้นหาเพลง ข้อมูลเพลงที่ซับซ้อนใดก็ตามที่คุณโหลดจากฐานข้อมูลควรจัดเก็บไว้ในออบเจ็กต์ ViewModel ทันที ซึ่งเป็นส่วนหนึ่งของสถานะ UI ของหน้าจอ

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

คืนค่าสถานะที่ซับซ้อน: ประกอบชิ้นส่วนใหม่

เมื่อถึงเวลาที่ผู้ใช้กลับมาทำกิจกรรมต่อ จะมีสถานการณ์ที่เป็นไปได้ 2 อย่าง ในการสร้างกิจกรรมขึ้นใหม่ ดังนี้

  • ระบบจะสร้างกิจกรรมขึ้นใหม่หลังจากที่ระบบหยุดกิจกรรมไปแล้ว ระบบจะบันทึกการค้นหาไว้ในชุดสถานะอินสแตนซ์ที่บันทึกไว้ และ UI ควรส่งการค้นหาไปยัง ViewModel หากไม่ได้ใช้ SavedStateHandle ViewModel เห็นว่าไม่มีผลการค้นหาที่แคชไว้ จึงมอบหมายให้โหลดผลการค้นหาโดยใช้คำค้นหาที่ระบุ
  • ระบบจะสร้างกิจกรรมขึ้นใหม่หลังจากการเปลี่ยนแปลงการกำหนดค่า เนื่องจากไม่ได้ทำลายอินสแตนซ์ ViewModel ViewModel จึงมีข้อมูลทั้งหมดที่แคชไว้ในหน่วยความจำและไม่จำเป็นต้องค้นหาฐานข้อมูลอีกครั้ง

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการบันทึกสถานะ UI ได้ที่แหล่งข้อมูลต่อไปนี้

บล็อก

Codelab