mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
switch to main thread async
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -77,4 +77,4 @@ public class Watcher : Observable
|
||||
watcher.IsCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user