Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Creare app WinUI con Windows App SDK che iniziano rapidamente riducendo il lavoro di avvio, semplificando il primo frame e caricando funzionalità non critiche dopo che la finestra è interattiva.
Procedure consigliate per le prestazioni di avvio dell'app
In parte, gli utenti percepiscono se l'app è veloce o lenta in base al tempo necessario per l'avvio. Ai fini di questo argomento, l'ora di avvio di un'app inizia quando l'utente avvia l'app e termina quando l'utente può interagire con l'app in modo significativo. Questo articolo fornisce suggerimenti su come ottenere prestazioni di avvio migliori da un'app WinUI.
Misurazione del tempo di avvio dell'app
Assicurati di avviare l'app alcune volte prima di misurare effettivamente il tempo di avvio. In questo modo si ottiene una linea di base per la misurazione e si garantisce di misurare il tempo di avvio ragionevolmente breve possibile.
Eseguire misurazioni rappresentative delle esperienze dell'utente finale. Misura il rilascio basandosi su hardware rappresentativo, osserva sia l'avvio a freddo che l'avvio a caldo, e si concentra sul tempo necessario per il primo fotogramma interattivo piuttosto che solo sul tempo fino a quando il processo non è attivo.
Rinviare il lavoro il più a lungo possibile
Per migliorare il tempo di avvio dell'app, eseguire solo il lavoro assolutamente necessario per consentire all'utente di iniziare a interagire con l'app. Ciò può essere particolarmente utile se è possibile ritardare il caricamento di moduli aggiuntivi. Il Common Language Runtime carica un assembly la prima volta che viene utilizzato. Se puoi ridurre al minimo il numero di assembly caricati, potresti essere in grado di migliorare il tempo di avvio dell'app e il relativo consumo di memoria.
Eseguire operazioni a esecuzione prolungata in modo indipendente
L'app può essere interattiva anche se ci sono parti dell'app che non sono completamente funzionanti. Ad esempio, se l'app visualizza i dati che richiedono un po' di tempo per il recupero, puoi eseguire il codice indipendentemente dal codice di avvio dell'app recuperando i dati in modo asincrono. Quando i dati sono disponibili, popolare l'interfaccia utente dell'app con i dati.
Molte API che recuperano i dati sono asincrone, quindi è probabile che i dati vengano recuperati in modo asincrono comunque. Per altre info, vedi Programmazione asincrona con async e await. Se si lavora che non usa API asincrone, è possibile usare la Task classe per eseguire operazioni a esecuzione prolungata in modo da non impedire all'utente di interagire con l'app. In questo modo l'app rimane reattiva durante il caricamento dei dati.
Se l'app richiede molto tempo per caricare parte dell'interfaccia utente, è consigliabile visualizzare un messaggio in tale area, ad esempio "Recupero di dati più recenti" in modo che gli utenti sappiano che l'app è ancora in fase di elaborazione.
Ridurre al minimo il tempo di avvio
Tutte le app più semplici richiedono una quantità di tempo percepibile per caricare risorse, analizzare XAML, configurare strutture di dati ed eseguire la logica durante l'avvio. Per le app WinUI, è utile considerare l'avvio in quattro fasi: avvio del processo, creazione di finestre, creazione della pagina principale e layout/rendering per il primo fotogramma.
Il periodo di avvio è il tempo tra il momento in cui un utente avvia l'app e il momento in cui l'app diventa funzionale. Questo è un momento critico perché è la prima impressione dell'utente dell'app. Gli utenti si aspettano un feedback immediato e continuo dal sistema e dalle app. Il sistema e l'app vengono percepiti come interrotti o progettati in modo non adeguato quando le app non vengono avviate rapidamente.
Introduzione alle fasi di avvio
L'avvio prevede una serie di parti in movimento e tutte devono essere coordinate per un'esperienza utente ottimale. I passaggi seguenti si verificano tra l'avvio dell'app e il contenuto dell'applicazione visualizzato.
- Il processo si avvia e il codice di avvio generato dal modello chiama
Main. - Viene creato l'oggetto
Application.- Il costruttore dell'app chiama
InitializeComponent, il che causa l'analisi diApp.xamle la creazione di oggetti.
- Il costruttore dell'app chiama
-
Application.OnLaunched viene generato.
- Il codice dell'app crea la finestra principale, assegna il contenuto iniziale e chiama
Activate. - Il costruttore della pagina principale chiama
InitializeComponent, che determina l'analisi del codice XAML della pagina e la creazione di oggetti.
- Il codice dell'app crea la finestra principale, assegna il contenuto iniziale e chiama
- Il framework XAML esegue il passaggio di layout, inclusa la misura e la disposizione.
-
ApplyTemplatefa sì che il contenuto del modello di controllo venga creato per ogni controllo, che in genere è la maggior parte del tempo di layout durante l'avvio.
-
- Il rendering crea oggetti visivi per il contenuto della finestra.
- Il primo fotogramma viene presentato e il lavoro post-avvio continua in modo asincrono.
Fare meno nel percorso della tua startup
Mantenere libero il percorso del codice di avvio da qualsiasi elemento non necessario per il primo fotogramma.
- Se si dispone di DLL utente contenenti controlli non necessari durante il primo fotogramma, prendere in considerazione il caricamento ritardato.
- Se si ha una parte dell'interfaccia utente che dipende dai dati dal cloud, suddividere l'interfaccia utente. Innanzitutto, visualizzare l'interfaccia utente che non dipende dai dati cloud e quindi visualizzare in modo asincrono l'interfaccia utente dipendente dal cloud. È anche consigliabile prendere in considerazione la memorizzazione dei dati nella cache in locale in modo che l'applicazione possa funzionare offline o non essere influenzata dalla connettività di rete lenta.
- Mostra l'interfaccia di avanzamento se la tua interfaccia utente è in attesa di dati.
- Prestare attenzione alle progettazioni di app che comportano un sacco di analisi dei file di configurazione o dell'interfaccia utente generata dinamicamente dal codice.
Ridurre il numero di elementi
Le prestazioni di avvio in un'app XAML sono direttamente correlate al numero di elementi creati durante l'avvio. Minore è il numero di elementi creati, minore sarà il tempo necessario per l'avvio dell'app. Come benchmark approssimativo, considera che ogni elemento richieda 1 ms per essere creato.
- I modelli usati nei controlli elementi possono avere l'impatto maggiore, perché vengono ripetuti più volte. Vedi Ottimizzazione dell'interfaccia utente listView e GridView.
- I modelli usercontrols e di controllo vengono espansi, quindi devono essere presi in considerazione anche quelli.
- Se crei codice XAML che non viene visualizzato sullo schermo, devi giustificare se tali parti di XAML devono essere create durante l'avvio.
Nella finestra Struttura ad albero visuale dinamica di Visual Studio vengono visualizzati i conteggi degli elementi figlio per ogni nodo dell'albero.
Utilizzare il differimento. La compressione di un elemento o l'impostazione dell'opacità su 0 non impedisce la creazione dell'elemento. Usando x:Load o x:DeferLoadStrategy, è possibile ritardare il caricamento di una parte dell'interfaccia utente e caricarlo quando necessario. Questo è un buon modo per ritardare l'elaborazione dell'interfaccia utente che non è visibile durante l'avvio in modo da poterla caricare quando necessario o come parte di un set di logica ritardata. Per attivare il caricamento, è necessario chiamare FindName solo per l'elemento . Per un esempio e altre informazioni, vedere attributo x:Load e attributo x:DeferLoadStrategy.
Virtualizzazione. Se nell'interfaccia utente sono presenti contenuti elenco o ripetitori, è consigliabile usare la virtualizzazione dell'interfaccia utente. Se l'interfaccia utente dell'elenco non è virtualizzata, si paga il costo della creazione di tutti gli elementi in anticipo e ciò può rallentare l'avvio. Vedi Ottimizzazione dell'interfaccia utente listView e GridView.
Le prestazioni dell'applicazione non sono solo relative alle prestazioni non elaborate; è anche sulla percezione. La modifica dell'ordine delle operazioni affinché gli aspetti visivi vengano elaborati per primi spesso dà all'utente l'impressione che l'applicazione sia più veloce. Gli utenti considerano l'applicazione caricata quando il contenuto è sullo schermo. Le applicazioni in genere devono eseguire più operazioni durante l'avvio e non tutte le operazioni necessarie per visualizzare l'interfaccia utente, quindi tali parti devono essere ritardate o classificate in ordine di priorità inferiori rispetto all'interfaccia utente.
Questo articolo illustra il primo fotogramma, che deriva dalla terminologia di animazione e video ed è una misura del tempo necessario fino a quando il contenuto non viene visualizzato dall'utente finale.
Migliorare la percezione delle startup
Usiamo l'esempio di un semplice gioco online per identificare ogni fase di avvio e tecniche diverse per fornire all'utente feedback durante il processo.
Nella prima fase il processo viene avviato e l'app ne crea la finestra. Durante questo periodo, l'utente non ha ancora visto il contenuto dell'app. L'obiettivo è quello di ottenere rapidamente una finestra leggera sullo schermo.
La seconda fase comprende la creazione e l'inizializzazione delle strutture fondamentali per il gioco. Se l'app può creare rapidamente l'interfaccia utente iniziale con i dati disponibili all'avvio, questa fase è semplice e puoi visualizzare immediatamente l'interfaccia utente. In caso contrario, visualizzare una pagina di caricamento leggera durante l'inizializzazione dell'app.
L'aspetto della pagina di caricamento è a tua discrezione; può essere semplice come visualizzare una barra di avanzamento o un cerchio di avanzamento. Il punto chiave è che l'app indica che sta eseguendo il lavoro prima che diventi completamente reattivo. Nel caso del gioco, la schermata iniziale richiede che alcune immagini e suoni vengano caricati dal disco in memoria. Queste attività richiedono tempo, quindi l'app mantiene l'utente informato mostrando una pagina di caricamento con una semplice animazione correlata al tema del gioco.
La terza fase inizia dopo che il gioco ha un set minimo di informazioni per creare un'interfaccia utente interattiva, che sostituisce la pagina di caricamento. A questo punto, le uniche informazioni disponibili per il gioco online possono essere il contenuto caricato dall'app dal disco. Il gioco può essere fornito con contenuti sufficienti per creare un'interfaccia utente interattiva, ma poiché si tratta di un gioco online che non sarà completamente funzionante finché non si connette a Internet e scarica alcune informazioni aggiuntive. Fino a quando non ha tutte le informazioni necessarie, l'utente può interagire con l'interfaccia utente, ma le funzionalità che richiedono dati aggiuntivi dal Web devono fornire commenti e suggerimenti sul contenuto ancora in fase di caricamento. Potrebbe essere necessario del tempo per rendere un'app completamente funzionante, quindi è importante che le funzionalità vengano rese disponibili il prima possibile.
Ora che sono state identificate le tre fasi di avvio nel gioco online, è possibile collegarle al codice effettivo.
Fase 1 e Fase 2
Usa il costruttore dell'app solo per inizializzare le strutture di dati fondamentali per l'app. Mantieni OnLaunched concentrato sulla rapida creazione della prima finestra, assegnando contenuti leggeri e attivando la finestra affinché l'app possa visualizzare immediatamente il feedback.
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;
}
}
Una delle attività principali in OnLaunched consiste nel creare un'interfaccia utente, assegnarla a Window.Content e quindi chiamare Window.Activate. Se sono necessari più flussi di attivazione, mantenere lo stesso principio: mostrare contenuto leggero rapidamente e spostare il lavoro costoso dal percorso di avvio critico.
Le app che visualizzano una pagina di caricamento durante l'avvio possono iniziare a lavorare per creare l'interfaccia utente principale in background. Dopo aver creato l'elemento, si verifica l'evento FrameworkElement.Loaded . Nel gestore eventi è possibile sostituire il contenuto della finestra, che è attualmente la schermata di caricamento, con la home page appena creata.
È fondamentale che un'app con un periodo di inizializzazione esteso mostri una pagina di caricamento. Oltre a fornire commenti e suggerimenti sul processo di avvio, la finestra deve essere attivata rapidamente in modo che gli utenti vedano che l'app sta effettuando progressi.
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;
}
}
Fase 3
Solo perché l'app ha visualizzato l'interfaccia utente non significa che sia completamente pronta per l'uso. Nel caso del gioco, l'interfaccia utente viene visualizzata con segnaposto per le funzionalità che richiedono dati da Internet. A questo punto, il gioco scarica i dati aggiuntivi necessari per rendere l'app completamente funzionante e abilita progressivamente le funzionalità durante l'acquisizione dei dati.
A volte gran parte del contenuto necessario per l'avvio può essere inserita in un pacchetto con l'app. Questo è il caso di un gioco semplice. In questo modo il processo di avvio è piuttosto semplice. Ma molti programmi, come lettori di notizie e visualizzatori di foto, devono estrarre informazioni dal Web per diventare funzionali. Questi dati possono essere di grandi dimensioni e possono richiedere una quantità equa di tempo per il download. Il modo in cui l'app ottiene questi dati durante l'avvio può avere un impatto enorme sulle prestazioni percepite.
È possibile visualizzare una pagina di caricamento troppo lunga se un'app ha tentato di scaricare un intero set di dati necessario per la funzionalità nella prima o nella seconda fase di avvio. Questo rende un'app bloccata. È consigliabile che un'app scarichi la quantità minima di dati necessaria per mostrare un'interfaccia utente interattiva con elementi segnaposto nella fase 2 e quindi caricare progressivamente i dati, che sostituisce gli elementi segnaposto, nella fase 3. Per altre info sulla gestione dei dati, vedi Ottimizzare ListView e GridView.
Il modo in cui un'app reagisce esattamente a ogni fase di avvio dipende completamente dall'utente, ma offre all'utente il maggior numero possibile di feedback usando l'interfaccia utente iniziale leggera, il caricamento di schermate e il caricamento progressivo dei dati rende l'app più veloce.
Ridurre al minimo gli assembly gestiti nel percorso di avvio
Il codice riutilizzabile viene spesso fornito sotto forma di moduli (DLL) inclusi in un progetto. Caricare questi moduli richiede l'accesso al disco e il costo può accumularsi. Questo ha il maggiore impatto sull'avvio a freddo, ma può influire anche sull'avvio a caldo. Nelle app .NET, il CLR tenta di ritardare il costo il più possibile caricando gli assembly su richiesta. Ovvero, CLR non carica un modulo finché non vi fa riferimento un metodo eseguito. Quindi fare riferimento solo agli assembly necessari per l'avvio dell'app nel codice di avvio in modo che CLR non carichi moduli non necessari. Se nel percorso di avvio sono presenti percorsi di codice inutilizzati con riferimenti non necessari, spostare questi percorsi di codice in altri metodi per evitare i carichi non necessari.
Un altro modo per ridurre i caricamenti dei moduli consiste nel combinare i moduli dell'app. Il caricamento di un assembly di grandi dimensioni richiede in genere meno tempo rispetto al caricamento di due piccoli assembly. Questo non è sempre possibile e è consigliabile combinare i moduli solo se non fa una differenza sostanziale per la produttività degli sviluppatori o la riutilizzabilità del codice. È possibile usare strumenti come PerfView o Windows Performance Analyzer (WPA) per scoprire quali moduli vengono caricati all'avvio.
Effettuare richieste Web intelligenti
Puoi migliorare notevolmente il tempo di caricamento di un'app inserendone il contenuto in locale, inclusi XAML, immagini e qualsiasi altro file importante per l'app. Le operazioni su disco sono più veloci rispetto alle operazioni di rete. Se un'app richiede un particolare file all'inizializzazione, è possibile ridurre il tempo di avvio complessivo caricandolo dal disco anziché recuperarlo da un server remoto.
Registra e gestisci le pagine della cache in modo efficiente
Il Frame controllo fornisce funzionalità di spostamento. Offre la navigazione alla pagina (Navigate metodo), alla registrazione di navigazione (BackStack proprietà, ForwardStack e GoForward metodi, GoBack), alla memorizzazione nella cache delle pagine (Page.NavigationCacheMode) e al supporto per la serializzazione (GetNavigationState metodo).
Le prestazioni di cui essere consapevoli con Frame riguardano principalmente il journaling e il caching delle pagine.
Registrazione dei fotogrammi. Quando si naviga a una pagina con Frame.Navigate, alla raccolta viene aggiunto un PageStackEntry della pagina corrente Frame.BackStack.
PageStackEntry è relativamente piccolo, ma non esiste alcun limite predefinito per le dimensioni della BackStack raccolta. Potenzialmente, un utente potrebbe navigare in un ciclo e aumentare questa raccolta indefinitamente.
L'PageStackEntry include anche il parametro passato al metodo Frame.Navigate. È consigliabile che il parametro sia un tipo serializzabile primitivo, ad esempio o intstring, per consentire il funzionamento del Frame.GetNavigationState metodo. Ma questo parametro potrebbe potenzialmente fare riferimento a un oggetto che rappresenta quantità più significative di working set o altre risorse, rendendo ogni voce in BackStack molto più costosa. Ad esempio, è possibile utilizzare un StorageFile come parametro e di conseguenza il BackStack potrebbe mantenere aperto un numero indefinito di file.
È pertanto consigliabile mantenere i parametri di spostamento piccoli e limitare le dimensioni di BackStack.
BackStack è una raccolta standard in C#, quindi può essere ridotta semplicemente rimuovendo gli elementi.
Memorizzazione nella cache delle pagine. Per impostazione predefinita, quando si passa a una pagina con il Frame.Navigate metodo, viene creata una nuova istanza della pagina. Analogamente, se si torna alla pagina precedente con Frame.GoBack, viene allocata una nuova istanza della pagina precedente.
Frame offre anche una cache di pagina opzionale che può evitare queste istanziazioni. Per ottenere una pagina inserita nella cache, usare la Page.NavigationCacheMode proprietà . Impostando tale modalità su Required forza la memorizzazione nella cache della pagina, mentre configurandola su Enabled consente la memorizzazione nella cache. Per impostazione predefinita, le dimensioni della cache sono pari a 10 pagine, ma è possibile eseguirne l'override con la Frame.CacheSize proprietà . Tutte le Required pagine vengono memorizzate nella cache e, se sono presenti meno di CacheSize pagine necessarie, è possibile memorizzare nella cache anche le Enabled pagine.
La memorizzazione nella cache delle pagine consente di evitare le istanze e quindi migliorare le prestazioni di navigazione. La memorizzazione nella cache delle pagine può compromettere le prestazioni a causa dell'eccessiva memorizzazione nella cache e influire sull'insieme di lavoro.
È pertanto consigliabile usare la memorizzazione nella cache delle pagine in base alle esigenze dell'applicazione. Si supponga, ad esempio, di avere un'app che mostra un elenco di elementi in un Frameoggetto e quando si seleziona un elemento, il frame viene spostato in una pagina di dettaglio per tale elemento. La pagina di elenco dovrebbe essere probabilmente impostata sulla cache. Se la pagina dei dettagli è la stessa per tutti gli elementi, probabilmente dovrebbe essere memorizzata nella cache. Tuttavia, se la pagina dei dettagli è più eterogenea, potrebbe essere preferibile uscire dalla memorizzazione nella cache.