وحدة Saved State الخاصة بـ ViewModel (طرق العرض) جزء من Android Jetpack
المفاهيم والتنفيذ في Jetpack Compose
كما هو موضّح في حفظ حالات واجهة المستخدم، يمكن لكائنات ViewModel التعامل مع تغييرات الإعدادات، لذا لا داعي للقلق بشأن الحالة عند تدوير الشاشة أو في حالات أخرى. ومع ذلك، إذا كنت بحاجة إلى التعامل مع إيقاف العملية الذي يبدأه النظام، يمكنك استخدام واجهة برمجة التطبيقات SavedStateHandle كنسخة احتياطية.
عادةً ما يتم تخزين حالة واجهة المستخدم أو الإشارة إليها في عناصر ViewModel وليس في الأنشطة، لذا يتطلّب استخدام onSaveInstanceState() بعض الرموز النموذجية التي يمكن أن تتولّى وحدة الحالة المحفوظة معالجتها.
عند استخدام هذه الوحدة، تتلقّى عناصر ViewModel عنصر SavedStateHandle
من خلال الدالة الإنشائية. هذا العنصر هو خريطة مفتاح-قيمة تتيح لك كتابة العناصر واستردادها من الحالة المحفوظة. تظل هذه القيم متاحة بعد أن يوقف النظام العملية، وتبقى متاحة من خلال الكائن نفسه.
يتم ربط الحالة المحفوظة بمجموعة المهام. إذا اختفت حزمة المهام، سيختفي أيضًا الحالة المحفوظة. يمكن أن يحدث ذلك عند إيقاف تطبيق بالقوة أو إزالته من قائمة التطبيقات الحديثة أو إعادة تشغيل الجهاز. في هذه الحالات، يختفي حزمة المهام ولا يمكنك استعادة المعلومات في الحالة المحفوظة. في سيناريوهات إغلاق واجهة المستخدم من قِبل المستخدم، لا تتم استعادة الحالة المحفوظة. في سيناريوهات بدء التشغيل من قِبل النظام، يكون ذلك ممكنًا.
الإعداد
بدءًا من Fragment 1.2.0 أو الاعتمادية المتعدية 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 هي عبارة عن خريطة مفتاح-قيمة تتيح لك كتابة البيانات واسترجاعها من الحالة المحفوظة باستخدام الطريقتَين set() وget().
باستخدام SavedStateHandle، يتم الاحتفاظ بقيمة طلب البحث عند إيقاف العملية نهائيًا، ما يضمن أن يرى المستخدم المجموعة نفسها من البيانات المفلترة قبل وبعد إعادة الإنشاء بدون الحاجة إلى أن يحفظ النشاط أو الجزء القيمة يدويًا ويعيدها ويُعيد توجيهها إلى ViewModel.
تتضمّن SavedStateHandle أيضًا طرقًا أخرى قد تتوقّعها عند التفاعل مع خريطة قيم مفتاحية:
contains(String key): للتحقّق مما إذا كانت هناك قيمة للمفتاح المحدّد.remove(String key): لإزالة قيمة المفتاح المحدّدkeys(): تعرض جميع المفاتيح المتوفّرة فيSavedStateHandle.
بالإضافة إلى ذلك، يمكنك استرداد القيم من SavedStateHandle باستخدام حاوية بيانات قابلة للمراقبة. في ما يلي قائمة بالأنواع المتوافقة:
LiveData
استرداد القيم من SavedStateHandle التي تم تضمينها في LiveData
قابلة للمراقبة باستخدام 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.
بدءًا من الإصدار 2.3.0-alpha03 من Lifecycle، تتيح لك SavedStateHandle حفظ أي عنصر من خلال توفير منطقك الخاص لحفظ العنصر واستعادته كـ Bundle باستخدام طريقة setSavedStateProvider().
SavedStateRegistry.SavedStateProvider هي واجهة تحدّد طريقة saveState() واحدة تعرض Bundle يتضمّن الحالة التي تريد حفظها. عندما يكون SavedStateHandle جاهزًا لحفظ حالته، يستدعي saveState() لاسترداد Bundle من SavedStateProvider ويحفظ Bundle للمفتاح المرتبط.
لنفترض أنّ هناك تطبيقًا يطلب صورة من تطبيق الكاميرا من خلال الغرض 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 غير مفعّلة
- حفظ حالات واجهة المستخدم
- العمل مع عناصر البيانات القابلة للتتبّع
- إنشاء ViewModels مع التبعيات