switch to main thread async

This commit is contained in:
DismissedLight
2022-08-18 20:59:07 +08:00
parent 9c70105c61
commit 625f3ee42c
8 changed files with 145 additions and 39 deletions

View File

@@ -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;
/// </summary>
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();
/// <summary>
/// 响应激活事件
/// 激活事件一般不会在UI线程上触发
/// </summary>
/// <param name="sender">发送方</param>
/// <param name="args">激活参数</param>
@@ -49,19 +49,7 @@ internal static class Activation
isActivating = true;
}
App.Window = Ioc.Default.GetRequiredService<MainWindow>();
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
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<MainWindow>();
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
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<INavigationService>()
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true);
});
await Program.SwitchToMainThreadAsync();
INavigationAwaiter navigationAwaiter = new NavigationExtra("InvokeByUri");
await Ioc.Default
.GetRequiredService<INavigationService>()
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
.ConfigureAwait(false);
break;
}
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Threading;
/// <summary>
/// 同步上下文等待体
/// </summary>
public struct SynchronizationContextAwaitable
{
private readonly SynchronizationContext context;
/// <summary>
/// 构造一个新的同步上下文等待体
/// </summary>
/// <param name="context">同步上下文</param>
public SynchronizationContextAwaitable(SynchronizationContext context)
{
this.context = context;
}
/// <summary>
/// 获取等待器
/// </summary>
/// <returns>等待器</returns>
public SynchronizationContextAwaiter GetAwaiter()
{
return new SynchronizationContextAwaiter(context);
}
}

View File

@@ -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;
/// <summary>
/// 同步上下文等待器
/// </summary>
public struct SynchronizationContextAwaiter : INotifyCompletion
{
private static readonly SendOrPostCallback PostCallback = state => ((Action)state!)();
private readonly SynchronizationContext context;
/// <summary>
/// 构造一个新的同步上下文等待器
/// </summary>
/// <param name="context">同步上下文</param>
public SynchronizationContextAwaiter(SynchronizationContext context)
{
this.context = context;
}
/// <summary>
/// 是否完成
/// </summary>
public bool IsCompleted => context == SynchronizationContext.Current;
/// <summary>
/// 完成操作
/// </summary>
/// <param name="continuation">后续操作</param>
[SuppressMessage("", "VSTHRD001")]
public void OnCompleted(Action continuation) => context.Post(PostCallback, continuation);
/// <summary>
/// 获取执行结果
/// </summary>
public void GetResult()
{
}
}

View File

@@ -77,4 +77,4 @@ public class Watcher : Observable
watcher.IsCompleted = true;
}
}
}
}

View File

@@ -10,6 +10,20 @@ namespace Snap.Hutao.Core.Validation;
/// </summary>
public static class Must
{
/// <summary>
/// Throws an <see cref="ArgumentException"/> if a condition does not evaluate to true.
/// </summary>
/// <param name="condition">The condition to check.</param>
/// <param name="message">message</param>
/// <param name="parameterName">The name of the parameter to blame in the exception, if thrown.</param>
public static void Argument([DoesNotReturnIf(false)] bool condition, string? message, [CallerArgumentExpression("condition")] string? parameterName = null)
{
if (!condition)
{
throw new ArgumentException(message, parameterName);
}
}
/// <summary>
/// Unconditionally throws an <see cref="NotSupportedException"/>.
/// </summary>

View File

@@ -10,8 +10,9 @@ namespace Snap.Hutao.Core;
/// <summary>
/// 检测 WebView2运行时 是否存在
/// 不再使用注册表检查方式
/// 必须为抽象类才能使用泛型日志器
/// </summary>
internal static class WebView2Helper
internal abstract class WebView2Helper
{
private static bool hasEverDetected = false;
private static bool isSupported = false;

View File

@@ -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;
/// <summary>
/// 主线程调度器队列
@@ -23,6 +25,15 @@ public static class Program
get => Must.NotNull(dispatcherQueue!);
}
/// <summary>
/// 异步切换到主线程
/// </summary>
/// <returns>等待体</returns>
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();
});

View File

@@ -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);
}
/// <summary>
@@ -107,9 +108,10 @@ internal class InfoBarService : IInfoBarService
/// <param name="title">标题</param>
/// <param name="message">消息</param>
/// <param name="delay">关闭延迟</param>
[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;
}
}