Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Создавайте приложения WinUI с помощью пакета SDK для приложений Windows, которые запускаются быстро за счёт сокращения работы в момент запуска, упрощения первого кадра и загрузки некритичных функций после становления окна интерактивным.
Рекомендации по повышению производительности запуска приложения
В частности, пользователи воспринимают, является ли ваше приложение быстрым или медленным в зависимости от того, сколько времени требуется для запуска. В этой статье время запуска приложения начинается, когда пользователь запускает приложение и заканчивается, когда пользователь может взаимодействовать с приложением в понятном виде. В этой статье приводятся рекомендации по повышению производительности запуска из приложения WinUI.
Измерение времени запуска приложения
Не забудьте запустить приложение несколько раз, прежде чем измерять время запуска. Это дает вам базовую линию для измерений и помогает убедиться, что вы измеряете максимально краткое и разумное время запуска.
Проведите измерения, которые отражают опыт конечного пользователя. Измерение выпуска строится на репрезентативном оборудовании, рассматривайте как холодный, так и теплый запуск, и сосредоточьтесь на времени до отображения первого интерактивного кадра, а не только на время до завершения процесса.
Отложить работу как можно дольше
Чтобы улучшить время запуска приложения, сделайте только работу, которую необходимо сделать, чтобы пользователь начал взаимодействовать с приложением. Это может быть особенно полезно, если можно отложить загрузку дополнительных сборок. Среда выполнения общего языка (англ. CLR) загружает сборку при первом использовании. Если вы можете свести к минимуму количество загруженных сборок, вы можете улучшить время запуска приложения и его потребление памяти.
Независимое выполнение длительной работы
Ваше приложение может быть интерактивным, даже если есть части приложения, которые не являются полностью функциональными. Например, если приложение отображает данные, которые занимает некоторое время для извлечения, этот код можно выполнить независимо от кода запуска приложения, извлекая данные асинхронно. Когда данные доступны, заполните пользовательский интерфейс приложения данными.
Многие API, которые извлекают данные, асинхронны, поэтому вы, вероятно, будете получать данные асинхронно. Дополнительные сведения см. в статье Асинхронное программирование с использованием async и await. Если вы работаете, не использующие асинхронные API, можно использовать Task класс для выполнения длительных работ, чтобы не блокировать взаимодействие пользователя с приложением. Это позволяет приложению реагировать во время загрузки данных.
Если ваше приложение занимает особенно много времени для загрузки части пользовательского интерфейса, рассмотрите возможность отображения сообщения в этой области, например "Получение последних данных", чтобы пользователи знали, что приложение по-прежнему обрабатывается.
Свести к минимуму время запуска
Для всех, кроме самых простых приложений, требуется достаточное время для загрузки ресурсов, анализа XAML, настройки структур данных и выполнения логики во время запуска. Для приложений WinUI он помогает думать о запуске на четырех этапах: запуск процесса, создание окна, создание главной страницы и макет и отрисовка первого кадра.
Период запуска — это время между моментом запуска приложения и моментом, когда приложение становится функциональным. Это критическое время, так как это первое впечатление пользователя о вашем приложении. Пользователи ожидают мгновенных и непрерывных отзывов от системы и от приложений. Система и приложение воспринимаются как сломанные или плохо разработанные, когда приложения не запускаются быстро.
Общие сведения о стадиях запуска
Запуск включает в себя ряд движущихся частей, и все из них должны быть согласованы для лучшего взаимодействия с пользователем. Следующие действия выполняются между запуском приложения и отображаемым содержимым приложения.
- Процесс запускается, и код запуска, созданный шаблоном, вызывает
Main. - Создается
Applicationобъект.- Конструктор приложения вызывает
InitializeComponent, что приводит к анализуApp.xamlи созданию объектов.
- Конструктор приложения вызывает
-
Application.OnLaunched вызывается.
- Код приложения создает главное окно, назначает исходное содержимое и вызывает
Activate. - Главный конструктор страницы вызывает
InitializeComponent, что приводит к анализу XAML страницы и созданию объектов.
- Код приложения создает главное окно, назначает исходное содержимое и вызывает
- Платформа XAML выполняет проход макета, включая измерение и размещение.
-
ApplyTemplateВызывает создание содержимого шаблона элемента управления для каждого элемента управления, что обычно является основной частью времени макета в процессе запуска.
-
- Отрисовка создает визуальные элементы для содержимого окна.
- Первый кадр представлен, а после запуска работа продолжается асинхронно.
Делайте меньше на пути развития вашего стартапа
Держите путь выполнения кода на старте свободным от всего, что не требуется для первого кадра.
- Если у вас есть пользовательские DLL, содержащие элементы управления, которые не нужны во время первого кадра, рассмотрите возможность их отложенной загрузки.
- Если у вас есть часть пользовательского интерфейса, которая зависит от данных из облака, разделите этот пользовательский интерфейс. Сначала откройте пользовательский интерфейс, который не зависит от облачных данных, а затем асинхронно откройте пользовательский интерфейс, зависящий от облака. Кроме того, следует рассмотреть возможность кэширования данных локально, чтобы приложение работало в автономном режиме или не влияло на медленное сетевое подключение.
- Показать индикатор прогресса, если ваш пользовательский интерфейс ожидает данных.
- Будьте осторожны с проектами приложений, которые включают много синтаксического анализа файлов конфигурации или пользовательского интерфейса, который динамически создается кодом.
Сокращение количества элементов
Производительность запуска в приложении XAML напрямую сопоставляется с количеством элементов, создаваемых во время запуска. Чем меньше элементов вы создаете, тем меньше времени, которое потребуется для запуска приложения. В качестве ориентировочного ориентира, рассмотрим, что создание каждого элемента занимает 1 мс.
- Шаблоны, используемые в элементах управления, могут оказать наибольшее влияние, так как они повторяются несколько раз. См. статью "Оптимизация пользовательского интерфейса ListView и GridView".
- Пользовательские элементы управления и шаблоны элементов управления расширяются, поэтому их также следует учитывать.
- Если вы создаете код XAML, который не отображается на экране, следует оправдать, следует ли создавать эти части XAML во время запуска.
В окне визуального дерева Visual Studio Live отображаются количество дочерних элементов для каждого узла в дереве.
Используйте отсрочку. Сворачивание элемента или установка его прозрачности на 0 не препятствует созданию элемента. С помощью x:Load или x:DeferLoadStrategyможно отложить загрузку части пользовательского интерфейса и загрузить ее при необходимости. Это хороший способ отложить обработку пользовательского интерфейса, который не отображается во время запуска, чтобы его можно было загрузить при необходимости или в составе набора отложенной логики. Чтобы активировать загрузку, необходимо только вызвать FindName для элемента. Пример и дополнительные сведения см. в атрибуте x:Load и атрибуте x:DeferLoadStrategy.
Виртуализация. Если в пользовательском интерфейсе есть список или повторяющийся контент, крайне рекомендуется использовать виртуализацию интерфейса. Если пользовательский интерфейс списка не виртуализирован, вы несёте затраты на создание всех элементов заранее и можете замедлять запуск. См. статью "Оптимизация пользовательского интерфейса ListView и GridView".
Производительность приложения — это не только чистая производительность; это также касается восприятия. Изменение порядка операций таким образом, чтобы визуальные элементы отображались в первую очередь, может создать у пользователя ощущение, что приложение работает быстрее. Пользователи считают приложение загруженным, когда содержимое находится на экране. Приложения, как правило, должны выполнять несколько задач во время запуска, и не все из них необходимы для отображения пользовательского интерфейса, поэтому такие действия можно отложить или им следует присвоить более низкий приоритет по сравнению с интерфейсом.
В этой статье рассказывается о первом кадре, который исходит от анимации и видео терминологии и является мерой того, сколько времени он занимает до тех пор, пока содержимое не будет видно конечным пользователем.
Улучшение восприятия запуска
Давайте рассмотрим пример простой онлайн-игры, чтобы определить каждый этап запуска и различные методы для предоставления отзывов пользователей на протяжении всего процесса.
На первом этапе процесс запускается и приложение создает его окно. В это время пользователь еще не видел собственного содержимого приложения. Ваша цель — быстро получить упрощенное окно на экране.
Второй этап включает создание и инициализацию структур, критически важных для игры. Если приложение может быстро создать свой исходный пользовательский интерфейс с данными, доступными при запуске, этот этап является тривиальным, и вы можете сразу отобразить пользовательский интерфейс. В противном случае отобразится упрощенная страница загрузки во время инициализации приложения.
Как будет выглядеть страница загрузки; это может быть так же просто, как отображение индикатора выполнения или кольца выполнения. Основной момент в том, что приложение показывает, что выполняет задачи, прежде чем оно становится полностью отзывчивым. В случае игры начальный экран требует, чтобы некоторые изображения и звуки загружались с диска в память. Эти задачи занимают некоторое время, поэтому приложение держит пользователя в курсе, показывая страницу загрузки с простой анимацией, связанной с темой игры.
Третий этап начинается после того, как игра имеет минимальный набор сведений для создания интерактивного пользовательского интерфейса, который заменяет страницу загрузки. На этом этапе единственными сведениями, доступными для онлайн-игры, может быть содержимое, загруженное приложением с диска. Игра может отправлять достаточно содержимого, чтобы создать интерактивный пользовательский интерфейс, но потому что это онлайн-игра она не будет полностью функциональной, пока она не подключается к Интернету и скачивает некоторую дополнительную информацию. Пока он не будет иметь все необходимые сведения, пользователь может взаимодействовать с пользовательским интерфейсом, но функции, требующие дополнительных данных из Интернета, должны дать отзыв о том, что содержимое по-прежнему загружается. Это может занять некоторое время, чтобы приложение стало полностью функциональным, поэтому важно, чтобы функциональные возможности были доступны как можно скорее.
Теперь, когда мы определили три этапа запуска в онлайн-игре, давайте связываем их с фактическим кодом.
Этап 1 и этап 2
Используйте конструктор приложения только для инициализации структур данных, критически важных для приложения. Продолжайте сосредотачиваться OnLaunched на быстром создании первого окна, назначении упрощенного содержимого и активации окна, чтобы приложение мгновенно отображало обратную связь.
public partial class App : Application
{
public static Window MainWindow { get; private set; } = null!;
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
base.OnLaunched(args);
MainWindow = new MainWindow();
MainWindow.Content = new LoadingPage();
MainWindow.Activate();
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
// Asynchronously restore state and load the minimum data needed
// to create the first interactive UI.
await LoadInitialDataAsync();
MainWindow.Content = new GameHomePage();
}
private static Task LoadInitialDataAsync()
{
// Download data to populate the initial UI.
return Task.CompletedTask;
}
}
Одной из ключевых задач в OnLaunched является создание пользовательского интерфейса, назначение его в Window.Content, а затем вызов Window.Activate. Если вам требуется больше одного потока активации, придерживайтесь того же принципа: быстро показывайте упрощенное содержимое и перемещайте ресурсоемкие задачи за пределы критического пути запуска.
Приложения, отображающие страницу загрузки во время запуска, могут начать работу, чтобы создать основной пользовательский интерфейс в фоновом режиме. После создания этого элемента происходит событие FrameworkElement.Loaded . В обработчике событий можно заменить содержимое окна, которое в настоящее время является экраном загрузки, на только что созданную домашнюю страницу.
Важно, чтобы приложение с длительным периодом инициализации отображало страницу загрузки. Помимо предоставления отзывов о процессе запуска, окно должно быстро открываться, чтобы пользователи видели, что приложение успешно запускается.
partial class GameHomePage : Page
{
public GameHomePage()
{
InitializeComponent();
// Add a handler to be called when the home page has been loaded.
Loaded += GameHomePageLoaded;
// Load the minimal amount of image and sound data from disk necessary
// to create the home page.
}
private void GameHomePageLoaded(object sender, RoutedEventArgs e)
{
// Set the content of the main window to the home page now that it's
// ready to be displayed.
App.MainWindow.Content = this;
}
}
Фаза 3
Просто потому, что приложение отобразило пользовательский интерфейс, не означает, что оно полностью готово к использованию. В нашей игре пользовательский интерфейс отображается с заполнителями, предназначенными для функций, которым требуются данные из интернета. На этом этапе игра скачивает дополнительные данные, необходимые для обеспечения полной функциональности приложения и постепенно включает функции по мере получения данных.
Иногда большая часть содержимого, необходимого для запуска, может быть упаковано с помощью приложения. Так обстоит дело с простой игрой. Это делает процесс запуска довольно простым. Но многие программы, такие как читатели новостей и фотозрители, должны извлекать информацию из Интернета, чтобы стать функциональной. Эти данные могут быть большими и могут занять достаточное время для скачивания. Как приложение получает эти данные во время запуска, может оказать огромное влияние на воспринимаемую производительность.
Приложение может слишком долго отображать страницу загрузки, если оно пытается скачать весь набор данных, необходимый для работы, на первом или втором этапе запуска. Это заставляет приложение выглядеть зависшим. Рекомендуется скачать минимальное количество данных, необходимых для отображения интерактивного пользовательского интерфейса с элементами заполнителя на этапе 2, а затем постепенно загружать данные, заменяющие элементы заполнителя на этапе 3. Дополнительные сведения о работе с данными см. в статье "Оптимизация ListView" и GridView.
Как именно приложение реагирует на каждый этап запуска полностью до вас, но предоставление пользователю максимальной обратной связи с помощью упрощенного начального пользовательского интерфейса, загрузки экранов и прогрессивной загрузки данных позволяет приложению чувствовать себя быстрее.
Минимизируйте количество управляемых сборок в пути запуска
Многократно используемый код часто поставляется в виде модулей (DLL), включенных в проект. Для загрузки этих модулей требуется доступ к диску, и стоимость может увеличиваться. Это имеет наибольшее влияние на холодный запуск, но это может повлиять на теплый запуск тоже. В приложениях .NET среда CLR пытается отложить затраты ресурсов настолько, насколько это возможно, загружая сборки по мере необходимости. То есть среда CLR не загружает модуль, пока не ссылается на нее выполненный метод. Поэтому ссылайтесь только на сборки, необходимые для запуска приложения в коде запуска, чтобы среда CLR не загружала ненужные модули. Если у вас есть неиспользуемые пути кода в пути запуска с ненужными ссылками, переместите эти пути кода на другие методы, чтобы избежать ненужных загрузок.
Другим способом снижения нагрузки модуля является объединение модулей приложений. Загрузка одной большой сборки обычно занимает меньше времени, чем загрузка двух небольших. Это не всегда возможно, и вы должны объединять модули только в том случае, если это не имеет существенного значения для производительности разработчика или повторного использования кода. Вы можете использовать такие средства, как PerfView или Анализатор производительности Windows (WPA), чтобы узнать, какие модули загружаются при запуске.
Создание смарт-веб-запросов
Вы можете значительно улучшить время загрузки приложения, упаковав его содержимое локально, включая XAML, изображения и другие файлы, важные для приложения. Операции с дисками быстрее, чем сетевые операции. Если приложению требуется определенный файл при инициализации, можно сократить общее время запуска, загрузив его с диска вместо извлечения из удаленного сервера.
Эффективное ведение журнала и кэширование страниц
Элемент Frame управления предоставляет функции навигации. Он предлагает навигацию на страницу (Navigateметод), журнал навигации (BackStackи свойства ForwardStack и GoForwardGoBack методы), кэширование страниц (Page.NavigationCacheMode), а также поддержку сериализации (GetNavigationStateметод).
Производительность, о которой следует знать с Frame, в основном касается ведения журнала и кэширования страниц.
Кадровая журнализация. При перемещении на страницу с Frame.Navigate объект PageStackEntry для текущей страницы добавляется в коллекцию Frame.BackStack.
PageStackEntry является относительно небольшим, но встроенные ограничения на размер BackStack коллекции отсутствуют. Возможно, пользователь может перемещаться в цикле и увеличивать эту коллекцию на неопределенный срок.
Элемент PageStackEntry также включает параметр, переданный методу Frame.Navigate. Рекомендуется, чтобы этот параметр был примитивным сериализуемым типом, например int или string, чтобы метод Frame.GetNavigationState работал. Но этот параметр может ссылаться на объект, который рассчитывает более значительные объемы рабочего набора или других ресурсов, что делает каждую запись в BackStack намного более дорогой. Например, вы можете использовать StorageFile в качестве параметра, и, следовательно, BackStack может поддерживать неограниченное количество открытых файлов.
Поэтому рекомендуется сохранить параметры навигации небольшими и ограничить размер.BackStack Это BackStack стандартная коллекция в C#, поэтому ее можно просто обрезать, удалив записи.
Кэширование страниц. По умолчанию при переходе на страницу с Frame.Navigate методом создается экземпляр новой страницы. Аналогичным образом, если вы Frame.GoBack вернетесь на предыдущую страницу, создается новый экземпляр предыдущей страницы.
Frame также предлагает опциональный страничный кэш, который может избежать этих инициализаций. Чтобы получить страницу, помещенную в кэш, используйте Page.NavigationCacheMode свойство. Установка этого режима в Required приводит к принудительному кэшированию страницы, в то время как установка в Enabled позволяет ее кэшировать. По умолчанию размер кэша составляет 10 страниц, но это может быть переопределено свойством Frame.CacheSize . Все Required страницы кэшируются, а если страниц меньше чем CacheSize, необходимых, то можно кэшировать также Enabled страниц.
Кэширование страниц может улучшить производительность, избегая создания новых экземпляров, что в свою очередь повышает быстродействие навигации. Кэширование страниц может снизить производительность путем чрезмерного кэширования и, следовательно, влияния на рабочий набор.
Поэтому рекомендуется использовать кэширование страниц в соответствии с вашим приложением. Например, предположим, что у вас есть приложение, отображающее список элементов в Frame, и при выборе элемента происходит переход на страницу с подробностями этого элемента. Вероятно, страницу списка следует кэшировать. Если страница сведений одинакова для всех элементов, она, вероятно, должна быть кэширована. Но если страница сведений является более разнородной, возможно, лучше оставить кэширование отключенным.
Windows developer