ماژول حالت ذخیره شده برای ViewModel بخشی از Android Jetpack .
همانطور که در Saving UI States ذکر شد، اشیاء ViewModel
می توانند تغییرات پیکربندی را انجام دهند، بنابراین نیازی نیست نگران وضعیت در چرخش یا موارد دیگر باشید. با این حال، اگر نیاز به مدیریت مرگ فرآیند آغاز شده توسط سیستم دارید، ممکن است بخواهید از SavedStateHandle
API به عنوان پشتیبان استفاده کنید.
حالت رابط کاربری معمولاً در اشیاء ViewModel
ذخیره یا ارجاع میشود و نه در فعالیتها، بنابراین استفاده از onSaveInstanceState()
یا rememberSaveable
به مقداری دیگ بخار نیاز دارد که ماژول حالت ذخیرهشده بتواند برای شما مدیریت کند.
هنگام استفاده از این ماژول، اشیاء ViewModel
یک شی SavedStateHandle
را از طریق سازنده خود دریافت می کنند. این شی یک نقشه کلید-مقدار است که به شما امکان می دهد اشیاء را به و از حالت ذخیره شده بنویسید و بازیابی کنید. این مقادیر پس از کشته شدن فرآیند توسط سیستم باقی می مانند و از طریق همان شی در دسترس باقی می مانند.
حالت ذخیره شده به پشته وظیفه شما گره خورده است. اگر پشته وظیفه شما از بین برود، وضعیت ذخیره شده شما نیز از بین می رود. این می تواند هنگام توقف اجباری یک برنامه، حذف برنامه از منوی اخیر، یا راه اندازی مجدد دستگاه رخ دهد. در چنین مواردی، پشته وظیفه ناپدید می شود و نمی توانید اطلاعات را در حالت ذخیره شده بازیابی کنید. در سناریوهای رد وضعیت رابط کاربری ایجاد شده توسط کاربر ، وضعیت ذخیره شده بازیابی نمی شود. در سناریوهای شروع شده توسط سیستم ، اینطور است.
راه اندازی
با شروع Fragment 1.2.0 یا وابستگی انتقالی آن Activity 1.1.0 ، می توانید SavedStateHandle
به عنوان آرگومان سازنده برای ViewModel
خود بپذیرید.
کاتلین
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
جاوا
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
سپس می توانید نمونه ای از ViewModel
خود را بدون هیچ گونه پیکربندی اضافی بازیابی کنید. کارخانه پیش فرض ViewModel
SavedStateHandle
مناسب را برای ViewModel
شما فراهم می کند.
کاتلین
class MainFragment : Fragment() { val vm: SavedStateViewModel by viewModels() ... }
جاوا
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
استفاده کرد.
کاتلین
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 } }
جاوا
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); } }
StateFlow
مقادیری را از SavedStateHandle
که در یک StateFlow
قابل مشاهده با استفاده از getStateFlow()
پیچیده شده اند، بازیابی کنید. هنگامی که مقدار کلید را به روز می کنید، StateFlow
مقدار جدید را دریافت می کند. اغلب، ممکن است به دلیل تعاملات کاربر، مانند وارد کردن یک پرس و جو برای فیلتر کردن لیستی از داده ها، مقدار را تنظیم کنید. سپس می توانید این مقدار به روز شده را با استفاده از سایر عملگرهای Flow تبدیل کنید.
کاتلین
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { val filteredData: StateFlow<List<String>> = savedStateHandle.getStateFlow<String>("query") .flatMapLatest { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } }
پشتیبانی دولتی Experimental Compose
مصنوع lifecycle-viewmodel-compose
APIهای آزمایشی saveable
را ارائه میکند که امکان همکاری بین SavedStateHandle
و Compose's Saver
را فراهم میکند، به طوری که هر State
که میتوانید از طریق rememberSaveable
با یک Saver
سفارشی ذخیره کنید، میتواند با SavedStateHandle
نیز ذخیره شود.
کاتلین
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { var filteredData: List<String> by savedStateHandle.saveable { mutableStateOf(emptyList()) } fun setQuery(query: String) { withMutableSnapshot { filteredData += query } } }
انواع پشتیبانی شده
دادههایی که در یک SavedStateHandle
نگهداری میشوند بهعنوان یک Bundle
، همراه با بقیه savedInstanceState
برای فعالیت یا قطعه، ذخیره و بازیابی میشوند.
انواع پشتیبانی مستقیم
بهطور پیشفرض، میتوانید set()
و get()
در یک SavedStateHandle
برای انواع دادههای مشابه یک Bundle
فراخوانی کنید، همانطور که در زیر نشان داده شده است:
پشتیبانی از نوع/کلاس | پشتیبانی از آرایه |
double | double[] |
int | int[] |
long | long[] |
String | String[] |
byte | byte[] |
char | char[] |
CharSequence | CharSequence[] |
float | float[] |
Parcelable | Parcelable[] |
Serializable | Serializable[] |
short | short[] |
SparseArray | |
Binder | |
Bundle | |
ArrayList | |
Size (only in API 21+) | |
SizeF (only in API 21+) |
اگر کلاس یکی از موارد موجود در لیست بالا را گسترش نمیدهد، با اضافه کردن حاشیهنویسی @Parcelize
Kotlin@ یا پیادهسازی Parcelable
به طور مستقیم، کلاس را قابل بستهبندی کنید.
صرفه جویی در کلاس های غیر قابل بسته بندی
اگر کلاسی Parcelable
یا Serializable
را پیادهسازی نمیکند و نمیتوان آن را برای پیادهسازی یکی از آن رابطها تغییر داد، امکان ذخیره مستقیم نمونهای از آن کلاس در SavedStateHandle
وجود ندارد.
با شروع Lifecycle 2.3.0-alpha03 ، SavedStateHandle
به شما این امکان را می دهد که با ارائه منطق خود برای ذخیره و بازیابی شی خود به عنوان یک Bundle
با استفاده از متد setSavedStateProvider()
هر شی را ذخیره کنید. SavedStateRegistry.SavedStateProvider
رابطی است که یک متد saveState()
را تعریف می کند که یک Bundle
حاوی حالتی را که می خواهید ذخیره کنید برمی گرداند. هنگامی که SavedStateHandle
آماده ذخیره وضعیت خود است، saveState()
را فراخوانی می کند تا Bundle
از SavedStateProvider
بازیابی کند و Bundle
برای کلید مرتبط ذخیره می کند.
نمونهای از برنامهای را در نظر بگیرید که از طریق هدف ACTION_IMAGE_CAPTURE
تصویری را از برنامه دوربین درخواست میکند و در یک فایل موقت برای جایی که دوربین باید تصویر را ذخیره کند، ارسال میکند. TempFileViewModel
منطق ایجاد آن فایل موقت را کپسوله می کند.
کاتلین
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
جاوا
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
تنظیم کنید:
کاتلین
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 } } }
جاوا
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
جدید استفاده شود.
کاتلین
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 } } }
جاوا
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; } } }
SavedStateHandle در تست ها
برای آزمایش یک ViewModel
که یک SavedStateHandle
را به عنوان وابستگی می گیرد، یک نمونه جدید از SavedStateHandle
با مقادیر تستی که نیاز دارد ایجاد کنید و آن را به نمونه ViewModel
که در حال آزمایش هستید ارسال کنید.
کاتلین
class MyViewModelTest { private lateinit var viewModel: MyViewModel @Before fun setup() { val savedState = SavedStateHandle(mapOf("someIdArg" to testId)) viewModel = MyViewModel(savedState = savedState) } }
منابع اضافی
برای اطلاعات بیشتر در مورد ماژول وضعیت ذخیره شده برای ViewModel
، به منابع زیر مراجعه کنید.
Codelabs
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- حالت های رابط کاربری را ذخیره کنید
- با اشیاء داده قابل مشاهده کار کنید
- ViewModels با وابستگی ایجاد کنید