Delen via


Aanbevolen procedures voor de opstartprestaties van uw WinUI-app

Maak WinUI-apps met Windows App SDK die snel beginnen door opstartwerkzaamheden te verminderen, het eerste frame te vereenvoudigen en niet-kritieke functies te laden nadat het venster interactief is.

Aanbevolen procedures voor de opstartprestaties van uw app

Gebruikers zien deels of uw app snel of traag is op basis van hoe lang het duurt om te starten. Voor de doeleinden van dit onderwerp begint de opstarttijd van een app wanneer de gebruiker de app start en eindigt wanneer de gebruiker op een zinvolle manier met de app kan communiceren. Dit artikel bevat suggesties voor het verkrijgen van betere opstartprestaties van een WinUI-app.

De opstarttijd van uw app meten

Zorg ervoor dat u uw app een paar keer start voordat u de opstarttijd daadwerkelijk meet. Dit geeft u een basislijn voor uw meting en zorgt ervoor dat u zo kort mogelijk een opstarttijd meet.

Neem metingen die representatief zijn voor wat de eindgebruiker zal ervaren. Measure Release bouwt voort op representatieve hardware, bekijk zowel koude als warme start en richt u op de tijd tot het eerste interactieve beeld in plaats van alleen de tijd totdat het proces is gestart.

Werk zo lang mogelijk uitstellen

Als u de opstarttijd van uw app wilt verbeteren, moet u alleen het werk doen dat absoluut nodig is om de gebruiker te laten communiceren met de app. Dit kan met name nuttig zijn als u het laden van extra assembly's kunt vertragen. De Common Language Runtime laadt een assembly bij eerste gebruik. Als u het aantal geladen assembly's kunt minimaliseren, kunt u mogelijk de opstarttijd en het geheugenverbruik van uw app verbeteren.

Langlopend werk onafhankelijk uitvoeren

Uw app kan interactief zijn, ook al zijn er onderdelen van de app die niet volledig functioneel zijn. Als uw app bijvoorbeeld gegevens weergeeft die even duren om op te halen, kunt u die code onafhankelijk van de opstartcode van de app uitvoeren door de gegevens asynchroon op te halen. Wanneer de gegevens beschikbaar zijn, vult u de gebruikersinterface van de app in met de gegevens.

Veel van de API's die gegevens ophalen, zijn asynchroon, dus u haalt waarschijnlijk asynchroon gegevens op. Zie Asynchrone programmering met async en await voor meer informatie. Als u werkt waarvoor geen asynchrone API's worden gebruikt, kunt u de Task klasse gebruiken om langlopend werk uit te voeren, zodat de gebruiker niet kan communiceren met de app. Hierdoor blijft uw app responsief terwijl de gegevens worden geladen.

Als het erg lang duurt voordat uw app een deel van de gebruikersinterface laadt, kunt u overwegen een bericht in dat gebied weer te geven, zoals 'Meest recente gegevens ophalen', zodat uw gebruikers weten dat de app nog steeds wordt verwerkt.

Opstarttijd minimaliseren

Alle maar de eenvoudigste apps vereisen een zichtbare hoeveelheid tijd om resources te laden, XAML te parseren, gegevensstructuren in te stellen en logica uit te voeren tijdens het starten. Voor WinUI-apps is het handig om na te denken over opstarten in vier fasen: proceslancering, het maken van vensters, het maken van hoofdpagina's en het maken/renderen van het eerste frame.

De opstartperiode is de tijd tussen het moment waarop een gebruiker de app start en het moment dat de app functioneel wordt. Dit is een kritieke tijd omdat het de eerste indruk van uw app is van een gebruiker. Gebruikers verwachten direct en doorlopend feedback van het systeem en van apps. Het systeem en de app worden beschouwd als beschadigd of slecht ontworpen wanneer apps niet snel worden gestart.

Inleiding tot de fasen van opstarten

Opstarten omvat een aantal bewegende onderdelen en alle onderdelen moeten worden gecoördineerd voor de beste gebruikerservaring. De volgende stappen worden uitgevoerd tussen de gebruiker die uw app start en de inhoud van de toepassing die wordt weergegeven.

  • Het proces wordt gestart en de door een sjabloon gegenereerde opstartcode roept Main aan.
  • Het Application object wordt gemaakt.
    • De app-constructor roept InitializeComponent op, waardoor App.xaml wordt geparsed en objecten worden aangemaakt.
  • Application.OnLaunched wordt getriggerd.
    • App code maakt het hoofdvenster, wijst de initiële inhoud toe en roept Activate aan.
    • De hoofdpaginaconstructor roept InitializeComponentaan, waardoor de pagina XAML wordt geparseerd en objecten worden gemaakt.
  • Het XAML-framework voert de lay-outpas uit, inclusief meting en rangschikken.
    • ApplyTemplate zorgt ervoor dat voor elk besturingselement sjablooninhoud wordt gemaakt. Dit is meestal het grootste deel van de indelingstijd tijdens het opstarten.
  • Render maakt visuals voor de inhoud van het venster.
  • Het eerste frame wordt gepresenteerd en het werk na de opstart wordt asynchroon voortgezet.

Doe minder in je start-up traject

Bewaar uw opstartcodepad vrij van alles wat niet nodig is voor uw eerste frame.

  • Als u gebruikers-DLL's hebt die besturingselementen bevatten die tijdens het eerste frame niet nodig zijn, overweeg dan of ze vertraagd geladen kunnen worden.
  • Als u een deel van uw gebruikersinterface hebt dat afhankelijk is van gegevens uit de cloud, splitst u die gebruikersinterface. Open eerst de gebruikersinterface die niet afhankelijk is van cloudgegevens en breng vervolgens asynchroon de cloudafhankelijke gebruikersinterface weer. U moet ook overwegen om gegevens lokaal in de cache op te nemen, zodat de toepassing offline kan werken of niet kan worden beïnvloed door een trage netwerkverbinding.
  • Toon voortgangsindicator als uw interface wacht op gegevens.
  • Wees voorzichtig met app-ontwerpen waarbij veel configuratiebestanden worden geparseerd of dat de gebruikersinterface dynamisch wordt gegenereerd door code.

Aantal elementen verminderen

Opstartprestaties in een XAML-app worden rechtstreeks gecorreleerd aan het aantal elementen dat u tijdens het opstarten maakt. Hoe minder elementen u maakt, hoe minder tijd het duurt voordat uw app wordt opgestart. Als ruwe richtlijn kunt u ervan uitgaan dat het maken van elk element 1 ms duurt.

  • Sjablonen die worden gebruikt in besturingselementen voor items, kunnen de grootste impact hebben, omdat ze meerdere keren worden herhaald. Zie De optimalisatie van de gebruikersinterface van ListView en GridView.
  • UserControls en beheersjablonen worden uitgebreid, dus deze moeten ook in aanmerking worden genomen.
  • Als u een XAML maakt die niet op het scherm wordt weergegeven, moet u rechtvaardigen of deze onderdelen van XAML moeten worden gemaakt tijdens het opstarten.

In het Visual Studio Live Visual Tree-venster wordt het aantal onderliggende elementen voor elk knooppunt in de structuur weergegeven.

Live visuele structuur.

Gebruik uitstel. Als je een element inklapt of de dekking instelt op 0, voorkomt dat niet dat het element wordt gemaakt. Met x:Load of x:DeferLoadStrategy kunt u het laden van een deel van de gebruikersinterface uitstellen en het laden wanneer dat nodig is. Dit is een goede manier om de verwerkingsinterface te vertragen die niet zichtbaar is tijdens het opstarten, zodat u deze kunt laden wanneer dat nodig is of als onderdeel van een set vertraagde logica. Als u het laden wilt activeren, hoeft u alleen het element aan te roepen FindName . Zie het kenmerk x:Load en x:DeferLoadStrategy voor een voorbeeld en meer informatie.

Virtualisatie. Als u inhoud van lijsten of herhalingen in uw gebruikersinterface hebt, wordt u ten zeerste aangeraden ui-virtualisatie te gebruiken. Als de lijstgebruikersinterface niet is gevirtualiseerd, betaalt u de kosten voor het maken van alle elementen vooraf en kunt u het opstarten vertragen. Zie De optimalisatie van de gebruikersinterface van ListView en GridView.

Toepassingsprestaties gaan niet alleen over onbewerkte prestaties; Het gaat ook om perceptie. Als u de volgorde van bewerkingen wijzigt, zodat visuele aspecten zich het eerst voordoen, voelt de gebruiker dat de toepassing sneller is. Gebruikers beschouwen de applicatie als geladen wanneer de inhoud op het scherm verschijnt. Toepassingen moeten vaak meerdere dingen doen tijdens het opstarten, en niet al dat werk is vereist om de gebruikersinterface weer te geven, zodat deze onderdelen moeten worden vertraagd of prioriteit moeten krijgen die lager zijn dan de gebruikersinterface.

In dit artikel wordt gesproken over het eerste frame, dat afkomstig is van terminologie voor animaties en video's en hoe lang het duurt totdat de inhoud door de eindgebruiker wordt gezien.

Opstartperceptie verbeteren

Laten we het voorbeeld van een eenvoudig onlinespel gebruiken om elke fase van het opstarten en verschillende technieken te identificeren om de gebruiker feedback te geven tijdens het proces.

In de eerste fase wordt het proces gestart en wordt het venster van de app gemaakt. Gedurende deze tijd heeft de gebruiker de eigen inhoud van de app nog niet gezien. Het doel is om snel een lichtgewicht venster op het scherm te krijgen.

De tweede fase omvat het maken en initialiseren van de structuren die essentieel zijn voor het spel. Als de app snel de eerste gebruikersinterface kan maken met de gegevens die beschikbaar zijn bij het starten, is deze fase triviaal en kunt u de gebruikersinterface onmiddellijk weergeven. Anders geeft u een lichtgewicht laadpagina weer terwijl de app wordt geïnitialiseerd.

Hoe de laadpagina eruitziet, is aan u. het kan zo eenvoudig zijn als het weergeven van een voortgangsbalk of voortgangsring. Het belangrijkste punt is dat de app aangeeft dat het werk uitvoert voordat deze volledig reageert. In het geval van de game vereist het eerste scherm dat sommige afbeeldingen en geluiden van schijf in het geheugen worden geladen. Deze taken nemen tijd in beslag, zodat de app de gebruiker op de hoogte houdt door een laadpagina weer te geven met een eenvoudige animatie die betrekking heeft op het thema van de game.

De derde fase begint nadat het spel een minimale set informatie heeft om een interactieve gebruikersinterface te maken, waardoor de laadpagina wordt vervangen. Op dit moment is de enige informatie die beschikbaar is voor de online game mogelijk de inhoud die de app vanaf de schijf heeft geladen. Het spel kan worden verzonden met voldoende inhoud om een interactieve gebruikersinterface te maken, maar omdat het een online game is, is het pas volledig functioneel als het verbinding maakt met internet en wat aanvullende informatie downloadt. Totdat alle benodigde informatie beschikbaar is, kan de gebruiker de gebruikersinterface gebruiken, maar functies die aanvullende gegevens van het web nodig hebben, moeten feedback geven dat de inhoud nog steeds aan het laden is. Het kan enige tijd duren voordat een app volledig functioneel wordt, dus het is belangrijk dat functionaliteit zo snel mogelijk beschikbaar wordt gesteld.

Nu we de drie fasen van opstarten in de online game hebben geïdentificeerd, gaan we ze koppelen aan de daadwerkelijke code.

Fase 1 en fase 2

Gebruik de constructor van de app alleen om gegevensstructuren te initialiseren die essentieel zijn voor de app. Blijf OnLaunched gefocust op het snel maken van het eerste venster, het toewijzen van lichtgewicht inhoud en het activeren van het venster, zodat de app direct feedback kan weergeven.

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;
    }
}

Een van de belangrijkste taken in OnLaunched is het maken van een gebruikersinterface, het toewijzen aan Window.Content en vervolgens Window.Activate aanroepen. Als u meer dan één activeringsstroom nodig hebt, moet u hetzelfde principe behouden: laat lichtgewicht inhoud snel zien en zet duur werk af van het kritieke opstartpad.

Apps die tijdens het starten een laadpagina weergeven, kunnen beginnen met het maken van de hoofdgebruikersinterface op de achtergrond. Nadat dat element is gemaakt, vindt de gebeurtenis FrameworkElement.Loaded plaats. In de gebeurtenis-handler kunt u de inhoud van het venster, dat momenteel het laadscherm is, vervangen door de zojuist gemaakte startpagina.

Het is van cruciaal belang dat een app met een verlengde initialisatieperiode een laadpagina weergeeft. Afgezien van het geven van feedback over het opstartproces, moet het venster snel worden geactiveerd, zodat gebruikers zien dat de app voortgang maakt.

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

Alleen omdat de app de gebruikersinterface weergeeft, betekent dat niet dat de app volledig gereed is voor gebruik. In het geval van onze game wordt de gebruikersinterface weergegeven met tijdelijke aanduidingen voor functies waarvoor gegevens van internet zijn vereist. Op dit moment downloadt de game de extra gegevens die nodig zijn om de app volledig functioneel te maken en maakt geleidelijk functies mogelijk wanneer gegevens worden verkregen.

Soms kan veel van de inhoud die nodig is voor het opstarten worden verpakt met de app. Dat is het geval met een eenvoudig spel. Dit maakt het opstartproces vrij eenvoudig. Maar veel programma's, zoals nieuwslezers en fotoviewers, moeten informatie ophalen van het web om functioneel te worden. Deze gegevens kunnen groot zijn en kunnen een behoorlijke hoeveelheid tijd in beslag nemen om te downloaden. Hoe de app deze gegevens krijgt tijdens het opstarten, kan een enorme invloed hebben op de waargenomen prestaties.

U kunt een laadpagina te lang weergeven als een app probeert een volledige gegevensset te downloaden die nodig is voor functionaliteit in de eerste of tweede fase van het opstarten. Dat maakt dat een app er vastgelopen uitziet. Het is raadzaam dat een app de minimale hoeveelheid gegevens downloadt die nodig is om een interactieve gebruikersinterface weer te geven met tijdelijke aanduidingelementen in fase 2 en vervolgens geleidelijk gegevens laadt, waardoor de tijdelijke aanduidingelementen in fase 3 worden vervangen. Zie ListView en GridView optimaliseren voor meer informatie over het omgaan met gegevens.

Hoe precies een app reageert op elke fase van het opstarten, is volledig aan u, maar de gebruiker zoveel mogelijk feedback te geven met behulp van lichtgewicht initiële gebruikersinterface, het laden van schermen en progressief laden van gegevens zorgt ervoor dat de app zich sneller voelt.

Minimaliseer beheerde assemblies in het opstartpad

Herbruikbare code wordt vaak geleverd in de vorm van modules (DLL's) die zijn opgenomen in een project. Het laden van deze modules vereist toegang tot de schijf, en de kosten kunnen zich opstapelen. Dit heeft de grootste invloed op koude opstartproblemen, maar het kan ook van invloed zijn op het warme opstarten. In .NET-apps probeert de CLR die kosten zoveel mogelijk uit te stellen door assembly's op aanvraag te laden. Dat wil gezegd, de CLR laadt geen module totdat een uitgevoerde methode ernaar verwijst. Verwijs dus alleen naar assembly's die nodig zijn voor het starten van uw app in opstartcode, zodat de CLR geen onnodige modules laadt. Als u ongebruikte codepaden in uw opstartpad hebt met onnodige verwijzingen, verplaatst u deze codepaden naar andere methoden om onnodige belastingen te voorkomen.

Een andere manier om modulebelastingen te verminderen, is door app-modules te combineren. Het laden van één grote assembly kost doorgaans minder tijd dan het laden van twee kleine assembly's. Dit is niet altijd mogelijk en u moet modules alleen combineren als dit geen belangrijk verschil maakt in de productiviteit van ontwikkelaars of het hergebruik van code. U kunt hulpprogramma's zoals PerfView of Windows Performance Analyzer (WPA) gebruiken om erachter te komen welke modules worden geladen bij het opstarten.

Slimme webaanvragen maken

U kunt de laadtijd van een app aanzienlijk verbeteren door de inhoud lokaal in te pakken, inclusief XAML, afbeeldingen en andere bestanden die belangrijk zijn voor de app. Schijfbewerkingen zijn sneller dan netwerkbewerkingen. Als een app een bepaald bestand nodig heeft bij de initialisatie, kunt u de totale opstarttijd verminderen door deze vanaf de schijf te laden in plaats van het op te halen van een externe server.

Dagboek- en cachepagina's efficiënt beheren

Het Frame besturingselement biedt navigatiefuncties. Het biedt navigatie naar een pagina (Navigate methode), navigatielogboeken (BackStack en ForwardStack eigenschappen GoForward en GoBack methoden), paginacaching (Page.NavigationCacheMode) en serialisatieondersteuning (GetNavigationState methode).

De prestatie waarmee u rekening moet houden bij Frame heeft vooral betrekking op journaling en pagina-caching.

Frame journaling. Wanneer u naar een pagina navigeert met Frame.Navigate, wordt een PageStackEntry voor de huidige pagina toegevoegd aan de Frame.BackStack verzameling. PageStackEntry is relatief klein, maar er is geen ingebouwde limiet voor de grootte van de BackStack verzameling. Een gebruiker kan in een lus navigeren en deze verzameling voor onbepaalde tijd uitbreiden.

De PageStackEntry parameter bevat ook de parameter die is doorgegeven aan de Frame.Navigate methode. Het wordt aanbevolen dat de parameter een primitief serialiseerbaar type is, zoals een int of string, om de Frame.GetNavigationState methode te laten werken. Maar die parameter kan mogelijk refereren aan een object dat verantwoordelijk is voor grotere hoeveelheden werkset of andere resources, waardoor elke entree in de BackStack veel kostbaarder wordt. U kunt bijvoorbeeld een StorageFile als parameter gebruiken en daardoor BackStack een onbeperkt aantal bestanden openen.

Daarom wordt aanbevolen om de navigatieparameters klein te houden en de omvang van de BackStack te beperken. Het BackStack is een standaardverzameling in C#, zodat deze eenvoudig kan worden bijgesneden door vermeldingen te verwijderen.

Pagina's opslaan in cache. Wanneer u met de Frame.Navigate methode naar een pagina navigeert, wordt standaard een nieuw exemplaar van de pagina geïnstantieerd. Als u vervolgens terug navigeert naar de vorige pagina met Frame.GoBack, wordt een nieuw exemplaar van de vorige pagina toegewezen.

Frame biedt ook een optionele paginacache die deze instantiëring kan voorkomen. Gebruik de Page.NavigationCacheMode eigenschap om een pagina in de cache te plaatsen. Door de modus in te stellen op Required wordt de pagina geforceerd in de cache opgeslagen, terwijl bij instelling op Enabled de pagina in de cache kan worden opgeslagen. De cachegrootte is standaard 10 pagina's, maar dit kan worden overschreven met de Frame.CacheSize eigenschap. Alle Required pagina's worden in de cache opgeslagen en als er minder dan CacheSize de vereiste pagina's zijn, Enabled kunnen ook pagina's in de cache worden opgeslagen.

Het opslaan van pagina's kan helpen bij het voorkomen van instantiëring en het verbeteren van de navigatieprestaties. Het cachen van pagina's kan de prestaties nadelig beïnvloeden door over-caching, waardoor het invloed heeft op de actieve set.

Daarom is het raadzaam om paginacaching te gebruiken voor uw toepassing. Stel dat u een app hebt met een lijst met items in een Frame, en wanneer u een item selecteert, wordt het frame naar een detailpagina voor dat item geleid. De lijstpagina moet waarschijnlijk worden ingesteld op cache. Als de detailpagina voor alle items hetzelfde is, moet deze waarschijnlijk ook in de cache worden opgeslagen. Maar als de detailpagina heterogener is, is het misschien beter om caching uit te schakelen.