این راهنما انتظارات کاربر در مورد وضعیت رابط کاربری و گزینه های موجود برای حفظ حالت را مورد بحث قرار می دهد.
ذخیره و بازیابی سریع حالت رابط کاربری یک فعالیت پس از اینکه سیستم فعالیتها یا برنامهها را از بین میبرد، برای یک تجربه کاربری خوب ضروری است. کاربران انتظار دارند که حالت UI ثابت بماند، اما سیستم ممکن است فعالیت و حالت ذخیره شده آن را از بین ببرد.
برای پر کردن شکاف بین انتظارات کاربر و رفتار سیستم، از ترکیبی از روشهای زیر استفاده کنید:
- اشیاء
ViewModel
. - حالت های نمونه ذخیره شده در زمینه های زیر:
- Jetpack Compose:
rememberSaveable
. - بازدیدها:
onSaveInstanceState()
API. - ViewModels:
SavedStateHandle
.
- Jetpack Compose:
- فضای ذخیرهسازی محلی برای حفظ وضعیت رابط کاربری در طول انتقال برنامه و فعالیت.
راه حل بهینه به پیچیدگی داده های رابط کاربری، موارد استفاده برنامه شما و یافتن تعادل بین سرعت دسترسی به داده و استفاده از حافظه بستگی دارد.
مطمئن شوید که برنامه شما انتظارات کاربران را برآورده می کند و یک رابط سریع و پاسخگو ارائه می دهد. هنگام بارگیری داده ها در رابط کاربری، به ویژه پس از تغییرات رایج پیکربندی مانند چرخش، از تأخیر جلوگیری کنید.
انتظارات کاربر و رفتار سیستم
بسته به اقدامی که کاربر انجام می دهد، آنها یا انتظار دارند که وضعیت فعالیت پاک شود یا وضعیت حفظ شود. در برخی موارد سیستم به طور خودکار آنچه مورد انتظار کاربر است را انجام می دهد. در موارد دیگر سیستم برخلاف آنچه کاربر انتظار دارد عمل می کند.
رد وضعیت رابط کاربری توسط کاربر
کاربر انتظار دارد که وقتی یک فعالیت را شروع می کند، وضعیت رابط کاربری گذرا آن فعالیت ثابت بماند تا زمانی که کاربر به طور کامل فعالیت را رد کند. کاربر می تواند با انجام کارهای زیر یک فعالیت را به طور کامل رد کند:
- کشیدن فعالیت از صفحه نمای کلی (اخیرا)
- کشتن یا ترک اجباری برنامه از صفحه تنظیمات.
- راه اندازی مجدد دستگاه
- انجام نوعی عمل "پایان" (که توسط
Activity.finish()
پشتیبانی می شود).
فرض کاربر در این موارد رد کامل این است که آنها به طور دائم از فعالیت دور شده اند و اگر فعالیت را دوباره باز کنند، انتظار دارند فعالیت از حالت پاک شروع شود. رفتار سیستم زیربنایی برای این سناریوهای حذف با انتظارات کاربر مطابقت دارد - نمونه فعالیت به همراه هر حالت ذخیره شده در آن و هر رکورد حالت نمونه ذخیره شده مرتبط با فعالیت از بین می رود و از حافظه حذف می شود.
این قانون در مورد حذف کامل استثنائاتی وجود دارد - برای مثال ممکن است یک کاربر از یک مرورگر انتظار داشته باشد که قبل از خروج از مرورگر با استفاده از دکمه بازگشت، آنها را به همان صفحه وبی که در آن نگاه میکردند ببرد.
رد وضعیت UI شروع شده توسط سیستم
کاربر انتظار دارد که وضعیت رابط کاربری یک فعالیت در طول یک تغییر پیکربندی، مانند چرخش یا تغییر حالت چند پنجره ای، ثابت بماند. با این حال، بهطور پیشفرض، هنگامی که چنین تغییری در پیکربندی رخ میدهد، سیستم فعالیت را از بین میبرد و هر حالت رابط کاربری ذخیره شده در نمونه فعالیت را پاک میکند. برای کسب اطلاعات بیشتر در مورد پیکربندی دستگاه، به صفحه مرجع پیکربندی مراجعه کنید. توجه داشته باشید، ممکن است (اگرچه توصیه نمی شود) رفتار پیش فرض را برای تغییرات پیکربندی لغو کنید. برای جزئیات بیشتر به مدیریت تغییر پیکربندی خود مراجعه کنید.
یک کاربر همچنین انتظار دارد که اگر به طور موقت به برنامه دیگری تغییر مکان دهد و بعداً به برنامه شما برگردد، وضعیت رابط کاربری فعالیت شما ثابت بماند. به عنوان مثال، کاربر در فعالیت جستجوی شما جستجویی را انجام می دهد و سپس دکمه خانه را فشار می دهد یا به تماس تلفنی پاسخ می دهد - وقتی به فعالیت جستجو برمی گردد انتظار دارد کلمه کلیدی جستجو و نتایج را دقیقاً مانند قبل پیدا کند.
در این سناریو، برنامه شما در پس زمینه قرار می گیرد و سیستم تمام تلاش خود را می کند تا روند برنامه شما را در حافظه نگه دارد. با این حال، سیستم ممکن است فرآیند برنامه را از بین ببرد در حالی که کاربر از تعامل با سایر برنامه ها دور است. در چنین حالتی، نمونه اکتیویتی به همراه هر حالتی که در آن ذخیره می شود، از بین می رود. هنگامی که کاربر برنامه را مجدداً راه اندازی می کند، فعالیت به طور غیرمنتظره ای در حالت تمیز قرار می گیرد. برای کسب اطلاعات بیشتر در مورد مرگ فرآیند، به فرآیندها و چرخه حیات برنامه مراجعه کنید.
گزینه هایی برای حفظ حالت رابط کاربری
هنگامی که انتظارات کاربر در مورد وضعیت رابط کاربری با رفتار پیشفرض سیستم مطابقت ندارد، باید حالت رابط کاربری کاربر را ذخیره و بازیابی کنید تا اطمینان حاصل کنید که تخریب ایجاد شده توسط سیستم برای کاربر شفاف است.
هر یک از گزینههای حفظ حالت رابط کاربری در ابعاد زیر متفاوت است که بر تجربه کاربر تأثیر میگذارد:
ViewModel | حالت نمونه ذخیره شده | ذخیره سازی دائمی | |
---|---|---|---|
محل ذخیره سازی | در حافظه | در حافظه | روی دیسک یا شبکه |
از تغییر پیکربندی جان سالم به در می برد | بله | بله | بله |
از مرگ فرآیند آغاز شده توسط سیستم زنده می ماند | خیر | بله | بله |
حذف کامل فعالیت کاربر زنده می ماند/onFinish() | خیر | خیر | بله |
محدودیت های داده | اشیاء پیچیده خوب هستند، اما فضا توسط حافظه در دسترس محدود است | فقط برای انواع اولیه و اشیاء ساده و کوچک مانند String | فقط با فضای دیسک یا هزینه / زمان بازیابی از منبع شبکه محدود شده است |
زمان خواندن/نوشتن | سریع (فقط دسترسی به حافظه) | کند (نیاز به سریالسازی/آهنگسازی دارد) | کند (نیاز به دسترسی به دیسک یا تراکنش شبکه دارد) |
از ViewModel برای مدیریت تغییرات پیکربندی استفاده کنید
ViewModel برای ذخیره و مدیریت داده های مربوط به رابط کاربری در زمانی که کاربر فعالانه از برنامه استفاده می کند ایده آل است. این امکان دسترسی سریع به دادههای رابط کاربری را فراهم میکند و به شما کمک میکند تا از واکشی مجدد دادهها از شبکه یا دیسک در طول چرخش، تغییر اندازه پنجره و سایر تغییرات معمول پیکربندی جلوگیری کنید. برای یادگیری نحوه پیاده سازی ViewModel، راهنمای ViewModel را ببینید.
ViewModel داده ها را در حافظه نگه می دارد، به این معنی که بازیابی آن ارزان تر از داده ها از دیسک یا شبکه است. یک ViewModel با یک اکتیویتی (یا مالک چرخه حیات دیگر) مرتبط است - در طول تغییر پیکربندی در حافظه می ماند و سیستم به طور خودکار ViewModel را با نمونه فعالیت جدیدی که از تغییر پیکربندی ناشی می شود مرتبط می کند.
ViewModel ها به طور خودکار توسط سیستم از بین می روند زمانی که کاربر شما از فعالیت یا قطعه شما عقب نشینی می کند یا اگر finish()
فراخوانی کنید، به این معنی است که وضعیت همانطور که کاربر در این سناریوها انتظار دارد پاک می شود.
بر خلاف حالت نمونه ذخیره شده، ViewModel ها در طول یک مرگ فرآیند آغاز شده توسط سیستم از بین می روند. برای بارگیری مجدد داده ها پس از مرگ فرآیند شروع شده توسط سیستم در ViewModel، از SavedStateHandle
API استفاده کنید. از طرف دیگر، اگر دادهها مربوط به UI هستند و نیازی به نگهداری در ViewModel ندارند، از onSaveInstanceState()
در سیستم View یا rememberSaveable
در Jetpack Compose استفاده کنید. اگر دادهها دادههای برنامه هستند، بهتر است آنها را روی دیسک نگه دارید.
اگر از قبل یک راه حل در حافظه برای ذخیره وضعیت رابط کاربری خود در تغییرات پیکربندی دارید، ممکن است نیازی به استفاده از ViewModel نداشته باشید.
از حالت نمونه ذخیره شده به عنوان پشتیبان برای مدیریت مرگ فرآیند آغاز شده توسط سیستم استفاده کنید
فراخوانی onSaveInstanceState()
در سیستم View، rememberSaveable
در Jetpack Compose، و SavedStateHandle
در ViewModels دادههای مورد نیاز برای بارگیری مجدد وضعیت یک کنترلکننده UI، مانند یک فعالیت یا یک قطعه، را ذخیره میکنند، در صورتی که سیستم آن کنترلر را از بین ببرد و بعداً دوباره ایجاد کند. برای یادگیری نحوه اجرای حالت نمونه ذخیره شده با استفاده از onSaveInstanceState
، به ذخیره و بازیابی وضعیت فعالیت در راهنمای چرخه حیات فعالیت مراجعه کنید.
بستههای حالت نمونه ذخیرهشده هم از طریق تغییرات پیکربندی و هم از طریق مرگ فرآیند باقی میمانند، اما با ذخیرهسازی و سرعت محدود میشوند، زیرا APIهای مختلف دادهها را سریالسازی میکنند. اگر اشیایی که سریال سازی می شوند پیچیده باشند، سریال سازی می تواند حافظه زیادی را مصرف کند. از آنجایی که این فرآیند در طول تغییر پیکربندی روی رشته اصلی اتفاق میافتد، سریالسازی طولانی مدت میتواند باعث افت فریم و لکنت بصری شود.
از حالت نمونه ذخیره شده برای ذخیره مقادیر زیادی از داده ها، مانند بیت مپ، یا ساختارهای داده پیچیده که نیاز به سریال سازی طولانی مدت یا deserialization دارند، استفاده نکنید. در عوض، فقط انواع اولیه و اشیاء ساده و کوچک مانند String
را ذخیره کنید. به این ترتیب، از حالت ذخیرهشده برای ذخیره حداقل مقدار دادههای لازم، مانند شناسه، استفاده کنید تا دادههای لازم برای بازگرداندن رابط کاربری به حالت قبلیاش را دوباره ایجاد کنید، در صورتی که مکانیسمهای ماندگاری دیگر از کار بیفتند. اکثر برنامه ها باید این را برای مدیریت مرگ فرآیند آغاز شده توسط سیستم اجرا کنند.
بسته به موارد استفاده برنامه شما، ممکن است اصلاً نیازی به استفاده از حالت نمونه ذخیره شده نباشد. به عنوان مثال، یک مرورگر ممکن است کاربر را به همان صفحه وب که قبل از خروج از مرورگر نگاه می کرد بازگرداند. اگر فعالیت شما اینگونه رفتار می کند، می توانید از استفاده از حالت نمونه ذخیره شده صرف نظر کنید و در عوض همه چیز را به صورت محلی ادامه دهید.
علاوه بر این، زمانی که یک اکتیویتی را از یک intent باز می کنید، دسته ای از موارد اضافی هم زمانی که پیکربندی تغییر می کند و هم زمانی که سیستم فعالیت را بازیابی می کند به اکتیویتی تحویل داده می شود. اگر هنگام راهاندازی فعالیت، بخشی از دادههای وضعیت رابط کاربری، مانند عبارت جستجو، به عنوان یک هدف اضافی ارسال میشد، میتوانید از بستههای اضافی بهجای بسته حالت نمونه ذخیرهشده استفاده کنید. برای کسب اطلاعات بیشتر در مورد موارد اضافی قصد، به فیلترهای هدف و هدف مراجعه کنید.
در هر یک از این سناریوها، برای جلوگیری از هدر رفتن چرخه بارگیری مجدد داده ها از پایگاه داده در طول تغییر پیکربندی، همچنان باید از ViewModel
استفاده کنید.
در مواردی که دادههای رابط کاربری برای حفظ ساده و سبک هستند، ممکن است از APIهای حالت نمونه ذخیرهشده به تنهایی برای حفظ دادههای حالت خود استفاده کنید.
با استفاده از SavedStateRegistry به حالت ذخیره شده قلاب کنید
با شروع Fragment 1.1.0 یا وابستگی گذرا آن Activity 1.0.0 ، کنترلکنندههای UI، مانند Activity
یا Fragment
، SavedStateRegistryOwner
را پیادهسازی میکنند و یک SavedStateRegistry
ارائه میدهند که به آن کنترلکننده متصل است. SavedStateRegistry
به اجزا اجازه می دهد تا به حالت ذخیره شده کنترلر UI شما متصل شوند تا آن را مصرف کنند یا در آن مشارکت کنند. به عنوان مثال، ماژول Saved State برای ViewModel از SavedStateRegistry
برای ایجاد SavedStateHandle
و ارائه آن به اشیاء ViewModel
شما استفاده می کند. شما می توانید SavedStateRegistry
از داخل کنترلر UI خود با فراخوانی getSavedStateRegistry()
بازیابی کنید.
مؤلفه هایی که به حالت ذخیره شده کمک می کنند باید SavedStateRegistry.SavedStateProvider
را پیاده سازی کنند که یک متد واحد به نام saveState()
را تعریف می کند. متد saveState()
به کامپوننت شما اجازه می دهد تا یک Bundle
حاوی هر حالتی را که باید از آن کامپوننت ذخیره شود، برگرداند. SavedStateRegistry
این روش را در مرحله ذخیره کردن حالت چرخه حیات کنترلر UI فراخوانی می کند.
کاتلین
class SearchManager : SavedStateRegistry.SavedStateProvider { companion object { private const val QUERY = "query" } private val query: String? = null ... override fun saveState(): Bundle { return bundleOf(QUERY to query) } }
جاوا
class SearchManager implements SavedStateRegistry.SavedStateProvider { private static String QUERY = "query"; private String query = null; ... @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); bundle.putString(QUERY, query); return bundle; } }
برای ثبت SavedStateProvider
، registerSavedStateProvider()
را در SavedStateRegistry
فراخوانی کنید و کلیدی را برای ارتباط با داده های ارائه دهنده و همچنین ارائه دهنده ارسال کنید. دادههای ذخیرهشده قبلی برای ارائهدهنده را میتوان با فراخوانی consumeRestoredStateForKey()
در SavedStateRegistry
و ارسال کلید مرتبط با دادههای ارائهدهنده، از حالت ذخیرهشده بازیابی کرد.
در یک Activity
یا Fragment
، میتوانید یک SavedStateProvider
در onCreate()
پس از فراخوانی super.onCreate()
ثبت کنید. یا میتوانید یک LifecycleObserver
روی SavedStateRegistryOwner
تنظیم کنید که LifecycleOwner
را پیادهسازی میکند و پس از وقوع رویداد ON_CREATE
SavedStateProvider
را ثبت کنید. با استفاده از LifecycleObserver
، می توانید ثبت و بازیابی حالت ذخیره شده قبلی را از خود SavedStateRegistryOwner
جدا کنید.
کاتلین
class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider { companion object { private const val PROVIDER = "search_manager" private const val QUERY = "query" } private val query: String? = null init { // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_CREATE) { val registry = registryOwner.savedStateRegistry // Register this object for future calls to saveState() registry.registerSavedStateProvider(PROVIDER, this) // Get the previously saved state and restore it val state = registry.consumeRestoredStateForKey(PROVIDER) // Apply the previously saved state query = state?.getString(QUERY) } } } override fun saveState(): Bundle { return bundleOf(QUERY to query) } ... } class SearchFragment : Fragment() { private var searchManager = SearchManager(this) ... }
جاوا
class SearchManager implements SavedStateRegistry.SavedStateProvider { private static String PROVIDER = "search_manager"; private static String QUERY = "query"; private String query = null; public SearchManager(SavedStateRegistryOwner registryOwner) { registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> { if (event == Lifecycle.Event.ON_CREATE) { SavedStateRegistry registry = registryOwner.getSavedStateRegistry(); // Register this object for future calls to saveState() registry.registerSavedStateProvider(PROVIDER, this); // Get the previously saved state and restore it Bundle state = registry.consumeRestoredStateForKey(PROVIDER); // Apply the previously saved state if (state != null) { query = state.getString(QUERY); } } }); } @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); bundle.putString(QUERY, query); return bundle; } ... } class SearchFragment extends Fragment { private SearchManager searchManager = new SearchManager(this); ... }
از پایداری محلی برای مدیریت مرگ پردازش برای داده های پیچیده یا بزرگ استفاده کنید
ذخیرهسازی محلی دائمی، مانند پایگاه داده یا تنظیمات برگزیده مشترک، تا زمانی که برنامه شما روی دستگاه کاربر نصب شده باشد، باقی خواهد ماند (مگر اینکه کاربر دادههای برنامه شما را پاک کند). در حالی که چنین ذخیرهسازی محلی از فعالیتهای آغاز شده توسط سیستم و مرگ فرآیند برنامهها جان سالم به در میبرد، بازیابی آن میتواند گران باشد زیرا باید از حافظه محلی در حافظه خوانده شود. اغلب این حافظه محلی دائمی ممکن است بخشی از معماری برنامه شما باشد تا تمام داده هایی را که نمی خواهید در صورت باز و بسته کردن فعالیت از دست بدهید، ذخیره می کند.
نه ViewModel و نه حالت نمونه ذخیره شده راه حل های ذخیره سازی طولانی مدت نیستند و بنابراین جایگزینی برای ذخیره سازی محلی، مانند پایگاه داده نیستند. در عوض باید از این مکانیسمها فقط برای ذخیره موقت حالت رابط کاربری گذرا استفاده کنید و از ذخیرهسازی دائمی برای سایر دادههای برنامه استفاده کنید. برای جزئیات بیشتر درباره نحوه استفاده از حافظه محلی برای حفظ طولانی مدت داده های مدل برنامه خود (مثلاً در راه اندازی مجدد دستگاه) به راهنمای معماری برنامه مراجعه کنید.
مدیریت وضعیت رابط کاربری: تقسیم کن و غلبه کن
میتوانید با تقسیم کار بین انواع مختلف مکانیسمهای ماندگاری، حالت رابط کاربری را ذخیره و بازیابی کنید. در بیشتر موارد، هر یک از این مکانیسمها باید نوع متفاوتی از دادههای مورد استفاده در فعالیت را ذخیره کنند، بر اساس معاوضه پیچیدگی داده، سرعت دسترسی و طول عمر:
- پایداری محلی: تمام داده های برنامه را که نمی خواهید در صورت باز و بسته کردن فعالیت از دست بدهید، ذخیره می کند.
- مثال: مجموعه ای از اشیاء آهنگ، که می تواند شامل فایل های صوتی و ابرداده باشد.
-
ViewModel
: تمام داده های مورد نیاز برای نمایش رابط کاربری مرتبط، وضعیت رابط کاربری صفحه نمایش را در حافظه ذخیره می کند.- مثال: اشیاء آهنگ آخرین جستجو و جدیدترین جستجو.
- وضعیت نمونه ذخیره شده: مقدار کمی از داده های مورد نیاز برای بارگیری مجدد وضعیت رابط کاربری را در صورت توقف سیستم ذخیره می کند و سپس UI را دوباره ایجاد می کند. به جای ذخیره اشیاء پیچیده در اینجا، اشیاء پیچیده را در حافظه محلی نگه دارید و یک شناسه منحصر به فرد برای این اشیاء در APIهای حالت نمونه ذخیره شده ذخیره کنید.
- مثال: ذخیره جدیدترین عبارت جستجو.
به عنوان مثال، فعالیتی را در نظر بگیرید که به شما امکان می دهد در کتابخانه آهنگ های خود جستجو کنید. در اینجا نحوه برخورد با رویدادهای مختلف آمده است:
هنگامی که کاربر آهنگی را اضافه می کند، ViewModel
فوراً حفظ این داده ها را به صورت محلی واگذار می کند. اگر این آهنگ جدید اضافه شده باید در UI نشان داده شود، باید داده ها را در شی ViewModel
نیز به روز کنید تا اضافه شدن آهنگ را منعکس کند. به یاد داشته باشید که تمام درج های پایگاه داده را از موضوع اصلی انجام دهید.
هنگامی که کاربر آهنگی را جستجو می کند، هر داده آهنگ پیچیده ای را که از پایگاه داده بارگیری می کنید، باید فوراً در شی ViewModel
به عنوان بخشی از حالت رابط کاربری صفحه ذخیره شود.
وقتی فعالیت به پسزمینه میرود و سیستم APIهای حالت نمونه ذخیرهشده را فراخوانی میکند، درخواست جستجو باید در حالت نمونه ذخیرهشده ذخیره شود، در صورتی که فرآیند دوباره ایجاد شود. از آنجایی که اطلاعات برای بارگیری داده های برنامه موجود در این مورد ضروری است، عبارت جستجو را در ViewModel SavedStateHandle
ذخیره کنید. این تمام اطلاعاتی است که برای بارگیری داده ها و برگرداندن رابط کاربری به وضعیت فعلی خود نیاز دارید.
حالت های پیچیده را بازیابی کنید: مونتاژ مجدد قطعات
هنگامی که زمان بازگشت کاربر به فعالیت فرا می رسد، دو سناریو ممکن برای ایجاد مجدد فعالیت وجود دارد:
- فعالیت پس از توقف توسط سیستم دوباره ایجاد می شود. سیستم درخواست را در یک بسته حالت نمونه ذخیره شده ذخیره می کند، و اگر از
SavedStateHandle
استفاده نمی شود، UI باید پرس و جو را بهViewModel
ارسال کند.ViewModel
می بیند که هیچ نتیجه جستجوی در حافظه پنهان ندارد و بارگیری نتایج جستجو را با استفاده از عبارت جستجوی داده شده واگذار می کند. - فعالیت پس از تغییر پیکربندی ایجاد می شود. از آنجایی که نمونه
ViewModel
از بین نرفته است،ViewModel
تمام اطلاعات را در حافظه پنهان دارد و نیازی به جستجو مجدد از پایگاه داده ندارد.
منابع اضافی
برای کسب اطلاعات بیشتر در مورد ذخیره وضعیت های رابط کاربری، به منابع زیر مراجعه کنید.
وبلاگ ها
- ViewModels: یک مثال ساده
- ViewModels: Persistence،
onSaveInstanceState()
، بازیابی حالت رابط کاربری و لودرها - آزمایشگاه کد اجزای آگاه از چرخه حیات اندروید
برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- ماژول حالت ذخیره شده برای ViewModel
- مدیریت چرخه زندگی با اجزای مربوط به چرخه حیات
- نمای کلی ViewModel