三、WinUI3中在OnLauncher处理单例

今天写程序中比较重要的一个知识点单例;他是软件生命周期中的第一步,如何启动;因为在传统开发中涉及到进程间通信;启动时间优化;逻辑拆分解耦,等等。这一步很重要,WinUI3对这一步做了什么一的优化和封装,我们先看单例;

在 Application.OnLaunched 中完成此工作可以简化应用。 不过,这在很大程度上取决于应用所执行的其他操作。 如果打算结束重定向,然后终止当前实例,则需要避免执行任何一次性工作(甚至是需要显式撤消的工作)。 在这类情况下,Application.OnLaunched 可能太晚,你可能更希望在应用的 Main 或 wWinMain 函数中完成此工作。

这一篇只分析OnLaunched单例的使用,在Main和wWinMain做单例涉及的知识更多,对初学者不是很友好,可以先跳过,感兴趣的可以看示例
Main单例连接

看Onlaunched文档示例;

对照文档:
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个验证:

  1. 启动多个AppInstances 实例,添加部分代码观察返回结果;
  2. 我们继续创建一个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所以单实例这里整体流程就通了;

多实例

启动2个进程

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. GetActivatedEventArgs()
  2. GetCurrent()
  3. GetInstances()
  4. RedirectActivationToAsync(AppActivationArguments)
  5. Restart(String)
  6. UnregisterKey()

1 2 3 4 我们都验证了。剩下的有需求的小伙伴自己验证把。
这篇到此就结束拉。单例还有很多。但是目前对我来说已经够用了,后续性能优化部分会在分析其他单例模式;
欢迎来群542633085 一起学习WinUI3

posted @ 2022-08-08 00:27  杜文龙  阅读(500)  评论(0编辑  收藏  举报