واحد «وضعیت ذخیرهشده» برای ViewModel بخشی از Android Jetpack.
همانطور که در ذخیره کردن حالتهای میانای کاربر ذکر شد، اشیا ViewModel میتوانند تغییرات پیکربندی را مدیریت کنند، بنابراین لازم نیست نگران حالت در چرخشها یا موارد دیگر باشید. بااینحال، اگر نیاز دارید که مرگ فرایند آغازشده توسط سیستم را مدیریت کنید، بهتر است از SavedStateHandle API بهعنوان پشتیبان استفاده کنید.
وضعیت واسط کاربر معمولاً در ViewModel شیء ذخیره یا ارجاع داده میشود، بنابراین استفاده از
rememberSaveable در Compose به مقداری کد استاندارد نیاز دارد که
واحد وضعیت ذخیرهشده میتواند آن را برایتان مدیریت کند.
هنگام استفاده از این واحد، ViewModel شیء ازطریق سازندهاش شیء SavedStateHandle
دریافت میکند. این شیء نقشه کلید-مقدار است که به شما امکان میدهد
اشیا را در وضعیت ذخیرهشده بنویسید و از آن بازیابی کنید. این مقادیر
پساز اینکه سیستم فرایند را متوقف میکند ماندگار میمانند و
ازطریق همان شیء دردسترس باقی میمانند.
وضعیت ذخیرهشده به پشته تکلیف شما مرتبط است. اگر پشته تکلیف شما ناپدید شود، وضعیت ذخیرهشده شما نیز ناپدید میشود. این اتفاق ممکن است هنگام توقف اجباری برنامه، برداشتن برنامه از منو برنامههای اخیر، یا بازراهاندازی دستگاه رخ دهد. در چنین مواردی، پشته تکلیف ناپدید میشود و نمیتوانید اطلاعات را در وضعیت ذخیرهشده بازیابی کنید. در حالتهای بستن رابط کاربری آغازشده ازسوی کاربر، حالت ذخیرهشده بازیابی نمیشود. در سناریوهای سیستم-آغازشده، اینگونه است.
راهاندازی
برای استفاده از SavedStateHandle، آن را بهعنوان آرگومان سازنده به
ViewModel اضافه کنید.
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
سپس میتوانید نمونهای از ViewModel را بدون پیکربندی اضافی در عناصر ترکیبیتان بازیابی کنید. کارخانه پیشفرض ViewModel
SavedStateHandle مناسب را برای ViewModel شما فراهم میکند.
class MyViewModel : ViewModel() { /*...*/ } // import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( viewModel: MyViewModel = viewModel() ) { // use viewModel here }
هنگام ارائه نمونه سفارشی ViewModelProvider.Factory، میتوانید بااستفاده از CreationExtras و
viewModelFactory DSL، استفاده از SavedStateHandle را فعال کنید.
کار کردن با SavedStateHandle
کلاس SavedStateHandle یک نقشه کلید-مقدار است که به شما امکان میدهد دادهها را ازطریق روشهای set() و get() در وضعیت ذخیرهشده بنویسید و بازیابی کنید.
بااستفاده از SavedStateHandle، مقدار پُرسمان درطول مرگ فرایند حفظ میشود و
مطمئن میشوید که کاربر مجموعه یکسانی از دادههای فیلترشده را قبلاز و بعداز
بازسازی بدون نیاز به ذخیره، بازیابی،
و ارسال دستی آن مقدار به ViewModel در فعالیت یا قطعه میبیند.
SavedStateHandle روشهای دیگری نیز دارد که هنگام تعامل با نقشه کلید-مقدار انتظار دارید:
contains(String key)- بررسی میکند که آیا مقدار برای کلید دادهشده وجود دارد یا نه.remove(String key)- مقدار کلید دادهشده را برمیدارد.keys()- همه کلیدهای موجود درSavedStateHandleرا برمیگرداند.
علاوهبراین، میتوانید مقادیر را بااستفاده از
نگهدارنده دادههای قابلمشاهده از SavedStateHandle بازیابی کنید. فهرست انواع پشتیبانیشده شامل موارد زیر است:
StateFlow
میتوانید مقادیر را از SavedStateHandle که در StateFlow
مشاهدهپذیر پیچیده شده است بازیابی کنید. بسته به اینکه نیاز دارید مقدار را مستقیماً تغییر دهید یا نه، میتوانید
جریان فقطخواندنی یا تغییرپذیر را انتخاب کنید:
getStateFlow(): اگر فقط نیاز به خواندن وضعیت دارید، از این استفاده کنید. وقتی مقدار کلید را در جای دیگری درSavedStateHandleبهروزرسانی میکنید، StateFlow مقدار جدید را دریافت میکند. این ویژگی زمانی ایدهآل است که بخواهید یک جریان فقطخواندنی را آشکار کنید و آن را بااستفاده از عاملهای Flow تبدیل کنید.getMutableStateFlow(): اگر به دسترسی خواندن و نوشتن نیاز دارید از این استفاده کنید. بهروزرسانی.valueازMutableStateFlowبرگشتی بهطور خودکارSavedStateHandleزیرین را بهروزرسانی میکند و شما را از نیاز به تنظیم دستی کلید بینیاز میکند.
اغلب، این مقادیر را بهدلیل تعاملات کاربر، مثل وارد کردن پُرسمان برای فیلتر کردن فهرست دادهها، بهروز میکنید.
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { // Use getMutableStateFlow to read and write the query directly private val _query = savedStateHandle.getMutableStateFlow("query", "") val query: StateFlow= _query.asStateFlow() // Use getStateFlow if you only need a read-only stream to react to changes val filteredData: StateFlow<List > = query.flatMapLatest { repository.getFilteredData(it) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() ) fun setQuery(newQuery: String) { // Updating the MutableStateFlow automatically updates the SavedStateHandle _query.value = newQuery } }
پشتیبانی از سریالسازی KotlinX
برای وضعیت پیچیده واسط کاربر، میتوانید از نماینده دارایی saved در کنار
KotlinX Serialization استفاده کنید. این نماینده به شما امکان میدهد کلاسهای داده سفارشی @Serializable
را مستقیماً در SavedStateHandle ماندگار کنید. این کار وضعیت
ViewModel را درطول مرگ پردازش حفظ میکند، بنابراین «میانای کاربری Compose» شما میتواند
وضعیت خود را پساز بازسازی بهطور یکپارچه بازیابی کند.
برای استفاده از آن، کلاس دادهتان را با @Serializable حاشیهنویسی کنید و از نماینده saved
در «نمای مدل» خود استفاده کنید:
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel // Ensure you have the savedstate-ktx dependency import androidx.savedstate.serialization.saved import kotlinx.serialization.Serializable @Serializable data class UserFilterState( val searchQuery: String, val minAge: Int, val includeInactive: Boolean ) class FilterViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { // The state is automatically serialized to a Bundle on process death, // and deserialized upon recreation. var filterState by savedStateHandle.saved { UserFilterState(searchQuery = "", minAge = 18, includeInactive = false) } fun updateQuery(newQuery: String) { // Mutating the property automatically updates the underlying SavedStateHandle filterState = filterState.copy(searchQuery = newQuery) } }
پشتیبانی از وضعیت نوشتن
اگر وضعیت شما بهجای KotlinX
Serialization به APIهای Saver در Compose متکی باشد،
آرتیفکت lifecycle-viewmodel-compose نماینده
saveable را ارائه میدهد. این کار امکان همکنشپذیری بین
SavedStateHandle و 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 برای برنامهتان.
انواع پشتیبانیشده مستقیم
بهطور پیشفرض، میتوانید در SavedStateHandle برای همان انواع دادهای که Bundle دارد با set() و get() تماس بگیرید، همانطور که در زیر نشان داده شده است:
| پشتیبانی نوع/کلاس | پشتیبانی آرایه |
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 } } }
برای اطمینان از اینکه اگر فرایند فعالیت متوقف شود و بعداً بازیابی شود، فایل موقت ازدست نمیرود، 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 } } }
برای بازیابی دادههای 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 } } }
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 با وابستگیها