From 7ef0c95a730e70aa1d92b2b0c75e7d0a298d2f17 Mon Sep 17 00:00:00 2001 From: Baddi86 Date: Wed, 2 Sep 2026 21:46:01 +0200 Subject: [PATCH] =?UTF-8?q?Optimierung=20des=20Startvorgangs,=20lazy=20loa?= =?UTF-8?q?d,=20async=20im=20thread=20pool=20oder=20w=C3=A4hrend=20des=20S?= =?UTF-8?q?tartbildschirms=20vor=20dem=20Mainwindow.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LehrerApp.Desktop/App.axaml.cs | 26 ++++++++++--------- .../Services/AnnualPlanSyncService.cs | 6 ++--- .../Services/UntisSyncService.cs | 6 ++--- .../ViewModels/DashboardViewModel.cs | 1 - 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/LehrerApp.Desktop/App.axaml.cs b/LehrerApp.Desktop/App.axaml.cs index ea26c44..90b2e08 100644 --- a/LehrerApp.Desktop/App.axaml.cs +++ b/LehrerApp.Desktop/App.axaml.cs @@ -52,7 +52,7 @@ public class App : Application base.OnFrameworkInitializationCompleted(); } - private static void ContinueStartup( + private static async void ContinueStartup( IClassicDesktopStyleApplicationLifetime desktop, SplashWindow splash) { // Verschlüsselte Datenbank (13.3.4): Passwort abfragen, bevor die Datenbank @@ -62,11 +62,10 @@ public class App : Application var promptVm = new DbPasswordPromptViewModel( new DatabaseEncryptionService(), AppBootstrapper.ResolveDbPath()); var promptWindow = new DbPasswordPromptWindow { DataContext = promptVm }; - promptVm.OnUnlocked = password => + promptVm.OnUnlocked = async password => { AppBootstrapper.DbPassword = password; - StartMainApp(desktop, splash); - promptWindow.Close(); + await StartMainAppAsync(desktop, promptWindow); }; desktop.MainWindow = promptWindow; promptWindow.Show(); @@ -74,10 +73,10 @@ public class App : Application return; } - StartMainApp(desktop, splash); + await StartMainAppAsync(desktop, splash); } - private static void StartMainApp( + private static async Task StartMainAppAsync( IClassicDesktopStyleApplicationLifetime desktop, Window? windowToClose = null) { _serviceProvider = AppBootstrapper.BuildServices(); @@ -98,13 +97,16 @@ public class App : Application var mainVm = Services.GetRequiredService(); WireCallbacks(mainVm); - // DI-Singletons werden erst beim ersten Auflösen erzeugt. Ohne dieses explizite - // Auflösen entstand der Timer des optionalen WebUntis-Abgleichs erst, wenn die - // Einstellungen geöffnet oder ein manueller Abruf gestartet wurde. Den Dienst beim - // Anwendungsstart aktivieren und wie den Jahresplan sofort einmal abgleichen; danach - // übernimmt sein stündlicher Timer. + // Beide optionalen Erstabgleiche noch unter dem Splashscreen abschließen. Ihre + // CPU-/Datenbankarbeit läuft innerhalb der Dienste im Threadpool; dadurch öffnet das + // Hauptfenster mit fertigem Datenstand und friert nicht kurz danach ein. Das Auflösen + // aktiviert zugleich die periodischen Timer. + var initialPolls = new List(); if (Services.GetService() is { } untisSync) - _ = untisSync.PollAsync(); + initialPolls.Add(untisSync.PollAsync()); + if (Services.GetService() is { } annualPlanSync) + initialPolls.Add(annualPlanSync.PollAsync()); + await Task.WhenAll(initialPolls); var main = new MainWindow { DataContext = mainVm }; main.EnableWindowSizePersistence(Services.GetRequiredService()); diff --git a/LehrerApp.Desktop/Services/AnnualPlanSyncService.cs b/LehrerApp.Desktop/Services/AnnualPlanSyncService.cs index 88f5d81..d1e3031 100644 --- a/LehrerApp.Desktop/Services/AnnualPlanSyncService.cs +++ b/LehrerApp.Desktop/Services/AnnualPlanSyncService.cs @@ -38,7 +38,7 @@ public sealed class AnnualPlanSyncService : IDisposable public async Task PollAsync() { - if (!await _gate.WaitAsync(0)) return; + if (!await _gate.WaitAsync(0).ConfigureAwait(false)) return; try { var url = _settings.GetIcalUrl(); @@ -47,7 +47,7 @@ public sealed class AnnualPlanSyncService : IDisposable string icsText; try { - icsText = await _http.GetStringAsync(url); + icsText = await _http.GetStringAsync(url).ConfigureAwait(false); } catch (Exception ex) { @@ -59,7 +59,7 @@ public sealed class AnnualPlanSyncService : IDisposable AnnualPlanPollResult result; try { - result = ProcessIcsText(icsText); + result = await Task.Run(() => ProcessIcsText(icsText)).ConfigureAwait(false); } catch (Exception ex) { diff --git a/LehrerApp.Desktop/Services/UntisSyncService.cs b/LehrerApp.Desktop/Services/UntisSyncService.cs index 3e9b009..a4818f4 100644 --- a/LehrerApp.Desktop/Services/UntisSyncService.cs +++ b/LehrerApp.Desktop/Services/UntisSyncService.cs @@ -67,7 +67,7 @@ public class UntisSyncService : IDisposable public async Task PollAsync() { - if (!await _gate.WaitAsync(0)) return; + if (!await _gate.WaitAsync(0).ConfigureAwait(false)) return; try { var url = _settings.GetIcalUrl(); @@ -76,7 +76,7 @@ public class UntisSyncService : IDisposable string icsText; try { - icsText = await _http.GetStringAsync(url); + icsText = await _http.GetStringAsync(url).ConfigureAwait(false); } catch (Exception ex) { @@ -88,7 +88,7 @@ public class UntisSyncService : IDisposable UntisPollResult result; try { - result = ProcessIcsText(icsText); + result = await Task.Run(() => ProcessIcsText(icsText)).ConfigureAwait(false); } catch (Exception ex) { diff --git a/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs b/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs index 0c991ba..685f063 100644 --- a/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs +++ b/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs @@ -170,7 +170,6 @@ public partial class DashboardViewModel : ObservableObject if (annualPlanSync is not null) { annualPlanSync.DataChanged += () => Avalonia.Threading.Dispatcher.UIThread.Post(LoadCalendar); - _ = annualPlanSync.PollAsync(); } LoadDashboardCards(); Load();