三、WinUI3中在OnLauncher处理单例
今天写程序中比较重要的一个知识点单例;他是软件生命周期中的第一步,如何启动;因为在传统开发中涉及到进程间通信;启动时间优化;逻辑拆分解耦,等等。这一步很重要,WinUI3对这一步做了什么一的优化和封装,我们先看单例;
在 Application.OnLaunched 中完成此工作可以简化应用。 不过,这在很大程度上取决于应用所执行的其他操作。 如果打算结束重定向,然后终止当前实例,则需要避免执行任何一次性工作(甚至是需要显式撤消的工作)。 在这类情况下,Application.OnLaunched 可能太晚,你可能更希望在应用的 Main 或 wWinMain 函数中完成此工作。这一篇只分析OnLaunched单例的使用,在Main和wWinMain做单例涉及的知识更多,对初学者不是很友好,可以先跳过,感兴趣的可以看示例
Main单例连接
看Onlaunched文档示例;
单例的位置主要是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个验证:
- 启动多个AppInstances 实例,添加部分代码观察返回结果;
- 我们继续创建一个AppInstances2实例,同时复制一样的OnLauncher代码,我们验证一下FindorRegisterForKey能否跨应用;观察返回结果;
为了更好的观察,我们添加文件类型关联实例,结合AppInstances,看返回结果;
步骤:
打开 Package.appxmanifest。 在“声明”中,选择“文件类型关联”,然后单击“添加”。 设置以下属性:
显示名称:MyFile 名称:myfile 文件类型:.myf
我多添加了一个文件类型: .pdf
若要注册文件类型关联,请生成应用,进行启动,然后关闭。
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还支持其他方法:
- GetActivatedEventArgs()
- GetCurrent()
- GetInstances()
- RedirectActivationToAsync(AppActivationArguments)
- Restart(String)
- UnregisterKey()
1 2 3 4 我们都验证了。剩下的有需求的小伙伴自己验证把。
这篇到此就结束拉。单例还有很多。但是目前对我来说已经够用了,后续性能优化部分会在分析其他单例模式;
欢迎来群542633085 一起学习WinUI3