این راهنما در مورد انتظارات کاربر در مورد وضعیت UI و گزینه های موجود برای حفظ حالت بحث می کند.
ذخیره و بازیابی سریع حالت رابط کاربری یک فعالیت پس از اینکه سیستم فعالیتها یا برنامهها را از بین میبرد، برای یک تجربه کاربری خوب ضروری است. کاربران انتظار دارند که دولت UI یکسان باشد ، اما این سیستم ممکن است فعالیت و وضعیت ذخیره شده آن را از بین ببرد.
برای ایجاد شکاف بین انتظارات کاربر و رفتار سیستم ، از ترکیبی از روشهای زیر استفاده کنید:
- اشیاء
ViewModel
. - نمونه های ذخیره شده در زمینه های زیر:
- Jetpack Compose:
rememberSaveable
. - بازدیدها:
onSaveInstanceState()
API. - ViewModels:
SavedStateHandle
.
- Jetpack Compose:
- ذخیره محلی برای ادامه وضعیت UI در طول انتقال برنامه و فعالیت.
The optimal solution depends on your UI data's complexity, your app's use cases, and finding a balance between data access speed and memory use.
Make sure your app meets users' expectations and offers a fast, responsive interface. Avoid delays when loading data into the UI, particularly after common configuration changes like rotation.
انتظارات کاربر و رفتار سیستم
Depending upon the action a user takes, they either expect that activity state to be cleared or the state to be preserved. In some cases the system automatically does what is expected by the user. In other cases the system does the opposite of what the user expects.
رد وضعیت رابط کاربری توسط کاربر
کاربر انتظار دارد که وقتی یک فعالیت را شروع می کند، وضعیت رابط کاربری گذرا آن فعالیت ثابت بماند تا زمانی که کاربر به طور کامل فعالیت را رد کند. The user can completely dismiss an activity by doing the following:
- Swiping the activity off of the Overview (Recents) screen.
- کشتن یا ایجاد نیرو برنامه از صفحه تنظیمات.
- راه اندازی مجدد دستگاه.
- Completing some sort of "finishing" action (which is backed by
Activity.finish()
).
فرض کاربر در این موارد رد کامل این است که آنها به طور دائم از فعالیت دور شده اند و اگر فعالیت را دوباره باز کنند، انتظار دارند فعالیت از حالت پاک شروع شود. رفتار سیستم زیربنایی برای این سناریوهای حذف با انتظارات کاربر مطابقت دارد - نمونه فعالیت به همراه هر حالت ذخیره شده در آن و هر رکورد حالت نمونه ذخیره شده مرتبط با فعالیت از بین می رود و از حافظه حذف می شود.
این قانون در مورد حذف کامل استثنائاتی وجود دارد - برای مثال ممکن است یک کاربر از یک مرورگر انتظار داشته باشد که قبل از خروج از مرورگر با استفاده از دکمه بازگشت، آنها را به همان صفحه وبی که در آن نگاه میکردند ببرد.
رد وضعیت UI شروع شده توسط سیستم
کاربر انتظار دارد که وضعیت رابط کاربری یک فعالیت در طول یک تغییر پیکربندی، مانند چرخش یا تغییر حالت چند پنجره ای، ثابت بماند. با این حال، بهطور پیشفرض، هنگامی که چنین تغییری در پیکربندی رخ میدهد، سیستم فعالیت را از بین میبرد و هر حالت رابط کاربری ذخیره شده در نمونه فعالیت را پاک میکند. To learn more about device configurations, see the Configuration reference page . Note, it is possible (though not recommended) to override the default behavior for configuration changes. برای جزئیات بیشتر به رسیدگی به پیکربندی خود تغییر دهید .
یک کاربر همچنین انتظار دارد که اگر به طور موقت به برنامه دیگری تغییر مکان دهد و بعداً به برنامه شما برگردد، وضعیت رابط کاربری فعالیت شما ثابت بماند. به عنوان مثال، کاربر در فعالیت جستجوی شما جستجویی را انجام می دهد و سپس دکمه خانه را فشار می دهد یا به تماس تلفنی پاسخ می دهد - وقتی به فعالیت جستجو برمی گردد انتظار دارد کلمه کلیدی جستجو و نتایج را دقیقاً مانند قبل پیدا کند.
In this scenario, your app is placed in the background the system does its best to keep your app process in memory. However, the system may destroy the application process while the user is away interacting with other apps. In such a case, the activity instance is destroyed, along with any state stored in it. هنگامی که کاربر برنامه را مجدداً راه اندازی می کند ، فعالیت به طور غیر منتظره ای در حالت تمیز است. برای کسب اطلاعات بیشتر در مورد مرگ فرآیند ، به فرایندها و چرخه عمر برنامه مراجعه کنید.
گزینه هایی برای حفظ حالت رابط کاربری
هنگامی که انتظارات کاربر در مورد وضعیت رابط کاربری با رفتار پیشفرض سیستم مطابقت ندارد، باید حالت رابط کاربری کاربر را ذخیره و بازیابی کنید تا اطمینان حاصل کنید که تخریب ایجاد شده توسط سیستم برای کاربر شفاف است.
Each of the options for preserving UI state vary along the following dimensions that impact the user experience:
ViewModel | حالت نمونه ذخیره شده | ذخیره سازی دائمی | |
---|---|---|---|
محل ذخیره سازی | در حافظه | در حافظه | روی دیسک یا شبکه |
از تغییر پیکربندی جان سالم به در می برد | بله | بله | بله |
از مرگ فرآیند آغاز شده توسط سیستم زنده می ماند | خیر | بله | بله |
Survives user complete activity dismissal/onFinish() | خیر | خیر | بله |
محدودیت های داده | اشیاء پیچیده خوب هستند ، اما فضا با حافظه موجود محدود است | only for primitive types and simple, small objects such as String | only limited by disk space or cost / time of retrieval from the network resource |
زمان خواندن/نوشتن | سریع (فقط دسترسی به حافظه) | کند (نیاز به سریالسازی/آهنگسازی دارد) | آهسته (نیاز به دسترسی به دیسک یا معامله شبکه دارد) |
از ViewModel برای مدیریت تغییرات پیکربندی استفاده کنید
ViewModel is ideal for storing and managing UI-related data while the user is actively using the application. این امکان دسترسی سریع به دادههای رابط کاربری را فراهم میکند و به شما کمک میکند تا از واکشی مجدد دادهها از شبکه یا دیسک در طول چرخش، تغییر اندازه پنجره و سایر تغییرات معمول پیکربندی جلوگیری کنید. To learn how to implement a ViewModel, see the ViewModel guide .
ViewModel retains the data in memory, which means it is cheaper to retrieve than data from the disk or the network. یک ViewModel با یک اکتیویتی (یا مالک چرخه حیات دیگر) مرتبط است - در طول تغییر پیکربندی در حافظه می ماند و سیستم به طور خودکار ViewModel را با نمونه فعالیت جدیدی که از تغییر پیکربندی ناشی می شود مرتبط می کند.
ViewModel ها به طور خودکار توسط سیستم از بین می روند زمانی که کاربر شما از فعالیت یا قطعه شما عقب نشینی می کند یا اگر finish()
فراخوانی کنید، به این معنی است که وضعیت همانطور که کاربر در این سناریوها انتظار دارد پاک می شود.
بر خلاف وضعیت نمونه ذخیره شده ، ViewModels در طی یک فرآیند آغاز شده توسط سیستم از بین می رود. برای بارگیری مجدد داده ها پس از مرگ فرآیند شروع شده در سیستم در یک ViewModel ، از API SavedStateHandle
استفاده کنید. از طرف دیگر، اگر دادهها مربوط به UI هستند و نیازی به نگهداری در ViewModel ندارند، از onSaveInstanceState()
در سیستم View یا rememberSaveable
در Jetpack Compose استفاده کنید. اگر داده ها داده های کاربردی است ، بهتر است که آن را به دیسک ادامه دهید.
اگر در حال حاضر یک راه حل در حافظه برای ذخیره وضعیت UI خود در سراسر تغییرات پیکربندی دارید ، ممکن است نیازی به استفاده از ViewModel نداشته باشید.
Use Saved instance state as backup to handle system-initiated process death
فراخوانی onSaveInstanceState()
در سیستم View، rememberSaveable
در Jetpack Compose، و SavedStateHandle
در ViewModels دادههای مورد نیاز برای بارگیری مجدد وضعیت یک کنترلکننده UI، مانند یک فعالیت یا یک قطعه، را ذخیره میکنند، در صورتی که سیستم آن کنترلر را از بین ببرد و بعداً دوباره ایجاد کند. برای یادگیری نحوه اجرای حالت نمونه ذخیره شده با استفاده از onSaveInstanceState
، به ذخیره و بازیابی وضعیت فعالیت در راهنمای چرخه حیات فعالیت مراجعه کنید.
بستههای حالت نمونه ذخیرهشده هم از طریق تغییرات پیکربندی و هم از طریق مرگ فرآیند باقی میمانند، اما با ذخیرهسازی و سرعت محدود میشوند، زیرا APIهای مختلف دادهها را سریالسازی میکنند. Serialization can consume a lot of memory if the objects being serialized are complicated. از آنجایی که این فرآیند در طول تغییر پیکربندی روی رشته اصلی اتفاق میافتد، سریالسازی طولانی مدت میتواند باعث افت فریم و لکنت بصری شود.
از حالت نمونه ذخیره شده برای ذخیره مقادیر زیادی از داده ها، مانند بیت مپ، یا ساختارهای داده پیچیده که نیاز به سریال سازی طولانی مدت یا deserialization دارند، استفاده نکنید. Instead, store only primitive types and simple, small objects such as String
. به این ترتیب، از حالت ذخیرهشده برای ذخیره حداقل مقدار دادههای لازم، مانند شناسه، استفاده کنید تا دادههای لازم برای بازگرداندن رابط کاربری به حالت قبلیاش را دوباره ایجاد کنید، در صورتی که مکانیسمهای ماندگاری دیگر از کار بیفتند. Most apps should implement this to handle system-initiated process death.
Depending on your app's use cases, you might not need to use saved instance state at all. به عنوان مثال ، یک مرورگر ممکن است کاربر را قبل از خروج از مرورگر ، به صفحه وب دقیق مورد نظر خود برگرداند. اگر فعالیت شما از این طریق رفتار می کند ، می توانید با استفاده از حالت نمونه ذخیره شده را کنار بگذارید و در عوض همه چیز را به صورت محلی ادامه دهید.
علاوه بر این، زمانی که یک اکتیویتی را از یک intent باز می کنید، دسته ای از موارد اضافی هم زمانی که پیکربندی تغییر می کند و هم زمانی که سیستم فعالیت را بازیابی می کند به اکتیویتی تحویل داده می شود. اگر هنگام راهاندازی فعالیت، بخشی از دادههای وضعیت رابط کاربری، مانند عبارت جستجو، به عنوان یک هدف اضافی ارسال میشد، میتوانید از بستههای اضافی بهجای بسته حالت نمونه ذخیرهشده استفاده کنید. To learn more about intent extras, see Intent and Intent Filters .
در هر یک از این سناریوها، برای جلوگیری از هدر رفتن چرخه بارگیری مجدد داده ها از پایگاه داده در طول تغییر پیکربندی، همچنان باید از ViewModel
استفاده کنید.
در مواردی که داده های UI برای حفظ آن ساده و سبک است ، ممکن است از API های حالت ذخیره شده به تنهایی برای حفظ داده های حالت خود استفاده کنید.
با استفاده از SavedStateRegistry به حالت ذخیره شده قلاب کنید
با شروع Fragment 1.1.0 یا وابستگی گذرا آن Activity 1.0.0 ، کنترلکنندههای UI، مانند Activity
یا Fragment
، SavedStateRegistryOwner
را پیادهسازی میکنند و یک SavedStateRegistry
ارائه میدهند که به آن کنترلکننده متصل است. SavedStateRegistry
allows components to hook into your UI controller's saved state to consume or contribute to it. For example, the Saved State module for ViewModel uses SavedStateRegistry
to create a SavedStateHandle
and provide it to your ViewModel
objects. You can retrieve the SavedStateRegistry
from within your UI controller by calling getSavedStateRegistry()
.
مؤلفه هایی که به حالت ذخیره شده کمک می کنند باید SavedStateRegistry.SavedStateProvider
را پیاده سازی کنند که یک متد واحد به نام saveState()
را تعریف می کند. The saveState()
method allows your component to return a Bundle
containing any state that should be saved from that component. SavedStateRegistry
calls this method during the saving state phase of the UI controller's lifecycle.
کاتلین
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
، می توانید پس از تماس با super.onCreate()
، یک SavedStateProvider
در 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); ... }
از پایداری محلی برای رسیدگی به فرآیند مرگ برای داده های پیچیده یا بزرگ استفاده کنید
ذخیرهسازی محلی دائمی، مانند پایگاه داده یا تنظیمات برگزیده مشترک، تا زمانی که برنامه شما روی دستگاه کاربر نصب شده باشد، باقی خواهد ماند (مگر اینکه کاربر دادههای برنامه شما را پاک کند). در حالی که چنین ذخیرهسازی محلی از فعالیتهای آغاز شده توسط سیستم و مرگ فرآیند برنامهها جان سالم به در میبرد، بازیابی آن میتواند گران باشد زیرا باید از حافظه محلی در حافظه خوانده شود. اغلب این حافظه محلی دائمی ممکن است بخشی از معماری برنامه شما باشد تا تمام داده هایی را که نمی خواهید در صورت باز و بسته کردن فعالیت از دست بدهید، ذخیره می کند.
Neither ViewModel nor saved instance state are long-term storage solutions and thus are not replacements for local storage, such as a database. Instead you should use these mechanisms for temporarily storing transient UI state only and use persistent storage for other app data. برای جزئیات بیشتر درباره نحوه استفاده از حافظه محلی برای حفظ طولانی مدت داده های مدل برنامه خود (مثلاً در راه اندازی مجدد دستگاه) به راهنمای معماری برنامه مراجعه کنید.
مدیریت وضعیت رابط کاربری: تقسیم کن و غلبه کن
You can efficiently save and restore UI state by dividing the work among the various types of persistence mechanisms. در بیشتر موارد، هر یک از این مکانیسمها باید نوع متفاوتی از دادههای مورد استفاده در فعالیت را ذخیره کنند، بر اساس معاوضه پیچیدگی داده، سرعت دسترسی و طول عمر:
- Local persistence: Stores all the application data you don't want to lose if you open and close the activity.
- Example: A collection of song objects, which could include audio files and metadata.
-
ViewModel
: Stores in memory all the data needed to display the associated UI, the screen UI state .- Example: The song objects of the most recent search and the most recent search query.
- Saved instance state: Stores a small amount of data needed to reload UI state if the system stops and then recreates the UI. به جای ذخیره اشیاء پیچیده در اینجا، اشیاء پیچیده را در حافظه محلی نگه دارید و یک شناسه منحصر به فرد برای این اشیاء در APIهای حالت نمونه ذخیره شده ذخیره کنید.
- مثال: ذخیره جدیدترین عبارت جستجو.
As an example, consider an activity that lets you to search through your library of songs. در اینجا نحوه برخورد با رویدادهای مختلف آمده است:
When the user adds a song, the ViewModel
immediately delegates persisting this data locally. If this newly added song should be shown in the UI, you should also update the data in the ViewModel
object to reflect the addition of the song. Remember to do all database inserts off of the main thread.
هنگامی که کاربر آهنگی را جستجو می کند، هر داده آهنگ پیچیده ای را که از پایگاه داده بارگیری می کنید، باید فوراً در شی ViewModel
به عنوان بخشی از حالت رابط کاربری صفحه ذخیره شود.
وقتی فعالیت به پسزمینه میرود و سیستم APIهای حالت نمونه ذخیرهشده را فراخوانی میکند، درخواست جستجو باید در حالت نمونه ذخیرهشده ذخیره شود، در صورتی که فرآیند دوباره ایجاد شود. Since the information is necessary to load application data persisted in this, store the search query in the ViewModel SavedStateHandle
. This is all the information you need to load the data and get the UI back into its current state.
حالت های پیچیده را بازیابی کنید: مونتاژ مجدد قطعات
When it is time for the user to return to the activity, there are two possible scenarios for recreating the activity:
- The activity is recreated after having been stopped by the system. سیستم درخواست را در یک بسته حالت نمونه ذخیره شده ذخیره می کند، و اگر از
SavedStateHandle
استفاده نمی شود، UI باید پرس و جو را بهViewModel
ارسال کند. TheViewModel
sees that it has no search results cached and delegates loading the search results using the given search query. - The activity is created after a configuration change. از آنجایی که نمونه
ViewModel
از بین نرفته است،ViewModel
تمام اطلاعات را در حافظه پنهان دارد و نیازی به جستجو مجدد از پایگاه داده ندارد.
منابع اضافی
To learn more about saving UI states, see the following resources.
وبلاگ ها
- ViewModels: یک مثال ساده
- ViewModels: Persistence,
onSaveInstanceState()
, Restoring UI State and Loaders - آزمایشگاه کد اجزای آگاه از چرخه حیات اندروید
برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- ماژول حالت ذخیره شده برای ViewModel
- Handling Lifecycles with Lifecycle-Aware Components
- نمای کلی ViewModel