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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

การยกเลิกสถานะ UI ที่ระบบเป็นผู้เริ่ม

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

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

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

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

ตัวเลือกในการเก็บรักษาสถานะ 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 ระบบจะส่งบันเดิลส่วนเพิ่มเติมไปยังกิจกรรมทั้งเมื่อมีการเปลี่ยนแปลงการกำหนดค่าและเมื่อระบบกู้คืนกิจกรรม

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

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

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

ตั้งแต่ Fragment 1.1.0 หรือ Activity 1.0.0 ซึ่งเป็นทรัพยากร Dependency แบบถ่ายทอด ตัวควบคุม UI เช่น Activity หรือ Fragment จะติดตั้งใช้งาน SavedStateRegistryOwner และมี SavedStateRegistry ที่ เชื่อมโยงกับตัวควบคุมนั้น SavedStateRegistry ช่วยให้คอมโพเนนต์เชื่อมต่อกับสถานะที่บันทึกไว้ของตัวควบคุม UI เพื่อใช้หรือมีส่วนร่วมในสถานะดังกล่าว ตัวอย่างเช่น โมดูลสถานะที่บันทึกไว้สำหรับ 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 สถานะอินสแตนซ์ที่บันทึกไว้ ข้อความค้นหาควรจัดเก็บไว้ในสถานะอินสแตนซ์ที่บันทึกไว้ในกรณีที่กระบวนการสร้างขึ้นมาใหม่ เนื่องจากข้อมูลนี้จำเป็นในการโหลดข้อมูลแอปพลิเคชันที่เก็บไว้ใน API นี้ ให้จัดเก็บข้อความค้นหาไว้ใน SavedStateHandle ของ ViewModel ข้อมูลนี้เป็นข้อมูลทั้งหมดที่คุณต้องใช้ในการโหลดข้อมูลและทำให้ UI กลับสู่สถานะปัจจุบัน

กู้คืนสถานะที่ซับซ้อน: ประกอบชิ้นส่วนต่างๆ เข้าด้วยกัน

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

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

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

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

บล็อก

Codelab