โมดูลสถานะที่บันทึกไว้สำหรับ ViewModel (มุมมอง) ส่วนหนึ่งของ Android Jetpack
แนวคิดและการใช้งาน Jetpack Compose
ดังที่กล่าวไว้ในการบันทึกสถานะ UI ออบเจ็กต์ ViewModel สามารถจัดการ
การเปลี่ยนแปลงการกำหนดค่าได้ คุณจึงไม่ต้องกังวลเกี่ยวกับสถานะในการหมุน
หรือกรณีอื่นๆ อย่างไรก็ตาม หากคุณต้องจัดการกระบวนการที่ระบบเริ่มต้น
การสิ้นสุด คุณอาจต้องใช้ SavedStateHandle API เป็นข้อมูลสำรอง
โดยปกติแล้ว สถานะ UI จะจัดเก็บหรืออ้างอิงในออบเจ็กต์ ViewModel และไม่ใช่
กิจกรรม ดังนั้นการใช้ onSaveInstanceState() จึงต้องมีบอยเลอร์เพลตบางอย่างที่โมดูลสถานะที่บันทึกไว้จะจัดการให้คุณได้
เมื่อใช้โมดูลนี้ ออบเจ็กต์ ViewModel จะได้รับออบเจ็กต์ SavedStateHandle ผ่านตัวสร้าง ออบเจ็กต์นี้คือแผนที่คีย์-ค่าที่ช่วยให้คุณ
เขียนและเรียกออบเจ็กต์ไปยังและจากสถานะที่บันทึกไว้ได้ ค่าเหล่านี้จะยังคงอยู่
หลังจากที่ระบบปิดกระบวนการและยังคงใช้ได้ผ่านออบเจ็กต์เดียวกัน
สถานะที่บันทึกไว้จะเชื่อมโยงกับสแต็กงาน หากสแต็กงานหายไป สถานะที่บันทึกไว้ก็จะหายไปด้วย ซึ่งอาจเกิดขึ้นเมื่อบังคับหยุดแอป นำแอปออกจากเมนู "ล่าสุด" หรือรีบูตอุปกรณ์ ในกรณีเช่นนี้ งาน สแต็กจะหายไปและคุณจะกู้คืนข้อมูลในสถานะที่บันทึกไว้ไม่ได้ ในสถานการณ์การปิดสถานะ UI ที่ผู้ใช้เริ่ม ระบบจะไม่คืนค่าสถานะที่บันทึกไว้ ในสถานการณ์ที่ระบบเริ่มต้น
ตั้งค่า
ตั้งแต่ Fragment 1.2.0 หรือทรัพยากร Dependency แบบทรานซิทีฟ
Activity 1.1.0 เป็นต้นไป คุณจะยอมรับ SavedStateHandle เป็นอาร์กิวเมนต์ของเครื่องมือสร้าง
สำหรับ ViewModel ได้
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
จากนั้นคุณจะเรียกข้อมูลอินสแตนซ์ของ ViewModel ได้โดยไม่ต้องกำหนดค่าเพิ่มเติม
ViewModelเริ่มต้นจากโรงงานจะให้SavedStateHandleที่เหมาะสมแก่ViewModel
Kotlin
class MainFragment : Fragment() { val vm: SavedStateViewModel by viewModels() ... }
Java
class MainFragment extends Fragment { private SavedStateViewModel vm; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { vm = new ViewModelProvider(this).get(SavedStateViewModel.class); ... } ... }
เมื่อระบุอินสแตนซ์ ViewModelProvider.Factory ที่กำหนดเอง คุณจะ
เปิดใช้ SavedStateHandle ได้โดยการขยาย
AbstractSavedStateViewModelFactory
การทำงานกับ SavedStateHandle
SavedStateHandle class คือแผนที่คีย์-ค่าที่ช่วยให้คุณเขียนและ
เรียกข้อมูลไปยังและจากสถานะที่บันทึกไว้ผ่านเมธอด set() และ
get()
การใช้ SavedStateHandle จะช่วยให้ค่าการค้นหายังคงอยู่เมื่อการประมวลผลสิ้นสุดลง เพื่อให้มั่นใจว่าผู้ใช้จะเห็นชุดข้อมูลที่กรองแล้วชุดเดียวกันทั้งก่อนและหลัง การสร้างใหม่โดยไม่ต้องให้กิจกรรมหรือ Fragment บันทึก กู้คืน และส่งต่อค่านั้นกลับไปยัง ViewModel ด้วยตนเอง
SavedStateHandle ยังมีเมธอดอื่นๆ ที่คุณอาจคาดหวังเมื่อโต้ตอบ
กับแมปคีย์-ค่าด้วย
contains(String key)- ตรวจสอบว่ามีค่าสำหรับคีย์ที่ระบุหรือไม่remove(String key)- นำค่าสำหรับคีย์ที่ระบุออกkeys()- แสดงคีย์ทั้งหมดที่อยู่ในSavedStateHandle
นอกจากนี้ คุณยังดึงค่าจาก SavedStateHandle ได้โดยใช้ที่เก็บข้อมูลที่ได้รับอนุญาตให้สังเกตพฤติกรรมผู้ใช้ได้ รายการประเภทที่รองรับมีดังนี้
LiveData
ดึงค่าจาก SavedStateHandle ที่อยู่ใน LiveData
observable โดยใช้ getLiveData() เมื่อมีการอัปเดตค่าของคีย์ LiveData จะได้รับค่าใหม่ โดยส่วนใหญ่แล้ว ค่าจะตั้งขึ้นเนื่องจากการโต้ตอบของผู้ใช้ เช่น การป้อนคำค้นหาเพื่อกรองรายการข้อมูล จากนั้นจะใช้ค่าที่อัปเดตนี้เพื่อเปลี่ยนรูปแบบ LiveData ได้
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { val filteredData: LiveData<List<String>> = savedStateHandle.getLiveData<String>("query").switchMap { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle savedStateHandle; public LiveData<List<String>> filteredData; public SavedStateViewModel(SavedStateHandle savedStateHandle) { this.savedStateHandle = savedStateHandle; LiveData<String> queryLiveData = savedStateHandle.getLiveData("query"); filteredData = Transformations.switchMap(queryLiveData, query -> { return repository.getFilteredData(query); }); } public void setQuery(String query) { savedStateHandle.set("query", query); } }
ประเภทที่รองรับ
ระบบจะบันทึกและกู้คืนข้อมูลที่เก็บไว้ภายใน SavedStateHandle เป็น Bundle
พร้อมกับsavedInstanceStateที่เหลือสำหรับกิจกรรมหรือ
ส่วน
การบันทึกคลาสที่ส่งผ่านไม่ได้
หากคลาสไม่ได้ใช้ Parcelable หรือ Serializable และแก้ไขไม่ได้เพื่อใช้หนึ่งในอินเทอร์เฟซเหล่านั้น ก็จะบันทึกอินสแตนซ์ของคลาสดังกล่าวลงใน SavedStateHandle โดยตรงไม่ได้
ตั้งแต่ Lifecycle 2.3.0-alpha03 เป็นต้นไป SavedStateHandle จะช่วยให้คุณบันทึก
ออบเจ็กต์ใดก็ได้โดยการระบุตรรกะของคุณเองสำหรับการบันทึกและกู้คืนออบเจ็กต์เป็น Bundle โดยใช้วิธี setSavedStateProvider()
SavedStateRegistry.SavedStateProvider คืออินเทอร์เฟซที่กำหนดเมธอด saveState() เดียวที่ส่งคืน Bundle ซึ่งมีสถานะที่คุณต้องการบันทึก
เมื่อ SavedStateHandle พร้อมที่จะบันทึกสถานะของตัวเองแล้ว ก็จะเรียกใช้
saveState() เพื่อดึงข้อมูล Bundle จาก SavedStateProvider และบันทึก
Bundle สำหรับคีย์ที่เชื่อมโยง
ลองพิจารณาตัวอย่างแอปที่ขอรูปภาพจากแอปกล้องถ่ายรูปผ่าน
Intent ACTION_IMAGE_CAPTURE โดยส่งไฟล์ชั่วคราวสำหรับตำแหน่งที่
กล้องควรจัดเก็บรูปภาพ TempFileViewModel จะห่อหุ้ม
ตรรกะสำหรับการสร้างไฟล์ชั่วคราวดังกล่าว
Kotlin
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel() { } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } }
TempFileViewModel สามารถใช้ SavedStateHandle เพื่อคงข้อมูลไว้ได้ เพื่อให้มั่นใจว่าไฟล์ชั่วคราวจะไม่สูญหายหากกระบวนการของกิจกรรมถูกปิด
และกู้คืนในภายหลัง หากต้องการอนุญาตให้ TempFileViewModel บันทึกข้อมูล ให้ใช้
SavedStateProvider และตั้งค่าเป็นผู้ให้บริการใน SavedStateHandle ของ
ViewModel ดังนี้
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } } }
หากต้องการกู้คืนข้อมูล File เมื่อผู้ใช้กลับมา ให้ดึงข้อมูล temp_file
Bundle จาก SavedStateHandle ซึ่งเป็น Bundle เดียวกับที่ saveTempFile() ระบุไว้ซึ่งมีเส้นทางที่แน่นอน จากนั้นจะใช้เส้นทางแบบสัมบูรณ์เพื่อสร้างอินสแตนซ์ File ใหม่ได้
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) private fun Bundle.restoreTempFile() = if (containsKey("path")) { File(getString("path")) } else { null } class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { val tempFileBundle = savedStateHandle.get<Bundle>("temp_file") if (tempFileBundle != null) { tempFile = tempFileBundle.restoreTempFile() } savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { Bundle tempFileBundle = savedStateHandle.get("temp_file"); if (tempFileBundle != null) { tempFile = TempFileSavedStateProvider.restoreTempFile(tempFileBundle); } savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } @Nullable private static File restoreTempFile(Bundle bundle) { if (bundle.containsKey("path") { return File(bundle.getString("path")); } return null; } } }
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- บันทึกสถานะ UI
- ทำงานกับออบเจ็กต์ข้อมูลที่ได้รับอนุญาตให้สังเกตพฤติกรรมผู้ใช้ได้
- สร้าง ViewModel ที่มี Dependency