From 625f3ee42c2cd0802a69658801da70645aa9e00c Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Thu, 18 Aug 2022 20:59:07 +0800
Subject: [PATCH] switch to main thread async
---
.../Snap.Hutao/Core/LifeCycle/Activation.cs | 57 ++++++++++---------
.../SynchronizationContextAwaitable.cs | 30 ++++++++++
.../SynchronizationContextAwaiter.cs | 43 ++++++++++++++
.../Snap.Hutao/Core/Threading/Watcher.cs | 2 +-
.../Snap.Hutao/Core/Validation/Must.cs | 14 +++++
.../Snap.Hutao/Core/WebView2Helper.cs | 3 +-
src/Snap.Hutao/Snap.Hutao/Program.cs | 13 ++++-
.../Snap.Hutao/Service/InfoBarService.cs | 22 +++----
8 files changed, 145 insertions(+), 39 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaitable.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaiter.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
index 31f81298..abbbc6a3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using CommunityToolkit.WinUI;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
@@ -14,11 +13,12 @@ namespace Snap.Hutao.Core.LifeCycle;
///
internal static class Activation
{
- public static volatile bool isActivating = false;
- public static object activationLock = new object();
+ private static volatile bool isActivating = false;
+ private static object activationLock = new();
///
/// 响应激活事件
+ /// 激活事件一般不会在UI线程上触发
///
/// 发送方
/// 激活参数
@@ -49,19 +49,7 @@ internal static class Activation
isActivating = true;
}
- App.Window = Ioc.Default.GetRequiredService();
-
- IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
- await infoBarService.WaitInitializationAsync();
-
- if (args.Kind == ExtendedActivationKind.Protocol)
- {
- if (args.TryGetProtocolActivatedUri(out Uri? uri))
- {
- infoBarService.Information(uri.ToString());
- await HandleUrlActivationAsync(uri);
- }
- }
+ await HandleActivationCoreAsync(args).ConfigureAwait(false);
lock (activationLock)
{
@@ -69,20 +57,37 @@ internal static class Activation
}
}
+ private static async Task HandleActivationCoreAsync(AppActivationArguments args)
+ {
+ App.Window = Ioc.Default.GetRequiredService();
+
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+ await infoBarService.WaitInitializationAsync().ConfigureAwait(false);
+
+ if (args.Kind == ExtendedActivationKind.Protocol)
+ {
+ if (args.TryGetProtocolActivatedUri(out Uri? uri))
+ {
+ infoBarService.Information(uri.ToString());
+ await HandleUrlActivationAsync(uri).ConfigureAwait(false);
+ }
+ }
+ }
+
private static async Task HandleUrlActivationAsync(Uri uri)
{
UriBuilder builder = new(uri);
- Requires.Argument(builder.Scheme == "hutao", nameof(uri), "uri 的协议不正确");
+ Must.Argument(builder.Scheme == "hutao", "uri 的协议不正确");
string category = builder.Host.ToLowerInvariant();
string action = builder.Path.ToLowerInvariant();
- string rawParameter = builder.Query;
+ string rawParameter = builder.Query.ToLowerInvariant();
switch (category)
{
case "achievement":
{
- await HandleAchievementActionAsync(action, rawParameter);
+ await HandleAchievementActionAsync(action, rawParameter).ConfigureAwait(false);
break;
}
}
@@ -94,13 +99,13 @@ internal static class Activation
{
case "/import":
{
- await Program.UIDispatcherQueue.EnqueueAsync(async () =>
- {
- INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
- await Ioc.Default
- .GetRequiredService()
- .NavigateAsync(navigationAwaiter, true);
- });
+ await Program.SwitchToMainThreadAsync();
+
+ INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
+ await Ioc.Default
+ .GetRequiredService()
+ .NavigateAsync(navigationAwaiter, true)
+ .ConfigureAwait(false);
break;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaitable.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaitable.cs
new file mode 100644
index 00000000..d131549e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaitable.cs
@@ -0,0 +1,30 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Core.Threading;
+
+///
+/// 同步上下文等待体
+///
+public struct SynchronizationContextAwaitable
+{
+ private readonly SynchronizationContext context;
+
+ ///
+ /// 构造一个新的同步上下文等待体
+ ///
+ /// 同步上下文
+ public SynchronizationContextAwaitable(SynchronizationContext context)
+ {
+ this.context = context;
+ }
+
+ ///
+ /// 获取等待器
+ ///
+ /// 等待器
+ public SynchronizationContextAwaiter GetAwaiter()
+ {
+ return new SynchronizationContextAwaiter(context);
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaiter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaiter.cs
new file mode 100644
index 00000000..e735fbcd
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SynchronizationContextAwaiter.cs
@@ -0,0 +1,43 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+namespace Snap.Hutao.Core.Threading;
+
+///
+/// 同步上下文等待器
+///
+public struct SynchronizationContextAwaiter : INotifyCompletion
+{
+ private static readonly SendOrPostCallback PostCallback = state => ((Action)state!)();
+ private readonly SynchronizationContext context;
+
+ ///
+ /// 构造一个新的同步上下文等待器
+ ///
+ /// 同步上下文
+ public SynchronizationContextAwaiter(SynchronizationContext context)
+ {
+ this.context = context;
+ }
+
+ ///
+ /// 是否完成
+ ///
+ public bool IsCompleted => context == SynchronizationContext.Current;
+
+ ///
+ /// 完成操作
+ ///
+ /// 后续操作
+ [SuppressMessage("", "VSTHRD001")]
+ public void OnCompleted(Action continuation) => context.Post(PostCallback, continuation);
+
+ ///
+ /// 获取执行结果
+ ///
+ public void GetResult()
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/Watcher.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/Watcher.cs
index 0cf7c88c..70822137 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/Watcher.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/Watcher.cs
@@ -77,4 +77,4 @@ public class Watcher : Observable
watcher.IsCompleted = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
index 9c79c058..6189def4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
@@ -10,6 +10,20 @@ namespace Snap.Hutao.Core.Validation;
///
public static class Must
{
+ ///
+ /// Throws an if a condition does not evaluate to true.
+ ///
+ /// The condition to check.
+ /// message
+ /// The name of the parameter to blame in the exception, if thrown.
+ public static void Argument([DoesNotReturnIf(false)] bool condition, string? message, [CallerArgumentExpression("condition")] string? parameterName = null)
+ {
+ if (!condition)
+ {
+ throw new ArgumentException(message, parameterName);
+ }
+ }
+
///
/// Unconditionally throws an .
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
index 32aeac91..9cea7ea8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
@@ -10,8 +10,9 @@ namespace Snap.Hutao.Core;
///
/// 检测 WebView2运行时 是否存在
/// 不再使用注册表检查方式
+/// 必须为抽象类才能使用泛型日志器
///
-internal static class WebView2Helper
+internal abstract class WebView2Helper
{
private static bool hasEverDetected = false;
private static bool isSupported = false;
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index 818c9fd0..32e63b58 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -3,6 +3,7 @@
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
+using Snap.Hutao.Core.Threading;
using System.Runtime.InteropServices;
using WinRT;
@@ -14,6 +15,7 @@ namespace Snap.Hutao;
public static class Program
{
private static volatile DispatcherQueue? dispatcherQueue;
+ private static volatile SynchronizationContext? context;
///
/// 主线程调度器队列
@@ -23,6 +25,15 @@ public static class Program
get => Must.NotNull(dispatcherQueue!);
}
+ ///
+ /// 异步切换到主线程
+ ///
+ /// 等待体
+ public static SynchronizationContextAwaitable SwitchToMainThreadAsync()
+ {
+ return new SynchronizationContextAwaitable(context!);
+ }
+
[DllImport("Microsoft.ui.xaml.dll")]
private static extern void XamlCheckProcessRequirements();
@@ -36,7 +47,7 @@ public static class Program
Application.Start(p =>
{
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
- SynchronizationContext context = new DispatcherQueueSynchronizationContext(dispatcherQueue);
+ context = new DispatcherQueueSynchronizationContext(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(context);
_ = new App();
});
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
index e151d575..8789c9f2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
@@ -90,14 +90,15 @@ internal class InfoBarService : IInfoBarService
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
}
- private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
+ [SuppressMessage("", "VSTHRD100")]
+ private async void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
{
if (infoBarStack is null)
{
return;
}
- infoBarStack.DispatcherQueue.TryEnqueue(() => PrepareInfoBarAndShowInternal(severity, title, message, delay));
+ await PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).ConfigureAwait(false);
}
///
@@ -107,9 +108,10 @@ internal class InfoBarService : IInfoBarService
/// 标题
/// 消息
/// 关闭延迟
- [SuppressMessage("", "VSTHRD100", Justification = "只能通过 async void 方法使控件在主线程创建")]
- private async void PrepareInfoBarAndShowInternal(InfoBarSeverity severity, string? title, string? message, int delay)
+ private async Task PrepareInfoBarAndShowInternalAsync(InfoBarSeverity severity, string? title, string? message, int delay)
{
+ await Program.SwitchToMainThreadAsync();
+
InfoBar infoBar = new()
{
Severity = severity,
@@ -128,12 +130,12 @@ internal class InfoBarService : IInfoBarService
}
}
- private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
+ [SuppressMessage("", "VSTHRD100")]
+ private async void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
{
- Must.NotNull(infoBarStack!).DispatcherQueue.TryEnqueue(() =>
- {
- infoBarStack.Children.Remove(sender);
- sender.Closed -= OnInfoBarClosed;
- });
+ await Program.SwitchToMainThreadAsync();
+
+ Must.NotNull(infoBarStack!).Children.Remove(sender);
+ sender.Closed -= OnInfoBarClosed;
}
}