Optimizacija performansi .NET MAUI aplikacija u 2026: brže pokretanje, glatkiji UI i manje memorije

Praktični vodič kroz optimizaciju .NET MAUI 9 aplikacija u 2026: brže pokretanje s NativeAOT, glatkiji CollectionView, sprječavanje curenja memorije i smanjenje veličine APK/IPA paketa.

Optimizacija .NET MAUI 9: Vodič 2026

Ažurirano: 20. lipnja 2026.

Optimizacija performansi .NET MAUI aplikacija u 2026. svodi se na tri stupa: brzo pokretanje (cold start ispod 2 sekunde), glatko renderiranje listi (stabilnih 60 FPS) i kontroliranu potrošnju memorije bez curenja. Uz .NET 9 MAUI i nadolazeći .NET 10, ključne poluge su NativeAOT, ispravna upotreba CollectionView, optimizacija handlera te profiliranje pomoću dotnet-trace. U ovom tekstu dijelim konkretne mjere s mjerljivim učinkom i obrasce koje sam pokupio iz nekoliko produkcijskih MAUI projekata.

  • NativeAOT u .NET 9 MAUI smanjuje vrijeme pokretanja iOS aplikacija za 30–50% i veličinu binarija do 2,5x.
  • CollectionView s ItemsUpdatingScrollMode="KeepItemsInView" i virtualizacijom zamjenjuje sporiji ListView iz Xamarin.Forms ere.
  • Curenje memorije najčešće dolazi iz handlera koji drže reference na Page; ručno odjavljivanje događaja u OnDisappearing spašava aplikaciju.
  • Profiliranje pomoću dotnet-trace i Visual Studio Profilera daje točne brojeve umjesto nagađanja.
  • XAML kompajliranje, lijeno učitavanje stranica i optimizacija slika (WebP, vektorska grafika) najjednostavnije su pobjede.
  • Shell navigacija s registriranim rutama brža je od ručno složenog NavigationPage stoga kada se kombinira s DI kontejnerom.

Zašto je moja .NET MAUI aplikacija spora pri pokretanju?

Sporo pokretanje (cold start) MAUI aplikacije gotovo uvijek dolazi iz kombinacije triju razloga: prevelika količina koda koji se izvršava u MauiProgram.CreateMauiApp, učitavanje cijelog grafa stranica i ViewModela odjednom te nedostatak AOT kompilacije. Na iOS uređajima ovo se dodatno pogoršava jer JIT nije dozvoljen, pa cjelokupni IL kod prolazi kroz interpreter ako Mono AOT nije konfiguriran.

Iz mog iskustva, mjerenje je uvijek prvi korak (i da, znam koliko je to dosadno). U Visual Studiju 17.10+ otvorite Diagnostic Tools i pratite "Startup time" metriku. Tipičan cilj je 1,5–2 sekunde cold start na srednjim Android uređajima (npr. Pixel 6a) i ispod 1 sekunde na iPhoneu 13 i novijim. Ako vam pokretanje traje 4+ sekunde, gotovo sigurno učitavate prevelik graf servisa odjednom.

Ključna intervencija je odgađanje registracije nekritičnih servisa. Sve što nije potrebno za prvi prikazani ekran (analytics, sync motori, telemetrija) registrirajte kao Lazy<T> i instancirajte ih nakon prvog renderiranja:

builder.Services.AddSingleton<Lazy<ISyncEngine>>(sp =>
    new Lazy<ISyncEngine>(() => new SyncEngine(sp.GetRequiredService<IHttpClientFactory>())));

// U AppShell.xaml.cs:
protected override void OnAppearing()
{
    base.OnAppearing();
    Dispatcher.DispatchDelayed(TimeSpan.FromSeconds(2), () =>
    {
        var sync = Handler.MauiContext.Services.GetRequiredService<Lazy<ISyncEngine>>();
        _ = sync.Value.StartAsync();
    });
}

Druga česta zamka je registracija svih stranica kao Singleton umjesto Transient. Singleton stranice ostaju u memoriji i njihovi handleri mogu blokirati garbage collector. Ovo sam vidio dvaput, oba puta s istim simptomom: aplikacija se nakon sat vremena rada ponaša kao da hoda kroz blato.

Kako NativeAOT ubrzava .NET MAUI u 2026.

NativeAOT, stabiliziran u .NET 9 MAUI za iOS i Mac Catalyst krajem 2024., u potpunosti uklanja runtime kompilaciju tako što sav .NET kod pretvara u nativni strojni kod u trenutku buildanja. Rezultat je dramatično brže pokretanje (Microsoftova mjerenja pokazuju 30–50% poboljšanje cold starta na iOS-u) i 1,8–2,5x manji binarni paket. Android NativeAOT je u .NET 10 ušao u javni preview, prema .NET 9 MAUI najavi, a stabilna verzija očekuje se u .NET 11.

Aktivacija je jednostavan dodatak u .csproj:

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) or $(TargetFramework.Contains('-maccatalyst'))">
    <PublishAot>true</PublishAot>
    <StripSymbols>true</StripSymbols>
    <OptimizationPreference>Speed</OptimizationPreference>
</PropertyGroup>

Pripazite: NativeAOT zabranjuje runtime kod koji koristi System.Reflection.Emit, dinamičke proxyje i Assembly.Load na netrivijalan način. Ako koristite stariju verziju AutoMappera ili Newtonsoft.Json s dinamičkim tipovima, build će prijaviti AOT analyzer upozorenja. Preporučujem prelazak na System.Text.Json source generator, koji je usput i dramatično brži:

[JsonSerializable(typeof(Product))]
[JsonSerializable(typeof(List<Product>))]
internal partial class AppJsonContext : JsonSerializerContext { }

var products = JsonSerializer.Deserialize(json, AppJsonContext.Default.ListProduct);

Optimizacija CollectionView i renderiranje listi

Najčešća pritužba korisnika MAUI aplikacija je "lista zapinje pri scrollanju". Krivac je gotovo uvijek pogrešna konfiguracija CollectionView ili zaostali ListView iz Xamarin migracije. CollectionView koristi virtualizaciju samo ako su mu zadovoljeni uvjeti: fiksne dimenzije stavki ili pravilno postavljen ItemSizingStrategy.

Tri ključna podešavanja koja gotovo svaka MAUI aplikacija propušta:

<CollectionView ItemsSource="{Binding Products}"
                ItemSizingStrategy="MeasureFirstItem"
                ItemsUpdatingScrollMode="KeepItemsInView"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="vm:ProductVm">
            <Grid HeightRequest="80" Padding="12,8">
                <Label Text="{Binding Name}" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Dodavanje eksplicitnog HeightRequest u root template ukida potrebu za mjerenjem svake stavke. x:DataType aktivira compiled bindings, koja su 8–10x brža od refleksijskih.

Inkrementalno učitavanje umjesto cijele kolekcije

Ako učitavate 500+ stavki odjednom, framework mora sve provući kroz INotifyCollectionChanged. Bolje rješenje je ObservableRangeCollection iz CommunityToolkit.Mvvm paketa, koja batcha ažuriranja, ili paginacija od 20–50 stavki uz RemainingItemsThresholdReachedCommand.

Sprječavanje curenja memorije u handlerima

Curenje memorije u MAUI 2024–2026. najčešće potiče iz Handler arhitekture: Page drži referencu na ViewHandler, koji drži referencu na nativni view, koji ponekad zadržava jaku referencu na Page kroz delegate. Rezultat? Stranica se nikad ne oslobađa nakon navigacije. Točno na taj bug sam naletio prošlog ljeta dok smo profilirali aplikaciju za jednog klijenta, i izgubio sam dva dana prije nego što sam shvatio o čemu je riječ.

Dijagnostika je jednostavna pomoću dotnet-gcdump:

dotnet tool install -g dotnet-gcdump
dotnet-gcdump collect -p <PID>

Otvorite snimak u PerfView i potražite više instanci iste stranice; to su one koje su procurile. Tim koji održava MAUI repository objavio je službeni vodič o memory leakovima s popisom poznatih obrazaca.

Tri sigurnosna pravila koja primjenjujem u svakoj produkcijskoj aplikaciji:

  1. Odjavite event handlere u OnDisappearing: Svaki MessagingCenter.Subscribe ili WeakReferenceMessenger registar mora imati par Unsubscribe.
  2. Koristite WeakReferenceMessenger iz CommunityToolkit.Mvvm: Ona automatski oslobađa pretplatnike kad oni izađu iz scopea.
  3. Izbjegavajte lambda capture this u dugotrajnim event handlerima: Umjesto toga koristite imenovane metode.
// LOŠE — capture this, blokira GC stranicu
button.Clicked += (s, e) => this.Counter++;

// BOLJE — imenovana metoda + odjavljivanje
protected override void OnAppearing() { button.Clicked += OnClick; }
protected override void OnDisappearing() { button.Clicked -= OnClick; }
void OnClick(object s, EventArgs e) => Counter++;

Optimizacija slika i resursa

Slike su najveći izvor "neobjašnjivo spore" aplikacije. MAUI projekti često uvoze 2 MB PNG datoteku, render je u Image s HeightRequest="60", a nativni handler odluči učitati cijelu rezoluciju u memoriju. U katalogu od 200 proizvoda to znači 400 MB RAM-a samo za thumbnaile. Da, vidio sam to uživo.

Tri pravila koja eliminiraju problem:

  • Koristite WebP umjesto PNG/JPG: 25–35% manje datoteke uz istu kvalitetu. MAUI 9 podržava WebP nativno i na Androidu i na iOS-u.
  • SVG za ikone: MAUI ih automatski rasterizira u potrebnu rezoluciju. Dodajte ih kao MauiImage u .csproj.
  • FFImageLoading ili nativni image caching: Za remote slike koristite FFImageLoading.Maui ili UraniumUI.Icons.MaterialIcons s ugrađenim diskovskim cacheom.
<ItemGroup>
    <MauiImage Include="Resources\Images\logo.svg" BaseSize="32,32" />
    <MauiImage Include="Resources\Images\hero.webp" Resize="true" BaseSize="800,600" />
</ItemGroup>

Za lokalne pristupe podacima koje često koristite (a opisujemo ih u našem vodiču o SQLite i EF Core lokalnoj bazi za offline MAUI aplikacije), kombinacija lokalnog keširanja slika i prefetchanja kritičnih podataka pri startu daje najbolji subjektivni doživljaj brzine.

Profiliranje pomoću dotnet-trace i Visual Studio Profilera

Bez mjerenja, optimizacija je nagađanje. Tri alata koja moraju biti u standardnom toolkitu svakog MAUI razvojnog inženjera u 2026:

1. dotnet-trace za CPU sampling

dotnet tool install -g dotnet-trace
dotnet-trace ps                  # nađite PID procesa
dotnet-trace collect -p <PID> --providers Microsoft-DotNETCore-SampleProfiler
# Otvorite .nettrace u PerfView ili Visual Studiju

2. Visual Studio Profiler za stranicu pokretanja

Od Visual Studio 17.11 nadalje, "CPU Usage" instrument radi na MAUI iOS i Android targetima izravno. Pokrenite Profiling Session, izaberite CPU Usage, kliknite Start, otvorite aplikaciju, zaustavite mjerenje nakon prvog renderiranog ekrana. Tražite metode iznad 50 ms isključivog vremena.

3. Android Studio Profiler za nativne dijelove

Za Android probleme koji izgledaju kao "rendering jank", Android Studio Profiler ostaje neprevladan. Spojite ga na com.companyname.appname proces i koristite GPU Rendering inspekciju.

Za stalno praćenje performansi u produkciji, povezanost s CI/CD-om koju opisujemo u članku o CI/CD za .NET MAUI s GitHub Actions i Azure DevOps omogućuje automatsko izvršavanje performance smoke testova prije svake distribucije.

Kako smanjiti veličinu APK i IPA datoteka

Velika veličina paketa direktno utječe na konverziju instalacije. Studije Google Playa pokazuju 1% pad konverzije po svakih 6 MB. Tipična neoptimizirana MAUI Android aplikacija ima 35–50 MB; uz dolje navedene mjere, realan cilj je 12–18 MB.

Kombinacija konfiguracija u .csproj koja daje najveći učinak:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <AndroidLinkMode>SdkOnly</AndroidLinkMode>
    <AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
    <AndroidPackageFormat>aab</AndroidPackageFormat>
    <RunAOTCompilation>true</RunAOTCompilation>
    <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
    <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
    <TrimMode>full</TrimMode>
    <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

Kombinacija TrimMode=full i Profiled AOT obično daje 40–55% smanjenje veličine. Microsoft je u službenoj dokumentaciji za NativeAOT u MAUI objavio benchmark prema kojem ista aplikacija s NativeAOT-om na iOS-u smanjuje IPA s 23 MB na 9,8 MB.

Često postavljana pitanja

Je li .NET MAUI brži od Xamarin.Forms?

Da, .NET MAUI 9 je u prosjeku 25–40% brži u pokretanju i 15–20% manje potrošnje memorije od Xamarin.Forms 5.0, prema Microsoftovim mjerenjima. Razlika dolazi iz unified handler arhitekture, uklanjanja renderera i podrške za NativeAOT na iOS-u.

Vrijedi li koristiti NativeAOT u produkciji 2026.?

Za iOS i Mac Catalyst, apsolutno. Stabiliziran je u .NET 9 i koristi ga niz produkcijskih aplikacija. Za Android je u .NET 10 javni preview; preporučujem čekati .NET 11 prije produkcijske uporabe na Androidu.

Zašto CollectionView zapinje pri scrollanju?

Najčešći uzroci su nedostatak fiksne visine u ItemTemplate, nedostajući x:DataType (što onemogućuje compiled bindings) te slike bez eksplicitnih dimenzija. Postavite HeightRequest u template i koristite ItemSizingStrategy="MeasureFirstItem".

Kako mjeriti vrijeme pokretanja .NET MAUI aplikacije?

Najtočnije rezultate daje dotnet-trace s providerom Microsoft-DotNETCore-SampleProfiler, ili Visual Studio Diagnostic Tools s "Startup time" metrikom. Za Android, koristite adb shell am start -W koji vraća TotalTime u milisekundama.

Koja je razlika između TrimMode=full i TrimMode=partial?

partial trima samo eksplicitno označene assemblyje (<IsTrimmable>true</IsTrimmable>) i sigurniji je za projekte koji koriste refleksiju. full trima sve assemblyje i daje 30–50% manju veličinu, ali zahtijeva korištenje source-generated serializera i izbjegavanje dinamičkih tipova.

Editorial Team
O Autoru Editorial Team

Our team of expert writers and editors.