حالت های رابط کاربری را ذخیره کنید

این راهنما در مورد انتظارات کاربر در مورد وضعیت UI و گزینه های موجود برای حفظ حالت بحث می کند.

ذخیره و بازیابی سریع حالت رابط کاربری یک فعالیت پس از اینکه سیستم فعالیت‌ها یا برنامه‌ها را از بین می‌برد، برای یک تجربه کاربری خوب ضروری است. کاربران انتظار دارند که دولت UI یکسان باشد ، اما این سیستم ممکن است فعالیت و وضعیت ذخیره شده آن را از بین ببرد.

برای ایجاد شکاف بین انتظارات کاربر و رفتار سیستم ، از ترکیبی از روشهای زیر استفاده کنید:

  • اشیاء ViewModel .
  • نمونه های ذخیره شده در زمینه های زیر:
  • ذخیره محلی برای ادامه وضعیت 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 ارسال کند. The ViewModel 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.

وبلاگ ها

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}