بسته به اینکه ایالت شما در کجا قرار گرفته است و منطق مورد نیاز، می توانید از API های مختلف برای ذخیره و بازیابی حالت رابط کاربری خود استفاده کنید. هر اپلیکیشنی از ترکیبی از APIها برای رسیدن به این هدف استفاده می کند.
هر برنامه اندرویدی ممکن است به دلیل فعالیت یا سرگرمی فرآیند ، حالت رابط کاربری خود را از دست بدهد. این از دست دادن حالت می تواند به دلیل رویدادهای زیر رخ دهد:
- تغییرات پیکربندی فعالیت از بین می رود و دوباره ایجاد می شود مگر اینکه تغییر پیکربندی به صورت دستی انجام شود.
- مرگ فرآیند آغاز شده توسط سیستم برنامه در پسزمینه است و دستگاه منابع (مانند حافظه) را برای استفاده توسط سایر فرآیندها آزاد میکند.
حفظ وضعیت پس از این رویدادها برای یک تجربه کاربری مثبت ضروری است. انتخاب حالتی که ادامه پیدا کند به جریان کاربر منحصر به فرد برنامه شما بستگی دارد. به عنوان بهترین روش، حداقل باید ورودی کاربر و وضعیت مربوط به ناوبری را حفظ کنید. نمونه هایی از این موارد عبارتند از موقعیت اسکرول یک لیست، شناسه موردی که کاربر جزئیات بیشتری در مورد آن می خواهد، انتخاب در حال انجام تنظیمات برگزیده کاربر، یا ورودی در فیلدهای متنی.
این صفحه APIهای موجود برای ذخیره حالت رابط کاربری را بسته به اینکه ایالت شما در کجا قرار دارد و منطقی که به آن نیاز دارد خلاصه می کند.
منطق رابط کاربری
اگر حالت شما در UI، یا در توابع ترکیبپذیر یا کلاسهای دارنده حالت ساده که در محدوده Composition قرار دارند، بالا میرود، میتوانید از rememberSaveable
برای حفظ حالت در سراسر فعالیت و بازآفرینی فرآیند استفاده کنید.
در قطعه زیر، rememberSaveable
برای ذخیره یک حالت عنصر UI بولی استفاده می شود:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } ) if (showDetails) { Text(message.timestamp) } }
showDetails
یک متغیر بولی است که در صورت جمع شدن یا بزرگ شدن حباب چت ذخیره می شود.
rememberSaveable
حالت عنصر UI را در یک Bundle
از طریق مکانیسم حالت نمونه ذخیره شده ذخیره می کند.
این می تواند انواع اولیه را به صورت خودکار در بسته ذخیره کند. اگر حالت شما در یک نوع ابتدایی نیست، مانند کلاس داده، میتوانید از مکانیسمهای ذخیرهسازی مختلفی استفاده کنید، مانند استفاده از حاشیهنویسی Parcelize
، استفاده از Compose API مانند listSaver
و mapSaver
، یا پیادهسازی یک کلاس محافظ سفارشی که Compose Runtime Saver
را گسترش میدهد. کلاس برای کسب اطلاعات بیشتر در مورد این روشها، راههای ذخیره اسناد حالت را ببینید.
در قطعه زیر، rememberLazyListState
Compose API LazyListState
را ذخیره میکند، که از حالت اسکرول یک LazyColumn
یا LazyRow
با استفاده از rememberSaveable
تشکیل شده است. از یک LazyListState.Saver
استفاده می کند که یک محافظ سفارشی است که می تواند وضعیت اسکرول را ذخیره و بازیابی کند. پس از یک فعالیت یا بازآفرینی فرآیند (به عنوان مثال، پس از تغییر پیکربندی مانند تغییر جهت دستگاه)، وضعیت اسکرول حفظ می شود.
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
بهترین تمرین
rememberSaveable
از یک Bundle
برای ذخیره حالت رابط کاربری استفاده می کند که توسط API های دیگری که در آن می نویسند نیز به اشتراک گذاشته می شود، مانند فراخوانی های onSaveInstanceState()
در فعالیت شما. با این حال، اندازه این Bundle
محدود است و ذخیره اشیاء بزرگ می تواند منجر به استثناهای TransactionTooLarge
در زمان اجرا شود. این میتواند بهویژه در برنامههای Activity
منفرد که از همان Bundle
در سراسر برنامه استفاده میشود، مشکلساز باشد.
برای جلوگیری از این نوع خرابی، نباید اشیاء پیچیده بزرگ یا لیستی از اشیاء را در بسته ذخیره کنید .
درعوض، حداقل حالت مورد نیاز، مانند شناسهها یا کلیدها را ذخیره کنید، و از آنها برای واگذاری بازیابی وضعیت پیچیدهتر رابط کاربری به مکانیسمهای دیگر، مانند ذخیرهسازی دائمی ، استفاده کنید.
این انتخاب های طراحی به موارد استفاده خاص برای برنامه شما و اینکه کاربران شما چگونه انتظار دارند که از آن رفتار کند بستگی دارد.
بازیابی حالت را تأیید کنید
می توانید تأیید کنید که وضعیت ذخیره شده با rememberSaveable
در عناصر Compose شما به درستی بازیابی شده است که فعالیت یا فرآیند دوباره ایجاد شود. API های خاصی برای رسیدن به این هدف وجود دارد، مانند StateRestorationTester
. برای کسب اطلاعات بیشتر، مستندات تست را بررسی کنید.
منطق کسب و کار
اگر حالت عنصر UI شما به ViewModel
تعبیه شده است زیرا منطق تجاری آن را لازم می داند، می توانید از API های ViewModel
استفاده کنید.
یکی از مزایای اصلی استفاده از ViewModel
در برنامه اندرویدی شما این است که تغییرات پیکربندی را به صورت رایگان انجام می دهد. هنگامی که یک تغییر پیکربندی وجود دارد، و فعالیت از بین میرود و دوباره ایجاد میشود، حالت رابط کاربری که در ViewModel
افزایش مییابد در حافظه نگهداری میشود. پس از بازآفرینی، نمونه ViewModel
قدیمی به نمونه فعالیت جدید متصل می شود.
با این حال، یک نمونه ViewModel
از مرگ فرآیند آغاز شده توسط سیستم جان سالم به در نمی برد. برای اینکه حالت رابط کاربری در این حالت باقی بماند، از ماژول Saved State برای ViewModel استفاده کنید که حاوی SavedStateHandle
API است.
بهترین تمرین
SavedStateHandle
همچنین از مکانیسم Bundle
برای ذخیره وضعیت رابط کاربری استفاده می کند، بنابراین شما باید از آن فقط برای ذخیره حالت عنصر UI ساده استفاده کنید.
حالت رابط کاربری صفحه ، که با اعمال قوانین تجاری و دسترسی به لایههای برنامه شما غیر از رابط کاربری ایجاد میشود، به دلیل پیچیدگی و اندازه احتمالی آن، نباید در SavedStateHandle
ذخیره شود. میتوانید از مکانیسمهای مختلفی برای ذخیره دادههای پیچیده یا بزرگ مانند ذخیرهسازی دائمی محلی استفاده کنید. پس از بازآفرینی فرآیند، صفحه با حالت گذرا بازیابی شده که در SavedStateHandle
ذخیره شده بود (در صورت وجود) دوباره ایجاد می شود و حالت رابط کاربری صفحه دوباره از لایه داده تولید می شود.
API های SavedStateHandle
SavedStateHandle
دارای API های مختلفی برای ذخیره وضعیت عنصر UI است که مهم ترین آنها عبارتند از:
State نوشتن | saveable() |
---|---|
StateFlow | getStateFlow() |
State
نوشتن
از API saveable
SavedStateHandle
برای خواندن و نوشتن حالت عنصر UI به عنوان MutableState
استفاده کنید، بنابراین با حداقل تنظیم کد، از فعالیت و بازآفرینی فرآیندها جان سالم به در میبرد.
API saveable
از انواع اولیه خارج از جعبه پشتیبانی میکند و یک پارامتر stateSaver
برای استفاده از ذخیرهکنندههای سفارشی، درست مانند rememberSaveable()
دریافت میکند.
در قطعه زیر، message
انواع ورودی کاربر را در یک TextField
ذخیره می کند:
class ConversationViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } private set fun update(newMessage: TextFieldValue) { message = newMessage } /*...*/ } val viewModel = ConversationViewModel(SavedStateHandle()) @Composable fun UserInput(/*...*/) { TextField( value = viewModel.message, onValueChange = { viewModel.update(it) } ) }
برای اطلاعات بیشتر در مورد استفاده از API saveable
به مستندات SavedStateHandle
مراجعه کنید.
StateFlow
از getStateFlow()
برای ذخیره حالت عنصر UI و مصرف آن به عنوان یک جریان از SavedStateHandle
استفاده کنید. StateFlow
فقط خواندنی است و API از شما می خواهد که یک کلید مشخص کنید تا بتوانید جریان را جایگزین کنید تا یک مقدار جدید منتشر شود. با کلیدی که پیکربندی کرده اید، می توانید StateFlow
را بازیابی کنید و آخرین مقدار را جمع آوری کنید.
در قطعه زیر، savedFilterType
یک متغیر StateFlow
است که نوع فیلتر اعمال شده در لیستی از کانالهای چت را در یک برنامه چت ذخیره میکند:
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey" class ChannelViewModel( channelsRepository: ChannelsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow( key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS ) private val filteredChannels: Flow<List<Channel>> = combine(channelsRepository.getAll(), savedFilterType) { channels, type -> filter(channels, type) }.onStart { emit(emptyList()) } fun setFiltering(requestType: ChannelsFilterType) { savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType } /*...*/ } enum class ChannelsFilterType { ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS }
هر بار که کاربر یک نوع فیلتر جدید را انتخاب می کند، setFiltering
فراخوانی می شود. این یک مقدار جدید را در SavedStateHandle
ذخیره می کند که با کلید _CHANNEL_FILTER_SAVED_STATE_KEY_
ذخیره شده است. savedFilterType
جریانی است که آخرین مقدار ذخیره شده در کلید را منتشر می کند. filteredChannels
مشترک جریان است تا فیلتر کانال را انجام دهد.
برای اطلاعات بیشتر در مورد API getStateFlow()
به مستندات SavedStateHandle
مراجعه کنید.
خلاصه
جدول زیر خلاصه ای از API های پوشش داده شده در این بخش و زمان استفاده از هر یک برای ذخیره وضعیت UI را نشان می دهد:
رویداد | منطق رابط کاربری | منطق کسب و کار در ViewModel |
---|---|---|
تغییرات پیکربندی | rememberSaveable | خودکار |
مرگ فرآیند آغاز شده توسط سیستم | rememberSaveable | SavedStateHandle |
API مورد استفاده بستگی به محل نگهداری حالت و منطق مورد نیاز آن دارد. برای حالتی که در منطق UI استفاده می شود، از rememberSaveable
استفاده کنید. برای حالتی که در منطق تجاری استفاده می شود، اگر آن را در ViewModel
نگه دارید، آن را با استفاده از SavedStateHandle
ذخیره کنید.
شما باید از APIهای بسته ( rememberSaveable
و SavedStateHandle
) برای ذخیره مقادیر کمی از حالت رابط کاربری استفاده کنید. این داده حداقلی است که برای بازگرداندن رابط کاربری به حالت قبلی، همراه با سایر مکانیسمهای ذخیرهسازی لازم است. برای مثال، اگر شناسه نمایهای را که کاربر به آن نگاه میکرد در بسته ذخیره کنید، میتوانید دادههای سنگین مانند جزئیات نمایه را از لایه داده دریافت کنید.
برای کسب اطلاعات بیشتر در مورد روشهای مختلف ذخیره حالت رابط کاربری، به مستندات کلی Saving State UI و صفحه لایه داده راهنمای معماری مراجعه کنید.
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- محل بالا بردن حالت
- State و Jetpack Compose
- فهرست ها و شبکه ها