Zvýšení Xamarin.Forms výkonu aplikace

Vývoj 2016: Optimalizace výkonu aplikací pomocí Xamarin.Forms

Nízký výkon aplikace se prezentuje mnoha způsoby. Aplikace může vypadat jako nereagující, může způsobit pomalé posouvání a může snížit životnost baterie zařízení. Optimalizace výkonu ale zahrnuje nejen implementaci efektivního kódu. Je také potřeba zvážit zkušenosti uživatele s výkonem aplikace. Například zajištění toho, aby se operace spouštěly bez blokování uživatele v provádění dalších aktivit, můžou pomoct zlepšit uživatelské prostředí.

Existuje mnoho technik pro zvýšení výkonu a vnímaného výkonu Xamarin.Forms aplikací. Souhrnně tyto techniky mohou výrazně snížit množství práce prováděné procesorem a množství paměti spotřebované aplikací.

Poznámka:

Než si přečtete tento článek, měli byste si nejprve přečíst výkon pro různé platformy, který popisuje jiné než platformy specifické techniky ke zlepšení využití paměti a výkonu aplikací vytvořených pomocí platformy Xamarin.

Povolení kompilátoru XAML

XAML lze volitelně zkompilovat přímo do zprostředkujícího jazyka (IL) pomocí kompilátoru XAML (XAMLC). XAMLC nabízí řadu výhod:

  • Provádí kontrolu času kompilace XAML a upozorní uživatele na případné chyby.
  • Odebere určitou dobu načítání a vytváření instancí elementů XAML.
  • Pomáhá zmenšit velikost souboru konečného sestavení tím, že už neobsahuje soubory .xaml.

XamlC je ve výchozím nastavení povolen v nových Xamarin.Forms řešeních. Ve starších řešeních ale může být potřeba ho povolit. Další informace naleznete v tématu Kompilace XAML.

Použití zkompilovaných vazeb

Kompilované vazby zlepšují výkon datových vazeb v Xamarin.Forms aplikacích překladem výrazů vazeb v době kompilace, nikoli za běhu s reflexí. Kompilace vazbového výrazu generuje zkompilovaný kód, který obvykle řeší 8–20krát rychlejší vazbu než použití klasické vazby. Další informace naleznete v tématu Kompilované vazby.

Omezení zbytečných vazeb

Nepoužívejte vazby pro obsah, který lze snadno nastavit staticky. Data vazeb, která nemusí být svázaná, nemají žádnou výhodu, protože vazby nejsou nákladově efektivní. Například nastavení Button.Text = "Accept" má menší režii než vazba Button.Text na vlastnost viewmodel string s hodnotou Accept.

Použití rychlých rendererů

Rychlé renderery snižují inflace a vykreslování nákladů na Xamarin.Forms ovládací prvky v Androidu tím, že zploštějí výslednou nativní řídicí hierarchii. Tím se zvýší výkon tím, že vytvoříte méně objektů, což pak vede k méně složitému vizuálnímu stromu a menšímu využití paměti.

Od Xamarin.Forms verze 4.0 používají všechny aplikace ve FormsAppCompatActivity výchozím nastavení rychlé renderery. Další informace naleznete v tématu Rychlé renderery.

Povolení trasování spuštění v Androidu

Kompilace AOT (Time) v Androidu minimalizuje režii při spuštění aplikace za běhu (JIT) a využití paměti za cenu vytvoření mnohem větší apk. Alternativou je použití trasování spuštění, které poskytuje kompromis mezi velikostí Android APK a časem spuštění v porovnání s konvenční kompilací AOT.

Namísto kompilace co nejvíce aplikace do nespravovaného kódu se trasování spuštění zkompiluje pouze sada spravovaných metod, které představují nejdražší části spuštění aplikace v prázdné Xamarin.Forms aplikaci. Výsledkem tohoto přístupu je zmenšená velikost APK ve srovnání s konvenční kompilací AOT, zatímco stále poskytuje podobná vylepšení při spuštění.

Povolení komprese rozložení

Komprese rozložení odebere zadaná rozložení ze stromu vizuálu při pokusu o zlepšení výkonu vykreslování stránek. Výhoda výkonu, kterou to přináší, se liší v závislosti na složitosti stránky, používané verzi operačního systému a zařízení, na kterém je aplikace spuštěná. Největší zvýšení výkonu ale uvidíte na starších zařízeních. Další informace naleznete v tématu Komprese rozložení.

Volba správného rozložení

Rozložení, které dokáže zobrazit více podřízených položek, ale má jenom jedno dítě, je plýtvání. Například následující příklad kódu ukazuje s jedním podřízeným kódem StackLayout :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DisplayImage.HomePage">
    <StackLayout>
        <Image Source="waterfront.jpg" />
    </StackLayout>
</ContentPage>

To je plýtvání a StackLayout prvek by měl být odebrán, jak je znázorněno v následujícím příkladu kódu:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DisplayImage.HomePage">
    <Image Source="waterfront.jpg" />
</ContentPage>

Kromě toho se nepokoušejte reprodukovat vzhled konkrétního rozložení pomocí kombinací jiných rozložení, protože výsledkem jsou nepotřebné výpočty rozložení. Nepokoušejte se například reprodukovat Grid rozložení pomocí kombinace StackLayout instancí. Následující příklad kódu ukazuje příklad tohoto chybného postupu:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Details.HomePage"
             Padding="0,20,0,0">
    <StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Name:" />
            <Entry Placeholder="Enter your name" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Age:" />
            <Entry Placeholder="Enter your age" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Occupation:" />
            <Entry Placeholder="Enter your occupation" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Address:" />
            <Entry Placeholder="Enter your address" />
        </StackLayout>
    </StackLayout>
</ContentPage>

Je to plýtvání, protože se provádějí nepotřebné výpočty rozložení. Místo toho lze požadované rozložení lépe dosáhnout pomocí Grid, jak je znázorněno v následujícím příkladu kódu:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Details.HomePage"
             Padding="0,20,0,0">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <Label Text="Name:" />
        <Entry Grid.Column="1" Placeholder="Enter your name" />
        <Label Grid.Row="1" Text="Age:" />
        <Entry Grid.Row="1" Grid.Column="1" Placeholder="Enter your age" />
        <Label Grid.Row="2" Text="Occupation:" />
        <Entry Grid.Row="2" Grid.Column="1" Placeholder="Enter your occupation" />
        <Label Grid.Row="3" Text="Address:" />
        <Entry Grid.Row="3" Grid.Column="1" Placeholder="Enter your address" />
    </Grid>
</ContentPage>

Optimalizace výkonu rozložení

Pokud chcete dosáhnout nejlepšího možného výkonu rozložení, postupujte podle těchto pokynů:

  • Zmenšete hloubku hierarchií rozložení zadáním Margin hodnot vlastností, což umožňuje vytváření rozložení s menším počtem zobrazení obtékání. Další informace najdete v tématu Okraje a odsazení.
  • Při použití parametru Gridse pokuste zajistit, aby bylo co nejvíce řádků a sloupců nastaveno na Auto velikost. Každý řádek nebo sloupec s automatickou velikostí způsobí, že modul rozložení provede další výpočty rozložení. Místo toho pokud je to možné, použijte řádky a sloupce s pevnou velikostí. Případně můžete nastavit řádky a sloupce tak, aby zabíraly proporcionální prostor s GridUnitType.Star hodnotou výčtu za předpokladu, že nadřazený strom dodržuje tyto pokyny rozložení.
  • Nenastavujte VerticalOptions a HorizontalOptions vlastnosti rozložení, pokud není potřeba. Výchozí hodnoty LayoutOptions.Fill a LayoutOptions.FillAndExpand umožňují nejlepší optimalizaci rozložení. Změna těchto vlastností má náklady a spotřebovává paměť, i když je nastavíte na výchozí hodnoty.
  • Vyhněte se použití RelativeLayout , kdykoli je to možné. Výsledkem bude, že procesor bude muset provádět výrazně více práce.
  • Při použití , AbsoluteLayoutvyhněte se použití AbsoluteLayout.AutoSize vlastnosti, kdykoli je to možné.
  • Při použití , StackLayoutujistěte se, že je nastavena LayoutOptions.Expandspouze jedna podřízená položka . Tato vlastnost zajišťuje, že zadaná podřízená položka bude zabírat největší prostor, který StackLayout mu může dát, a je plýtvání prováděním těchto výpočtů více než jednou.
  • Vyhněte se volání žádné metody Layout třídy, protože výsledkem jsou nákladné výpočty rozložení prováděné. Místo toho je pravděpodobné, že požadované chování rozložení lze získat nastavením TranslationX a TranslationY vlastností. Případně můžete podtřídu Layout<View> třídy dosáhnout požadovaného chování rozložení.
  • Neaktualizovat žádné Label instance častěji, než je potřeba, protože změna velikosti popisku může vést k opětovnému výpočtu celého rozložení obrazovky.
  • Nenastavujte Label.VerticalTextAlignment vlastnost, pokud není vyžadována.
  • LineBreakMode Nastavte všechny Label instance na NoWrap kdykoli je to možné.

Použití asynchronního programování

Celková odezva aplikace může být vylepšená a kritické body výkonu se často vyhýbají pomocí asynchronního programování. V .NET je vzor návrhu pro asynchronní operace založený na úlohách (TAP) doporučeným vzorem návrhu. Nesprávné použití tap však může vést k nevýkonným aplikacím. Proto by se při používání TAP měly dodržovat následující pokyny.

Základy

  • Seznamte se s životním cyklem úlohy, který je reprezentován výčtem TaskStatus . Další informace naleznete v tématu Význam Stavu úkolů a Stav úkolu.

  • Task.WhenAll Pomocí metody asynchronně počkejte na dokončení více asynchronních operací, nikoli jednotlivě await na řadu asynchronních operací. Další informace naleznete v tématu Task.WhenAll.

  • Task.WhenAny Pomocí metody asynchronně počkejte na dokončení jedné z několika asynchronních operací. Další informace naleznete v tématu Task.WhenAny.

  • Použijte metodu Task.Delay k vytvoření objektu Task , který se dokončí po zadaném čase. To je užitečné pro scénáře, jako je dotazování na data a zpoždění zpracování uživatelského vstupu pro předem určený čas. Další informace naleznete v tématu Task.Delay.

  • Pomocí metody proveďte náročné synchronní operace procesoru Task.Run ve fondu vláken. Tato metoda je zkratkou pro metodu TaskFactory.StartNew s nejoptimálnější sadou argumentů. Další informace naleznete v tématu Task.Run.

  • Nepokoušejte se vytvářet asynchronní konstruktory. Místo toho použijte k správné await inicializaci události životního cyklu nebo samostatnou logiku inicializace. Další informace naleznete v tématu Asynchronní konstruktory na blog.stephencleary.com.

  • Pomocí opožděného vzoru úlohy se vyhněte čekání na dokončení asynchronních operací během spouštění aplikace. Další informace naleznete v tématu AsyncLazy.

  • Vytvořte obálku úloh pro existující asynchronní operace, které nepoužívají TAP, vytvořením TaskCompletionSource<T> objektů. Tyto objekty získávají výhody Task programovatelnosti a umožňují řídit životnost a dokončení přidruženého Task. Další informace naleznete v tématu Příroda TaskCompletionSource.

  • Vrácení objektu Task namísto vrácení očekávaného Task objektu, pokud není potřeba zpracovat výsledek asynchronní operace. To je výkonnější kvůli méně prováděnému přepínání kontextu.

  • Knihovnu toku dat TPL (Task Parallel Library) použijte ve scénářích, jako je zpracování dat, jakmile jsou k dispozici, nebo pokud máte více operací, které musí vzájemně komunikovat asynchronně. Další informace najdete v tématu Tok dat (paralelní knihovna úloh).

Uživatelské rozhraní

  • Pokud je k dispozici, zavolejte asynchronní verzi rozhraní API. Tím se odblokuje vlákno uživatelského rozhraní, které pomůže zlepšit uživatelské prostředí s aplikací.

  • Aktualizujte prvky uživatelského rozhraní daty z asynchronních operací ve vlákně uživatelského rozhraní, abyste se vyhnuli vyvolání výjimek. Aktualizace ListView.ItemsSource vlastnosti se ale automaticky zařadí do vlákna uživatelského rozhraní. Informace o určení, jestli je kód spuštěný ve vlákně uživatelského rozhraní, najdete v tématu Xamarin.Essentials: MainThread.

    Důležité

    Všechny vlastnosti ovládacího prvku, které se aktualizují prostřednictvím datové vazby, se automaticky zařadí do vlákna uživatelského rozhraní.

Zpracování chyb

  • Seznamte se s asynchronním zpracováním výjimek. Neošetřené výjimky vyvolané kódem, který běží asynchronně, se šíří zpět do volajícího vlákna s výjimkou určitých scénářů. Další informace najdete v tématu Zpracování výjimek (paralelní knihovna úloh).
  • Vyhněte se vytváření async void metod a místo toho vytvořte async Task metody. Ty umožňují snadnější zpracování chyb, kompozičnost a testovatelnost. Výjimkou tohoto návodu jsou asynchronní obslužné rutiny událostí, které musí vrátit void. Další informace najdete v tématu Vyhněte se asynchronnímu Voidu.
  • Nekombinujte blokující a asynchronní kód voláním Task.WaitTask.Result, nebo GetAwaiter().GetResult metod, protože mohou vést k zablokování. Pokud však musí být toto vodítko porušeno, upřednostňovaným přístupem je volat metodu GetAwaiter().GetResult , protože zachovává výjimky úkolu. Další informace najdete v tématu Asynchronní zpracování výjimek a zpracování výjimek úloh v .NET 4.5.
  • Kdykoli je to možné, použijte metodu ConfigureAwait k vytvoření kódu bez kontextu. Kontextový kód má lepší výkon pro mobilní aplikace a je užitečnou technikou pro zabránění zablokování při práci s částečně asynchronním základem kódu. Další informace naleznete v tématu Konfigurace kontextu.
  • Úlohy pokračování použijte pro funkce, jako je zpracování výjimek vyvolaných předchozí asynchronní operací, a zrušení pokračování před jeho spuštěním nebo během jeho spuštění. Další informace naleznete v tématu Řetězení úkolů pomocí průběžné úlohy.
  • Použijte asynchronní ICommand implementaci při vyvolání asynchronních operací z objektu ICommand. Tím se zajistí, že všechny výjimky v asynchronní logice příkazů je možné zpracovat. Další informace najdete v tématu Asynchronní programování: Vzory pro asynchronní aplikace MVVM: Příkazy.

Pečlivě zvolte kontejner injektáže závislostí.

Kontejnery injektáže závislostí přinášejí do mobilních aplikací další omezení výkonu. Registrace a řešení typů v kontejneru má náklady na výkon kvůli použití reflexe kontejneru při vytváření jednotlivých typů, zejména pokud jsou závislosti rekonstruovány pro každou navigaci na stránce v aplikaci. Pokud existuje mnoho nebo hlubokých závislostí, mohou se náklady na vytvoření výrazně zvýšit. Kromě toho může registrace typu, která se obvykle vyskytuje při spuštění aplikace, mít výrazný dopad na dobu spuštění, která závisí na použitém kontejneru.

Jako alternativu může být injektáž závislostí výkonnější implementací ručně pomocí továren.

Vytváření aplikací prostředí

Xamarin.Forms Aplikace prostředí poskytují názorné navigační prostředí na základě informačních panelů a karet. Pokud je možné uživatelské prostředí aplikace implementovat s prostředím Shell, je užitečné to udělat. Aplikace prostředí pomáhají vyhnout se špatnému prostředí při spuštění, protože stránky se vytvářejí na vyžádání v reakci na navigaci místo při spuštění aplikace, což se vyskytuje u aplikací, které používají tabbedPage. Další informace najdete v tématu Xamarin.Forms Shell.

Použití CollectionView místo ListView

CollectionView je zobrazení pro prezentaci seznamů dat pomocí různých specifikací rozložení. Poskytuje flexibilnější a výkonnější alternativu k ListView. Další informace naleznete v tématu Xamarin.Forms CollectionView.

Optimalizace výkonu ListView

Při použití ListViewexistuje řada uživatelských prostředí, která by se měla optimalizovat:

  • Inicializace – časový interval začínající při vytvoření ovládacího prvku a ukončení, kdy se položky zobrazují na obrazovce.
  • Posouvání – možnost procházet seznamem a zajistit, aby uživatelské rozhraní nezpožďuje za dotykovými gesty.
  • Interakce s přidáváním, odstraňováním a výběrem položek

Ovládací ListView prvek vyžaduje, aby aplikace dodála data a šablony buněk. Jak toho dosáhnete, bude mít velký dopad na výkon ovládacího prvku. Další informace naleznete v tématu ListView Performance.

Optimalizace prostředků image

Zobrazení prostředků obrázků může výrazně zvýšit nároky na paměť aplikace. Proto by měly být vytvořeny pouze v případě potřeby a měly by být uvolněny, jakmile je aplikace už nevyžaduje. Pokud například aplikace zobrazuje obrázek čtením dat z datového proudu, ujistěte se, že se datový proud vytvoří jenom tehdy, když je to potřeba, a ujistěte se, že se stream uvolní, když už není potřeba. Toho lze dosáhnout vytvořením datového proudu při vytvoření stránky nebo při Page.Appearing spuštění události a následným zrušením streamu Page.Disappearing při spuštění události.

Při stahování obrázku pro zobrazení pomocí ImageSource.FromUri metody uložit stažený obrázek do mezipaměti tím, že zajistíte, že UriImageSource.CachingEnabled vlastnost je nastavena na true. Další informace naleznete v tématu Práce s obrázky.

Další informace naleznete v tématu Optimalizace prostředků image.

Zmenšení velikosti vizuálního stromu

Zmenšení počtu prvků na stránce urychlí vykreslení stránky. Existují dvě hlavní techniky, jak toho dosáhnout. Prvním je skrýt prvky, které nejsou viditelné. Vlastnost IsVisible každého prvku určuje, zda prvek má být součástí vizuálního stromu, nebo ne. Proto pokud prvek není viditelný, protože je skrytý za jinými prvky, buď odeberte prvek nebo nastavte jeho IsVisible vlastnost na false.

Druhou technikou je odebrání nepotřebných prvků. Například následující příklad kódu ukazuje rozložení stránky obsahující více Label objektů:

<StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Hello" />
    </StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Welcome to the App!" />
    </StackLayout>
    <StackLayout Padding="20,20,0,0">
        <Label Text="Downloading Data..." />
    </StackLayout>
</StackLayout>

Stejné rozložení stránky lze udržovat s nižším počtem prvků, jak je znázorněno v následujícím příkladu kódu:

<StackLayout Padding="20,35,20,20" Spacing="25">
  <Label Text="Hello" />
  <Label Text="Welcome to the App!" />
  <Label Text="Downloading Data..." />
</StackLayout>

Zmenšení velikosti slovníku prostředků aplikace

Všechny prostředky, které se používají v celé aplikaci, by měly být uložené ve slovníku prostředků aplikace, aby nedocházelo k duplikaci. To vám pomůže snížit množství XAML, které je potřeba analyzovat v celé aplikaci. Následující příklad kódu ukazuje HeadingLabelStyle prostředek, který se používá pro celou aplikaci, a tak je definován ve slovníku prostředků aplikace:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Resources.App">
     <Application.Resources>
         <ResourceDictionary>
            <Style x:Key="HeadingLabelStyle" TargetType="Label">
                <Setter Property="HorizontalOptions" Value="Center" />
                <Setter Property="FontSize" Value="Large" />
                <Setter Property="TextColor" Value="Red" />
            </Style>
         </ResourceDictionary>
     </Application.Resources>
</Application>

Kód XAML, který je specifický pro stránku, by se ale neměl zahrnout do slovníku prostředků aplikace, protože prostředky se pak budou analyzovat při spuštění aplikace místo toho, aby je vyžadovala stránka. Pokud prostředek používá stránka, která není úvodní stránkou, měla by být umístěna ve slovníku prostředků pro tuto stránku, a proto pomáhá snížit kód XAML, který se parsuje při spuštění aplikace. Následující příklad kódu ukazuje HeadingLabelStyle prostředek, který je pouze na jedné stránce, a tak je definován ve slovníku prostředků stránky:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Test.HomePage"
             Padding="0,20,0,0">
    <ContentPage.Resources>
        <ResourceDictionary>
          <Style x:Key="HeadingLabelStyle" TargetType="Label">
              <Setter Property="HorizontalOptions" Value="Center" />
              <Setter Property="FontSize" Value="Large" />
              <Setter Property="TextColor" Value="Red" />
          </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

Další informace o prostředcích aplikace naleznete v tématu Styly XAML.

Použití vlastního vzoru rendereru

Většina Xamarin.Forms tříd renderer zveřejňuje metodu OnElementChanged , která je volána při Xamarin.Forms vytvoření vlastního ovládacího prvku pro vykreslení odpovídajícího nativního ovládacího prvku. Vlastní třídy rendereru, v každém projektu platformy, pak přepsat tuto metodu vytvoření instance a přizpůsobení nativního ovládacího prvku. Metoda SetNativeControl se používá k vytvoření instance nativního ovládacího prvku a tato metoda také přiřadí odkaz na ovládací prvek vlastnosti Control .

V některých případech však lze metodu OnElementChanged volat vícekrát. Proto, aby se zabránilo nevracení paměti, které může mít dopad na výkon, je třeba při vytváření instance nového nativního ovládacího prvku věnovat pozornost. Přístup k použití při vytváření instance nového nativního ovládacího prvku ve vlastním rendereru se zobrazí v následujícím příkladu kódu:

protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
  base.OnElementChanged (e);

  if (e.OldElement != null)
  {
    // Unsubscribe from event handlers and cleanup any resources
  }

  if (e.NewElement != null)
  {
    if (Control == null)
    {
      // Instantiate the native control with the SetNativeControl method
    }
    // Configure the control and subscribe to event handlers
  }
}

Nový nativní ovládací prvek by měl být vytvořena pouze jednou, pokud Control je nullvlastnost . Kromě toho by měl být ovládací prvek vytvořen, nakonfigurován a obslužné rutiny událostí odebírané, když je vlastní renderer připojen k novému Xamarin.Forms prvku. Podobně by všechny obslužné rutiny událostí, které byly přihlášeny k odběru, měly být odhlášené pouze v případě, že je prvek, který renderer připojí ke změnám. Přijetí tohoto přístupu vám pomůže vytvořit efektivní výkon vlastního vykreslovacího modulu, který netrpí nevracením paměti.

Důležité

Metoda SetNativeControl by měla být vyvolána pouze v případě e.NewElement , že vlastnost není null, a Control vlastnost je null.

Další informace o vlastních rendererech naleznete v tématu Přizpůsobení ovládacích prvků na jednotlivých platformách.