Рекомендации по повышению производительности запуска приложения

Создавайте приложения универсальная платформа Windows (UWP) с оптимальным временем запуска, повышая способ запуска и активации.

Рекомендации по повышению производительности запуска приложения

В частности, пользователи воспринимают, является ли ваше приложение быстрым или медленным в зависимости от того, сколько времени требуется для запуска. В этой статье время запуска приложения начинается, когда пользователь запускает приложение, и заканчивается, когда пользователь может взаимодействовать с приложением в некотором смысле. В этом разделе приводятся рекомендации по повышению производительности приложения при запуске.

Измерение времени запуска приложения

Не забудьте запустить приложение несколько раз, прежде чем измерять время запуска. Это дает базовые показатели для измерения и гарантирует, что вы измеряете как можно меньше времени запуска.

По истечении времени, когда приложение UWP прибывает на компьютеры клиентов, приложение компилируется с помощью цепочки инструментов .NET Native. .NET Native — это технология компиляции перед временем, которая преобразует MSIL в машинный код с возможностью запуска в собственном коде. Собственные приложения .NET запускают быстрее, используют меньше памяти и используют меньше батареи, чем их коллеги MSIL. Приложения, созданные с помощью статической связи .NET Native в пользовательской среде выполнения и новой конвергентной .NET Core, которая может работать на всех устройствах, поэтому они не зависят от встроенной реализации .NET. На компьютере разработки приложение по умолчанию использует .NET Native, если вы создаете его в режиме выпуска, и использует CoreCLR, если вы создаете его в режиме отладки. Вы можете настроить этот параметр в Visual Studio на странице сборки в разделе "Свойства" (C#) или в разделе "Компиляция" -> "Расширенные" в "Мой проект" (VB). Найдите поле проверка box с надписью "Компиляция с помощью цепочки инструментов .NET Native".

Конечно, следует принимать измерения, которые представляют собой то, что будет иметь конечный пользователь. Таким образом, если вы не уверены, что компилируете приложение в машинный код на компьютере разработки, вы можете запустить средство "Генератор образов машинного кода" (Ngen.exe), чтобы предварительно компилировать приложение, прежде чем измерять время запуска.

В следующей процедуре описывается, как запустить Ngen.exe для предварительной компиляции приложения.

Запуск Ngen.exe

  1. Запустите приложение по крайней мере один раз, чтобы убедиться, что Ngen.exe обнаруживает его.

  2. Откройте планировщик задач, выполнив одно из следующих действий:

    • Найдите "Планировщик задач" на начальном экране.
    • Запустите taskschd.msc.
  3. В левой области планировщика задач разверните библиотеку планировщика задач.

  4. Разверните корпорацию Майкрософт.

  5. Разверните Windows.

  6. Выберите платформа .NET Framework.

  7. Выберите платформа .NET Framework NGEN 4.x из списка задач.

    При использовании 64-разрядного компьютера также существует платформа .NET Framework NGEN версии 4.x 64. Если вы создаете 64-разрядное приложение, выберите .NET Framework NGEN версии 4.x 64.

  8. В меню "Действие " нажмите кнопку "Выполнить".

Ngen.exe предварительно компилирует все приложения на компьютере, которые использовались и не имеют собственных образов. Если есть много приложений, которые необходимо предварительно компилировать, это может занять много времени, но последующие запуски гораздо быстрее.

При повторной компиляции приложения собственный образ больше не используется. Вместо этого приложение компилируется jit-in-time, что означает, что он компилируется при запуске приложения. Чтобы получить новый собственный образ, необходимо повторно запустить Ngen.exe.

Отложить работу как можно дольше

Чтобы уменьшить время запуска приложения, выполняйте только самые необходимые действия, обеспечивающие пользователю возможность приступить к работе с приложением. Это может быть особенно полезно, если можно отложить загрузку дополнительных сборок. Среда CLR загружает сборку при первом использовании. Если вы можете свести к минимуму количество загруженных сборок, вы можете улучшить время запуска приложения и его потребление памяти.

Независимое выполнение длительной работы

Ваше приложение может быть интерактивным, даже если есть части приложения, которые не являются полностью функциональными. Например, если приложение отображает данные, которые занимает некоторое время для извлечения, этот код можно выполнить независимо от кода запуска приложения, извлекая данные асинхронно. Когда данные доступны, заполните пользовательский интерфейс приложения данными.

Многие API-интерфейсы универсальная платформа Windows (UWP), которые извлекают данные, асинхронны, поэтому вы, вероятно, будете получать данные асинхронно. Дополнительные сведения об асинхронных API см. в статье "Вызов асинхронных API" в C# или Visual Basic. Если вы работаете, не использующие асинхронные API, можно использовать класс задач для выполнения длительной работы, чтобы не блокировать взаимодействие пользователя с приложением. Это позволит приложению реагировать на пользователя во время загрузки данных.

Если приложение занимает особенно много времени, чтобы загрузить часть пользовательского интерфейса, попробуйте добавить строку в эту область, которая говорит примерно следующее: "Получение последних данных", чтобы пользователи знали, что приложение по-прежнему обрабатывается.

Свести к минимуму время запуска

Для загрузки ресурсов, синтаксического анализа XAML, настройки структур данных и выполнения логики при активации требуются все, кроме простых приложений. Здесь мы анализируем процесс активации, разбивая его на три этапа. Мы также предоставляем советы по сокращению времени, затраченного на каждом этапе, и методов для создания каждого этапа запуска приложения более понятным для пользователя.

Период активации — это время между моментом запуска приложения пользователем и моментом работы приложения. Это критическое время, так как это первое впечатление пользователя о вашем приложении. Они ожидают мгновенных и непрерывных отзывов от системы и приложений. Система и приложение воспринимаются как сломанные или плохо разработанные, когда приложения не запускаются быстро. Еще хуже, если приложение занимает слишком много времени для активации, диспетчер времени существования процессов (PLM) может убить его, или пользователь может удалить его.

Общие сведения о стадиях запуска

Запуск включает в себя ряд движущихся частей, и все из них должны быть правильно согласованы для лучшего взаимодействия с пользователем. Следующие действия выполняются между пользователем, щелкнув плитку приложения и отображаемое содержимое приложения.

  • Оболочка Windows запускает процесс и вызывается Main.
  • Создается объект Application.
    • (Шаблон проекта) Конструктор вызывает InitializeComponent, что приводит к анализу App.xaml и созданию объектов.
  • Вызывается событие Application.OnLaunched.
    • (ProjectTemplate) Код приложения создает кадр и переходит в MainPage.
    • (ProjectTemplate) Конструктор Mainpage вызывает InitializeComponent, что приводит к синтаксическому анализу и созданию объектов MainPage.xaml.
    • Вызывается Окно.Current.Activate() ProjectTemplate.
  • Платформа XAML выполняет передачу макета, включая меру и упорядочение.
    • ApplyTemplate приведет к созданию содержимого шаблона элемента управления для каждого элемента управления, что обычно является основной частью времени запуска макета.
  • Вызывается отрисовка для создания визуальных элементов для всего содержимого окна.
  • Кадр представлен в диспетчере windows для настольных компьютеров (DWM).

Меньше в пути запуска

Не закрывайте путь к коду запуска от всего, что не требуется для первого кадра.

  • Если у вас есть пользовательские библиотеки DLL, содержащие элементы управления, которые не нужны во время первого кадра, рассмотрите возможность задержки их загрузки.
  • Если у вас есть часть пользовательского интерфейса, зависящая от данных из облака, разделите этот пользовательский интерфейс. Во-первых, откройте пользовательский интерфейс, который не зависит от облачных данных и асинхронно выводит пользовательский интерфейс, зависящий от облака. Кроме того, следует рассмотреть возможность кэширования данных локально, чтобы приложение работало в автономном режиме или не влияло на медленное сетевое подключение.
  • Отображение пользовательского интерфейса выполнения, если пользовательский интерфейс ожидает данных.
  • Будьте осторожны с проектами приложений, которые включают много синтаксического анализа файлов конфигурации или пользовательского интерфейса, который динамически создается кодом.

Сокращение количества элементов

Производительность запуска в приложении XAML напрямую сопоставляется с количеством элементов, создаваемых во время запуска. Чем меньше элементов вы создаете, тем меньше времени, которое потребуется для запуска приложения. В качестве грубого теста рассмотрим каждый элемент, чтобы создать 1 мс.

  • Шаблоны, используемые в элементах управления, могут оказать наибольшее влияние, так как они повторяются несколько раз. См . статью "Оптимизация пользовательского интерфейса ListView и GridView".
  • Пользовательские элементы управления и шаблоны элементов управления будут развернуты, поэтому их также следует учитывать.
  • Если вы создаете код XAML, который не отображается на экране, то следует оправдать, следует ли создавать эти части XAML во время запуска.

В окне визуального дерева Visual Studio Live отображаются количество дочерних элементов для каждого узла в дереве.

Live visual tree.

Использование отсрочки. Сворачивание элемента или установка его непрозрачности в значение 0 не будет препятствовать созданию элемента. С помощью атрибута x:Load или x:DeferLoadStrategy можно отложить загрузку элемента пользовательского интерфейса и загрузить его, когда это потребуется. Это хороший способ отложить обработку пользовательского интерфейса, который не отображается во время запуска экрана, чтобы вы могли загрузить его при необходимости или как часть набора отложенной логики. Чтобы активировать загрузку, необходимо вызвать только FindName для элемента. Примеры и дополнительные сведения см. в разделах Атрибут x:Load и Атрибут x:DeferLoadStrategy.

Виртуализация. Если в пользовательском интерфейсе есть список или содержимое повтора, настоятельно рекомендуется использовать виртуализацию пользовательского интерфейса. Если пользовательский интерфейс списка не виртуализирован, вы оплачиваете затраты на создание всех элементов, которые могут замедлить запуск. См . статью "Оптимизация пользовательского интерфейса ListView и GridView".

Производительность приложений не только о производительности необработанных данных, но и о представлении. Изменение порядка операций таким образом, чтобы визуальные аспекты выполнялись сначала, как правило, пользователь чувствует, что приложение быстрее. Пользователи будут рассматривать приложение, загруженное при появлении содержимого на экране. Как правило, приложения должны выполнять несколько действий в рамках запуска, а не все это необходимо для создания пользовательского интерфейса, поэтому их следует отложить или указать приоритет ниже, чем в пользовательском интерфейсе.

В этом разделе рассказывается о "первом кадре", который поступает из анимации или телевизора, и является мерой того, сколько времени до тех пор, пока содержимое не будет видно конечным пользователем.

Улучшение восприятия запуска

Давайте рассмотрим пример простой онлайн-игры, чтобы определить каждый этап запуска и различные методы для предоставления отзывов пользователей на протяжении всего процесса. В этом примере первый этап активации — это время между нажатием плитки игры и игрой, начинающейся с его кода. В течение этого времени система не содержит содержимого, отображаемого пользователю, чтобы даже указать, что запущена правильная игра. Но предоставление экрана-заставки дает это содержимое системе. Затем игра сообщает пользователю, что первый этап активации завершен путем замены статического экрана-заставки собственным пользовательским интерфейсом при запуске кода.

Второй этап активации включает создание и инициализацию структур, критически важных для игры. Если приложение может быстро создать свой исходный пользовательский интерфейс с данными, доступными после первого этапа активации, второй этап является тривиальным, и вы можете сразу отобразить пользовательский интерфейс. В противном случае рекомендуется отобразить страницу загрузки во время инициализации приложения.

Как выглядит страница загрузки, и это может быть так же просто, как отображение индикатора хода выполнения или кольца выполнения. Ключевой точкой является то, что приложение указывает на то, что оно выполняет задачи, прежде чем реагировать. В случае игры он хотел бы отобразить свой начальный экран, но этот пользовательский интерфейс требует, чтобы некоторые изображения и звуки загружались с диска в память. Эти задачи занимают несколько секунд, поэтому приложение информирует пользователя, заменив экран-заставку на страницу загрузки, которая показывает простую анимацию, связанную с темой игры.

Третий этап начинается после того, как игра имеет минимальный набор сведений для создания интерактивного пользовательского интерфейса, который заменяет страницу загрузки. На этом этапе единственная информация, доступная для онлайн-игры, — это содержимое, загруженное приложением с диска. Игра может отправлять достаточно содержимого для создания интерактивного пользовательского интерфейса; но потому что это онлайн-игра она не будет функциональной, пока она не подключается к Интернету и скачивает некоторую дополнительную информацию. До тех пор, пока она не будет работать, пользователь может взаимодействовать с пользовательским интерфейсом, но функции, требующие дополнительных данных из Интернета, должны дать отзыв о том, что содержимое по-прежнему загружается. Это может занять некоторое время, чтобы приложение стало полностью функциональным, поэтому важно, чтобы функциональные возможности были доступны как можно скорее.

Теперь, когда мы определили три этапа активации в онлайн-игре, давайте связываем их с фактическим кодом.

Фаза 1

Перед началом работы приложения необходимо сообщить системе, что она хочет отобразить в качестве экрана-заставки. Это делает, предоставляя цвет изображения и фона элементу SplashScreen в манифесте приложения, как в примере. Windows отображает это после начала активации приложения.

<Package ...>
  ...
  <Applications>
    <Application ...>
      <VisualElements ...>
        ...
        <SplashScreen Image="Images\splashscreen.png" BackgroundColor="#000000" />
        ...
      </VisualElements>
    </Application>
  </Applications>
</Package>

Дополнительные сведения см. в разделе "Добавление экрана-заставки".

Используйте конструктор приложения только для инициализации структур данных, критически важных для приложения. Конструктор вызывается только при первом запуске приложения и не обязательно каждый раз при активации приложения. Например, конструктор не вызывается для запуска приложения, размещенного в фоновом режиме, а затем активируется с помощью контракта поиска.

Фаза 2

Существует ряд причин активации приложения, каждое из которых может потребоваться обрабатывать по-разному. Вы можете переопределить OnActivated, OnCachedFileUpdaterActivated, OnFileActivated, OnFileOpenPickerActivated, OnFileSavePickerActivated, OnLaunched, OnSearchActivated и OnShareTargetActivated для обработки каждой причины активации. Одно из действий приложения, которое необходимо сделать в этих методах, заключается в создании пользовательского интерфейса, назначении его в Window.Content, а затем вызовите Window.Activate. На этом этапе экран-заставка заменяется пользовательским интерфейсом, созданным приложением. Этот визуальный элемент может быть загрузкой экрана или фактическим пользовательским интерфейсом приложения, если достаточно сведений при активации для его создания.

public partial class App : Application
{
    // A handler for regular activation.
    async protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        // Asynchronously restore state based on generic launch.

        // Create the ExtendedSplash screen which serves as a loading page while the
        // reader downloads the section information.
        ExtendedSplash eSplash = new ExtendedSplash();

        // Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash;

        // Notify the Window that the process of activation is completed
        Window.Current.Activate();
    }

    // a different handler for activation via the search contract
    async protected override void OnSearchActivated(SearchActivatedEventArgs args)
    {
        base.OnSearchActivated(args);

        // Do an asynchronous restore based on Search activation

        // the rest of the code is the same as the OnLaunched method
    }
}

partial class ExtendedSplash : Page
{
    // This is the UIELement that's the game's home page.
    private GameHomePage homePage;

    public ExtendedSplash()
    {
        InitializeComponent();
        homePage = new GameHomePage();
    }

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    {
    }
}
    Partial Public Class App
    Inherits Application

    ' A handler for regular activation.
    Protected Overrides Async Sub OnLaunched(ByVal args As LaunchActivatedEventArgs)
        MyBase.OnLaunched(args)

        ' Asynchronously restore state based on generic launch.

        ' Create the ExtendedSplash screen which serves as a loading page while the
        ' reader downloads the section information.
        Dim eSplash As New ExtendedSplash()

        ' Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash

        ' Notify the Window that the process of activation is completed
        Window.Current.Activate()
    End Sub

    ' a different handler for activation via the search contract
    Protected Overrides Async Sub OnSearchActivated(ByVal args As SearchActivatedEventArgs)
        MyBase.OnSearchActivated(args)

        ' Do an asynchronous restore based on Search activation

        ' the rest of the code is the same as the OnLaunched method
    End Sub
End Class

Partial Friend Class ExtendedSplash
    Inherits Page

    Public Sub New()
        InitializeComponent()

        ' Downloading the data necessary for
        ' initial UI on a background thread.
        Task.Run(Sub() DownloadData())
    End Sub

    Private Sub DownloadData()
        ' Download data to populate the initial UI.

        ' Create the first page.
        Dim firstPage As New MainPage()

        ' Add the data just downloaded to the first page

        ' Replace the loading page, which is currently
        ' set as the window's content, with the initial UI for the app
        Window.Current.Content = firstPage
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class

Приложения, отображающие страницу загрузки в обработчике активации, начинают работу, чтобы создать пользовательский интерфейс в фоновом режиме. После создания этого элемента происходит событие FrameworkElement.Loaded. В обработчике событий вы заменяете содержимое окна, которое в настоящее время является экраном загрузки, на только что созданную домашнюю страницу.

Важно, чтобы приложение с расширенным периодом инициализации отображалось страницу загрузки. Помимо предоставления отзывов пользователей о процессе активации, процесс будет завершен, если Window.Activate не вызывается в течение 15 секунд после начала процесса активации.

partial class GameHomePage : Page
{
    public GameHomePage()
    {
        InitializeComponent();

        // add a handler to be called when the home page has been loaded
        this.Loaded += ReaderHomePageLoaded;

        // load the minimal amount of image and sound data from disk necessary to create the home page.
    }

    void ReaderHomePageLoaded(object sender, RoutedEventArgs e)
    {
        // set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = this;
    }

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    {
    }
}
    Partial Friend Class GameHomePage
    Inherits Page

    Public Sub New()
        InitializeComponent()

        ' add a handler to be called when the home page has been loaded
        AddHandler Me.Loaded, AddressOf ReaderHomePageLoaded

        ' load the minimal amount of image and sound data from disk necessary to create the home page.
    End Sub

    Private Sub ReaderHomePageLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = Me
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class

Пример использования расширенных экранов-заставок см . в примере экрана-заставки.

Фаза 3

Просто потому, что приложение, отображаемое в пользовательском интерфейсе, не означает, что он полностью готов к использованию. В случае нашей игры пользовательский интерфейс отображается с заполнителями для функций, требующих данных из Интернета. На этом этапе игра скачивает дополнительные данные, необходимые для обеспечения полнофункциональным и прогрессивным включением функций по мере получения данных.

Иногда большая часть содержимого, необходимого для активации, может быть упаковано с помощью приложения. Это так с простой игрой. Это делает процесс активации довольно простым. Но многие программы (такие как читатели новостей и фотозрители) должны извлекать информацию из Интернета, чтобы стать функциональными. Эти данные могут быть большими и занять достаточное время для скачивания. Как приложение получает эти данные во время процесса активации, может оказать огромное влияние на воспринимаемую производительность приложения.

Вы можете отобразить страницу загрузки или, что еще хуже, экран-заставку в течение нескольких минут, если приложение попыталось скачать весь набор данных, который он нуждается в функциональных возможностях на этапе одной или двух активаций. Это делает приложение похожим на то, что оно зависло или вызвало его завершение системой. Мы рекомендуем приложению скачать минимальный объем данных для отображения интерактивного пользовательского интерфейса с элементами заполнителя на этапе 2, а затем постепенно загружать данные, заменяющие элементы заполнителя на этапе 3. Дополнительные сведения о работе с данными см. в статье "Оптимизация ListView" и GridView.

Как именно приложение реагирует на каждый этап запуска полностью до вас, но предоставление пользователю максимальной обратной связи (экран-заставка, экран загрузки, пользовательский интерфейс во время загрузки данных) заставляет пользователя чувствовать себя как будто приложение, и система в целом быстро.

Минимизация управляемых сборок в пути запуска

Многократно используемый код часто поставляется в виде модулей (DLL), включенных в проект. Загрузка этих модулей требует доступа к диску, и, как вы можете себе представить, затраты на это могут добавиться. Это имеет наибольшее влияние на холодный запуск, но он может иметь влияние на теплый запуск, тоже. В случае C# и Visual Basic среда CLR пытается отложить такую стоимость, как можно больше, загрузив сборки по запросу. То есть среда CLR не загружает модуль, пока не ссылается на нее выполненный метод. Таким образом, ссылайтесь только на сборки, необходимые для запуска приложения в коде запуска, чтобы среда CLR не загружала ненужные модули. Если у вас есть неиспользуемые пути кода в пути запуска с ненужными ссылками, эти пути кода можно переместить в другие методы, чтобы избежать ненужных нагрузок.

Другим способом уменьшения нагрузки модулей является объединение модулей приложения. Загрузка одной большой сборки обычно занимает меньше времени, чем загрузка двух небольших. Это не всегда возможно, и вы должны объединять модули только в том случае, если это не имеет существенного значения для производительности разработчика или повторного использования кода. Вы можете использовать такие средства, как PerfView или Windows Анализатор производительности (WPA), чтобы узнать, какие модули загружаются при запуске.

Создание смарт-веб-запросов

Вы можете значительно улучшить время загрузки приложения, упаковав его содержимое локально, включая XAML, изображения и другие файлы, важные для приложения. Операции с дисками быстрее, чем сетевые операции. Если приложению требуется определенный файл при инициализации, можно сократить общее время запуска, загрузив его с диска вместо извлечения из удаленного сервера.

Журнал и кэш страницы эффективно

Элемент управления Frame предоставляет функции навигации. Он предлагает навигацию к методу Page (Navigate), журнал навигации (свойства BackStack/ForwardStack, метод GoForward/GoBack), кэширование страниц (Page.NavigationCacheMode) и поддержка сериализации (метод GetNavigationState).

Производительность, о которых следует знать с помощью Frame, в основном связана с ведением журнала и кэшированием страниц.

Ведение журнала кадров. При переходе на страницу с frame.Navigate() страница PageStackEntry для текущей страницы добавляется в коллекцию Frame.BackStack. PageStackEntry относительно мал, но встроенные ограничения на размер коллекции BackStack отсутствуют. Возможно, пользователь может перемещаться в цикле и увеличивать эту коллекцию на неопределенный срок.

PageStackEntry также включает параметр, переданный методу Frame.Navigate(). Рекомендуется, чтобы этот параметр был примитивным сериализуемым типом (например, int или string), чтобы разрешить работать метод Frame.GetNavigationState(). Но этот параметр может потенциально ссылаться на объект, который учитывает более значительные объемы рабочих наборов или других ресурсов, что делает каждую запись в BackStack гораздо дороже. Например, вы можете использовать служба хранилища File в качестве параметра, и, следовательно, BackStack сохраняет неограниченное количество файлов.

Поэтому рекомендуется сохранить параметры навигации небольшими и ограничить размер BackStack. BackStack — это стандартный вектор (IList в C#, Platform::Vector в C++/CX), поэтому его можно обрезать, просто удалив записи.

Кэширование страниц. По умолчанию при переходе на страницу с методом Frame.Navigate создается новый экземпляр страницы. Аналогичным образом при переходе на предыдущую страницу с frame.GoBack выделяется новый экземпляр предыдущей страницы.

Фрейм, однако, предлагает необязательный кэш страниц, который может избежать этих экземпляров. Чтобы получить страницу, помещенную в кэш, используйте свойство Page.NavigationCacheMode. Если для этого режима задано значение "Обязательный", страница будет кэширована, если параметр "Включено" позволит кэшировать ее. По умолчанию размер кэша составляет 10 страниц, но это может быть переопределено свойством Frame.CacheSize. Все обязательные страницы будут кэшироваться, и если страницы кэшируются меньше, чем cacheSize Обязательные страницы, можно кэшировать также.

Кэширование страниц может помочь в повышении производительности, избегая экземпляров и повышая производительность навигации. Кэширование страниц может снизить производительность путем чрезмерного кэширования и, следовательно, влияния на рабочий набор.

Поэтому рекомендуется использовать соответствующее кэширование страниц для приложения. Например, предположим, что у вас есть приложение, отображающее список элементов в кадре, и при касании элемента он перемещает кадр на страницу сведений для этого элемента. Страница списка, вероятно, должна быть задана в кэш. Если страница сведений одинакова для всех элементов, она, вероятно, должна быть кэширована. Но если страница сведений является более разнородной, возможно, лучше оставить кэширование отключенным.