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
Grid
se pokuste zajistit, aby bylo co nejvíce řádků a sloupců nastaveno naAuto
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 sGridUnitType.Star
hodnotou výčtu za předpokladu, že nadřazený strom dodržuje tyto pokyny rozložení. - Nenastavujte
VerticalOptions
aHorizontalOptions
vlastnosti rozložení, pokud není potřeba. Výchozí hodnotyLayoutOptions.Fill
aLayoutOptions.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í ,
AbsoluteLayout
vyhněte se použitíAbsoluteLayout.AutoSize
vlastnosti, kdykoli je to možné. - Při použití ,
StackLayout
ujistěte se, že je nastavenaLayoutOptions.Expands
pouze 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ímTranslationX
aTranslationY
vlastností. Případně můžete podtříduLayout<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šechnyLabel
instance naNoWrap
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í objektuTask
, 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 metoduTaskFactory.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ýhodyTask
programovatelnosti a umožňují řídit životnost a dokončení přidruženéhoTask
. Další informace naleznete v tématu Příroda TaskCompletionSource.Vrácení objektu
Task
namísto vrácení očekávanéhoTask
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řteasync 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átitvoid
. Další informace najdete v tématu Vyhněte se asynchronnímu Voidu. - Nekombinujte blokující a asynchronní kód voláním
Task.Wait
Task.Result
, neboGetAwaiter().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 metoduGetAwaiter().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 objektuICommand
. 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í ListView
existuje ř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 null
vlastnost . 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.