New Windows 10 SDK - Multi-instance UWP apps

概述

前面一篇 About Windows 10 SDK Preview Build 17110 中,我们简单介绍了 Multi-instance UWP Apps,今天结合开发过程详细讲解一下。

在 Windows 10 Version 1803 以前,UWP App 同一时间只能启动一个实例,而在 1803 开始,UWP App 可以通过开发者的配置选择来支持多实例。如果一个多实例 UWP App 正在运行,这时一个激活请求发送过来,平台不会直接激活当前的实例,而是会创建一个新的实例,运行在单独的进程中。

开发过程 

配置多实例支持

多实例特性需要在 Visual Studio 中安装新的项目模板:Multi-Instance App Project Templates.VSIX, 安装后,使用 C# 和 C++ 都可以创建项目。

两个模板会被安装:

  • Multi-Instance UWP app -- 创建一个多实例的 App
  • Multi-Instance Redirection UWP app -- 提供一个附加的逻辑,让用户可以选择启动新实例,或者选择目前激活的实例。可以想象一下 Office 打开或编辑文件时的场景。

      

这两个模板都会在 manifest 文件中添加 SupportsMultipleInstances,其中 desktop4 和 iot2 前缀标志了项目只支持传统桌面 Windows 和 IoT 系统。manifest 配置如下,我们只保留了新增的部分:

<Package
  ...
  xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
  xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"  
  IgnorableNamespaces="uap mp desktop4 iot2">
  ...
  <Applications>
    <Application Id="App"
      ...
      desktop4:SupportsMultipleInstances="true"
      iot2:SupportsMultipleInstances="true">
      ...
    </Application>
  </Applications>
   ...
</Package>

实际运行时,每次点击 App 的磁贴,都会启动一个新的实例。如下图中,App 显示了启动的时间,在任务栏和运行窗口可以看到,两个实例同一时间在运行状态。 

 

多实例激活重定向

UWP App 对多实例的支持,可以让同一 App 的多个实例可以同时在运行。它运行开发者自己定义,是每次开启一个新的实例,还是重定向某个目前激活的应用。举例来说,让你想使用 App 编辑一个文件,而这个文件正在 App 中被编辑,这时就不应该再开启一个新的实例,而是应该重定向当前正在编辑文件的实例。这就会用到 Multi-Instance Redirection UWP app 模板。

Multi-Instance Redirection UWP app 模板和我们上面看到的一样,对 manifest 文件会做同样的调整。同时该模板会增加一个 Program.cs 文件,在文件中包含一个 Main() 方法,靠这个方法来实现多实例激活的重定向操作。

我们来重点看看 Program.cs 文件中的 Main() 方法

  • activatedArgs 中包含了应用启动时我们定义的参数,我们根据这些参数,比如 key 来决定多实例的重定向方式;
  • AppInstance.RecommendedInstance 系统推荐的实例,如果有,我们可以重定向到这个实例;
  • 多实例间唯一性的标识 key 的生成方式,我们可以根据 activatedArgs 来自定义,在默认的示例代码中,采用了随机数判断单双数的方式;
  • FindOrRegisterInstanceForKey(key) 会查询当前对应 key 的实例,如果没有则新注册一个实例;
  • 判断实例是不是新注册的,如果是则启动,如果是查询到的原有实例,则重定向到那个实例;
static void Main(string[] args)
{
    // First, we'll get our activation event args, which are typically richer
    // than the incoming command-line args. We can use these in our app-defined
    // logic for generating the key for this instance.
    IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();

    // In some scenarios, the platform might indicate a recommended instance.
    // If so, we can redirect this activation to that instance instead, if we wish.
    if (AppInstance.RecommendedInstance != null)
    {
        AppInstance.RecommendedInstance.RedirectActivationTo();
    }
    else
    {
        // Define a key for this instance, based on some app-specific logic.
        // If the key is always unique, then the app will never redirect.
        // If the key is always non-unique, then the app will always redirect
        // to the first instance. In practice, the app should produce a key
        // that is sometimes unique and sometimes not, depending on its own needs.
        uint number = CryptographicBuffer.GenerateRandomNumber();
        string key = (number % 2 == 0) ? "even" : "odd";
        var instance = AppInstance.FindOrRegisterInstanceForKey(key);
        if (instance.IsCurrentInstance)
        {
            // If we successfully registered this instance, we can now just
            // go ahead and do normal XAML initialization.
            global::Windows.UI.Xaml.Application.Start((p) => new App());
        }
        else
        {
            // Some other instance has registered for this key, so we'll 
            // redirect this activation to that instance instead.
            instance.RedirectActivationTo();
        }
    }
}

对于 key 的构造和判断,以及判断后的处理,是多实例重定向的关键,我们先看看 FindOrRegisterInstanceForKey(key) 和 IsCurrentInstance 的注释:

//
// 摘要:
//     如果另一个实例已注册该密钥,使用平台注册一个应用实例,或查找现有实例。
//
// 参数:
//   key:
//     作为实例密钥的非空字符串。
//
// 返回结果:
//     表示已注册密钥的第一个应用的应用实例。
public static AppInstance FindOrRegisterInstanceForKey(string key);

//
// 摘要:
//     应用的当前实例是否是该实例定义的特定密钥的已注册实例。
//
// 返回结果:
//     指示当前应用是否为该应用的已注册实例的布尔值。
public bool IsCurrentInstance { get; }

 

后台任务和多实例

关于后台任务的多实例,官方有以下说明:

  • 进程外的后台任务支持多实例,通常,每个新触发的结果会独立在一个后台任务的实例中;
  • 进程内的后台任务不支持多实例;
  • 后台音乐任务不支持多实例;
  • 当应用注册一个后台任务时,它通常会首先检查这个任务是否已经注册了,如果已注册,或删除重新创建它,或维持当前的注册。这也是多实例应用的典型特点。然而,多实例应用可能会选择在每个实例的基础上注册一个不同的后台任务名。这对导致多次注册相同的触发器,并且触发器触发时将会激活多个任务实例;
  • 应用服务会为每一个应用服务后台任务的连接启动一个单独的实例,这对多实例应用保持不变,即多实例应用的每个实例都会获得自己的应用服务后台任务实例;

 

其他注意事项

关于多实例应用,官方文档还提示了一些额外的注意事项:

  • 支持多实例应用的 UWP 应用,只能面向传统桌面系统和 IoT;
  • 为避免竞争条件和资源争夺的问题,多实例应用需要采取措施,分区和同步权限到对访问进行设置,应用本地存储和任何其他资源(如用户文件,数据存储等),以在多个实例间完成共享。标准的同步机制包括 mutexes,semaphores,events 等都是可用的;
  • 如果应用的 Package.appxmanifest 文件中存在 SupportsMultipleInstances 字段,那么他的扩展中不需要再声明 SupportsMultipleInstances;
  • 如果你把 SupportsMultipleInstances 添加到除后台任务,应用服务之外的的任何其他扩展中,并且托管该扩展的应用没有在 Package.appxmanifest 中声明 SupportsMultipleInstances,则会发生模式错误;
  • 应用可以在 manifest 中使用 ResourceGroup 来把多个后台任务分组到同一个宿主中, 这和多实例是冲突的,每个活动都会出现在单独的宿主中。因为一个应用不能同时声明 SupportsMultipleInstances 和 ResourceGroup;

 

多实例应用的介绍就到这里,大家可以结合自己应用的实际场景,更加合理的设置 key 和判断条件来使用多实例,谢谢!

posted @ 2018-03-22 14:29  shaomeng  阅读(2308)  评论(16编辑  收藏  举报