今天写程序中比较重要的一个知识点单例;他是软件生命周期中的第一步,如何启动;因为在传统开发中涉及到进程间通信;启动时间优化;逻辑拆分解耦,等等。这一步很重要,WinUI3对这一步做了什么一的优化和封装,我们先看单例;
在 Application.OnLaunched 中完成此工作可以简化应用。 不过,这在很大程度上取决于应用所执行的其他操作。 如果打算结束重定向,然后终止当前实例,则需要避免执行任何一次性工作(甚至是需要显式撤消的工作)。 在这类情况下,Application.OnLaunched 可能太晚,你可能更希望在应用的 Main 或 wWinMain 函数中完成此工作。这一篇只分析OnLaunched单例的使用,在Main和wWinMain做单例涉及的知识更多,对初学者不是很友好,可以先跳过,感兴趣的可以看示例
Main单例连接
对照文档:
https://docs.microsoft.com/zh-cn/windows/apps/windows-app-sdk/migrate-to-windows-app-sdk/guides/applifecycle#single-instancing-in-main-or-wwinmain
单例的位置主要是Main或者wWinMain函数和Application.OnLaunched;针对于情况的不同,使用的函数也不一样,我们开始学习这一块OnLaunched代码:
using Microsoft.UI.Xaml; using System; using System.Diagnostics; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. namespace AppInstances { /// <summary> /// Provides application-specific behavior to supplement the default Application class. /// </summary> public partial class App : Application { /// <summary> /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// </summary> public App() { this.InitializeComponent(); } /// <summary> /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// </summary> /// <param name="args">Details about the launch request and process.</param> protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { // If this is the first instance launched, then register it as the "main" instance. // If this isn't the first instance launched, then "main" will already be registered, // so retrieve it. var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("Main"); if (!mainInstance.IsCurrent) { //Redirect the activation (and args) to the "main" instance, and exit. var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs(); await mainInstance.RedirectActivationToAsync(activatedEventArgs); Process.GetCurrentProcess().Kill(); return; } m_window = new MainWindow(); m_window.Activate(); } private Window m_window; } }
主要看Override OnLauncher方法;
我们看OnLauncher的方法描述:
翻译后是当这个APP被终端用户正常的启动的时候,这个方法被调用,由其他方式被启动例如打开一个文件时,也会被调用;
启动参数是有关启动请求和流程的详细信息;
最为关键的是这个
var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("Main");
如果这是第一个启动的实例,则将其注册为“Main”实例。如果这不是第一个启动的实例,那么“main”将已经注册,因此请检索它。
我翻了一下文档
FindorRegisterForKey("")方法的描述是:
向平台注册应用实例,如果另一个实例已注册此密钥,则查找现有实例
参数为作为实例键的非空字符串;
返回AppInstance
一个应用实例,表示注册密钥的第一个应用。调用方可以确定该实例是否为当前实例。
同时请注意
应用注册平台后,当其他实例查询应用实例时,将返回该应用。一个应用实例可以使用不同的密钥注册多次。系统缓存为每个实例维护一行,因此,密钥将被覆盖。
我们做以下2个验证:
若要注册文件类型关联,请生成应用,进行启动,然后关闭。
using Microsoft.UI.Xaml; using System; using System.Diagnostics; using Windows.ApplicationModel.Activation; using System.Linq; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. namespace AppInstances { /// <summary> /// Provides application-specific behavior to supplement the default Application class. /// </summary> public partial class App : Application { /// <summary> /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// </summary> public App() { this.InitializeComponent(); } /// <summary> /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// </summary> /// <param name="args">Details about the launch request and process.</param> protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { // If this is the first instance launched, then register it as the "main" instance. // If this isn't the first instance launched, then "main" will already be registered, // so retrieve it. var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("Main"); if (!mainInstance.IsCurrent) { //Redirect the activation (and args) to the "main" instance, and exit. var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs(); await mainInstance.RedirectActivationToAsync(activatedEventArgs); Process.GetCurrentProcess().Kill(); return; } else { mainInstance.Activated += MainInstance_Activated; } m_window = new MainWindow(); m_window.Activate(); } private void MainInstance_Activated(object sender, Microsoft.Windows.AppLifecycle.AppActivationArguments e) { if (e.Kind == Microsoft.Windows.AppLifecycle.ExtendedActivationKind.File) { var fileActivated = (FileActivatedEventArgs)e.Data; var file = fileActivated.Files.FirstOrDefault(); } } private Window m_window; } }
我们在主Main中注册了Activated的消息,如果有程序启动则触发单实例的Action回调给主进程,通过前面的步骤我们注册了*.pdf
我在右键使用AppInstances打开PDF后,监听到file的Path就是我打开的pdf所以单实例这里整体流程就通了;
FindOrRegisterForKey注册多个不同名称我就不验证了,基本没有这种需求;
我需要验证以下跨不同进程的FindOrRegisterForKey是否生效;
方便调试修改代码发送内容到输出中:
private void MainInstance_Activated(object sender, Microsoft.Windows.AppLifecycle.AppActivationArguments e) { if (e.Kind == Microsoft.Windows.AppLifecycle.ExtendedActivationKind.File) { var fileActivated = (FileActivatedEventArgs)e.Data; var file = fileActivated.Files.FirstOrDefault(); Debug.WriteLine(file.Path); } }
2个完全一样的工程,使用一样的FindOrRegisterForKey,最后得出结论互不影响。
同时AppInstance还支持其他方法:
1 2 3 4 我们都验证了。剩下的有需求的小伙伴自己验证把。
这篇到此就结束拉。单例还有很多。但是目前对我来说已经够用了,后续性能优化部分会在分析其他单例模式;
欢迎来群542633085 一起学习WinUI3