Windows 10 2015 年特别版

第 30 卷,第 11 期

此文章由机器翻译。

应用集成 - 在 Windows 10 上链接和集成应用

通过 Arunjeet Singh |Windows 2015 年

大多数应用程序开发人员生成或定期维护多个应用程序。如成熟应用程序,用户经常要求涉及协同工作的多个应用程序的工作流。例如,您可能有的应用程序管理产品库存和未签出的另一个应用程序。理想的做法是为协同工作以完成采购工作流的两个应用程序。

若要解决这一难题的一种方法是只需将所有功能都合并到一个应用程序。实际上,这是一种方法通常在桌面类应用程序中看到。但是,这是充满了危险性的道路。很快,您得到的大多数用户只能使用功能的特定子集的臃肿应用程序。应用程序开发人员现在必须管理 UI 复杂性和整个应用程序的更新。甚至更糟糕,作为 UI 复杂性的增加,用户 — 尤其是在移动 — 开始倾向于多个主要选项。实际上,这种趋势已向以便用户可以安装和使用他们的需要在路上而无需担心他们并不需要的多余的位将应用程序分解到各自经验。

若要解决此问题的第二种方法是通信的利用云作为一种应用程序之间。直到获得超过某个大小或您的数据量非常适用于运行到用户通过有限的连接。这将启动以显示与抱怨如、",在这里我状态之间进行更新,但它不会显示在此其他应用程序那边!" 此外,我总是发现它有点奇怪该应用程序开发人员必须求助于云坐在同一个设备上的两个应用程序之间进行通信。必须有一种更好的方法。

在本文中,我将介绍一些 Windows 10 为了使应用程序更容易之间的通信而提供的工具。应用程序之间的通信可以采用以下形式的应用程序启动另一个应用程序与一些数据,也可能表示应用程序只需交换与每个其他而无需任何操作启动的数据。Windows 10 提供了可用于这两种情况的工具。

准备应用程序的深层链接

让我们来举的产品库存应用程序可以显示有关产品的详细信息的示例。现在,我们还将向可以显示各种趋势有关什么在哪个位置销售和销售需要去哪里何种混合的销售应用程序。销售应用程序具有较允许用户查看有关各个产品的详细信息的向下钻取的用户体验。当然,产品的最详细的视图是在库存应用程序中。图 1 显示举例说明了有关哪些我所讨论的方案。

销售应用程序清单应用程序的深层链接
图 1 销售应用程序清单应用程序的深层链接

在此方案中,您需要执行的第一件事就是使清单应用程序可用于启动。若要执行此操作将添加到清单应用程序的包清单 (package.appxmanifest) 的协议声明。协议声明是告诉它是可由其他应用程序启动世界库存应用程序的方法。图 2 显示此声明如下所示。请注意我使用的协议名称 com.contoso.showproduct。这是有效的命名约定为自定义协议,因为 Contoso 拥有域 contoso.com。其他应用程序开发人员错误地使用相同的自定义方案所幸的是远程。

协议声明
图 2 协议声明

下面是 XML 生成的协议声明:

<uap:Extension Category="windows.protocol">
  <uap:Protocol Name="com.contoso.showproduct" />
</uap:Extension>

接下来,您需要添加一些激活代码以便启动使用新的协议时,库存应用程序能够做出适当响应。因为这就是其中路由所有激活,代码应归属到库存应用程序的应用程序类 (App.xaml.cs) 中。您重写要响应协议激活的应用程序类的 OnActivated 方法。图 3 显示该代码如下所示。

图 3 处理深层链接

protected override void OnActivated(IActivatedEventArgs args)
{
  Frame rootFrame = CreateRootFrame();
  if (args.Kind == ActivationKind.Protocol)
  {
    var protocolArgs = args as ProtocolActivatedEventArgs;
    rootFrame.Navigate(typeof(ProtocolActivationPage), protocolArgs.Uri);
  }
  else
  {
    rootFrame.Navigate(typeof(MainPage));
  }
  // Ensure the current window is active
  Window.Current.Activate();
}

检查传入 IActivatedEventArgs 以查看是否协议激活的类型。如果是,您将类型转换为 ProtocolActivatedEventArgs 传入的参数和发送拖到 ProductDetails 页面上传入的 URI。ProductDetails 页设置来分析 URI (例如,com.contoso.showproduct:Details 吗?ProductId = 3748937 并显示相应的产品详细信息。此时,库存应用程序已准备好处理传入的深层链接。

完成此方案中的最后一步是启用到库存应用程序的深层链接到销售的应用程序。这是过程的最简单一部分。销售应用程序只是到库存应用程序使用 Launcher.LaunchUriAsync API 对深层链接。下面是该代码可能如下所示:

Uri uri = new Uri("com.contoso.showproduct:?ProductId=3748937");
await Launcher.LaunchUriAsync(uri);

应用程序之间共享数据

有应用程序需要共享数据但不一定的方案涉及向用户发送到另一个应用程序。例如,我的示例销售应用程序可以显示按区域和甚至深入了解一些特定的商店的销售。显示此数据按产品分类时它会很有用的存储区或区域中可用的该产品的单位数。获取此数据的最佳来源是库存应用程序中,但在这种情况下启动库存应用程序会中断 ux。这是完全方案 AppService 扩展的排序 (bit.ly/1JfcVkx) 用于处理。

其原理很简单: 库存应用程序提供了销售应用程序可以调用"服务"。销售应用程序使用此服务来查询它具有的数据清单应用程序。销售应用程序一次建立,与清单应用程序之间的连接可以保持打开状态,只要销售应用程序尚未被挂起。

创建清单应用程序服务

让我们看一下如何清点应用程序创建并发布它要提供的应用程序服务。应用程序服务基本上是专用的后台任务。因此为了添加应用程序服务将 Windows 运行时组件 (通用 Windows) 项目添加到包含库存应用程序的 Visual Studio 解决方案。您可以在 Visual Studio 在 Visual C# 的添加新项目窗口中找到 Windows 运行时组件项目 |Windows |通用。项目模板是在其他语言的类似的位置。

在新的 Windows 运行时组件项目,您将添加一个名为 InventoryServiceTask 的新类。因为,如前面所示,您希望此代码在后台运行而不显示 UI,应用程序服务是专用的后台任务。若要告诉操作系统 InventoryServiceTask 是后台任务,只需实现 IBackgroundTask 接口。IBackgroundTask 接口的 Run 方法将库存应用程序服务的入口点。在这里,您将延迟以便知道该任务应保留周围的只要客户端 (销售的应用程序) 需要它的操作系统。您还附加到应用程序特定于服务 RequestReceived 事件的事件处理程序。此事件处理程序将调用任何客户端发送来处理此服务的请求的时间。图 4 显示初始化的库存应用程序服务的代码如下所示。

图 4 初始化 Run 方法中的库存应用程序服务

namespace Contoso.Inventory.Service
{
  public sealed class InventoryServiceTask : IBackgroundTask
  {
    BackgroundTaskDeferral serviceDeferral;
    AppServiceConnection connection;
    public void Run(IBackgroundTaskInstance taskInstance)
    {
      // Take a service deferral so the service isn't terminated
      serviceDeferral = taskInstance.GetDeferral();
      taskInstance.Canceled += OnTaskCanceled;
      var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
      connection = details.AppServiceConnection;
      // Listen for incoming app service requests
      connection.RequestReceived += OnRequestReceived;
    }
  }
}

现在让我们看看 RequestReceived 处理程序的实现。再次重申,只要请求派上用场需要延迟。一旦完成后将释放此延迟处理传入的请求。货币符号的应用程序服务客户端和应用程序服务之间是通信的一个称为 ValueSet 的数据结构。ValueSets 是可以执行如整数、 浮点数、 字符串和字节数组的简单类型的键/值字典。

图 5 显示清单应用程序服务如何处理传入的请求。它将检查命令的传入消息,然后将使用正确的结果进行响应。在这种情况下,您显示该服务响应的产品和最后一个时间单位数与它所具有的数据的 GetProductUnitCountForRegion 命令已更新。该服务可能也是从 Web 服务中获取此数据或者只从脱机缓存中检索它。比较有利的一点是客户端 (销售的应用程序) 不需要知道或在意从数据的用武之地。

图 5 库存应用程序接收请求

async void OnRequestReceived(AppServiceConnection sender,
  AppServiceRequestReceivedEventArgs args)
{
  // Get a deferral so we can use an awaitable API to respond to the message
  var messageDeferral = args.GetDeferral();
  try
  {
    var input = args.Request.Message;
    string command = input["Command"] as string;
    switch(command)
    {
      case "GetProductUnitCountForRegion":
        {
          var productId = (int)input["ProductId"];
          var regionId = (int)input["RegionId"];
          var inventoryData = GetInventoryData(productId, regionId);
          var result = new ValueSet();
          result.Add("UnitCount", inventoryData.UnitCount);
          result.Add("LastUpdated", inventoryData.LastUpdated.ToString());
          await args.Request.SendResponseAsync(result);
        }
        break;
      // Other commands
      default:
        return;
    }
  }
  finally
  {
    // Complete the message deferral so the platform knows we're done responding
    messageDeferral.Complete();
  }
}
// Handle cancellation of this app service background task gracefullyprivate void OnTaskCanceled(IBackgroundTaskInstance sender,
  BackgroundTaskCancellationReason reason)
{
  if (serviceDeferral != null)
  {
    // Complete the service deferral
    serviceDeferral.Complete();
    serviceDeferral = null;
  }
}

也显示在 图 5 是取消处理程序的实现。很重要的应用程序服务放弃受理正常取消请求时的延迟。取消应用程序服务后台任务的可能情况可能是因为客户端关闭应用程序服务连接或系统资源不足。无论哪种方式,可确保正常取消,取消并不被视为崩溃由平台。

任何人都可以调用的库存应用程序服务之前,必须将其发布并为其提供一个终结点。首先,您在库存应用程序项目添加到新的 Windows 运行时组件的引用。接下来,您的应用程序服务将声明添加到清单应用程序项目,如中所示 图 6。入口点设置为 InventoryServiceTask 类的完全限定名和名称是将用于标识此应用程序服务终结点的名称。这是客户端将用于到达它的相同名称的应用程序服务。

在应用程序服务声明
图 6 应用程序服务声明

下面是生成的应用程序服务声明的 XML:

<uap:Extension Category="windows.appService"
  EntryPoint="Contoso.Inventory.Service.InventoryServiceTask">
  <uap:AppService Name="com.contoso.inventoryservice"/>
</uap:Extension>

另一条客户端将需要与库存应用程序服务进行通信是信息的库存应用程序的包系列名称。获取此值的最简单方法是使用 Windows.ApplicationModel.Package.Current.Id.FamilyName API 在库存应用程序。我通常只是输出该值设置为调试窗口并从该处选取它。

调用应用程序服务

现在,库存应用程序服务已到位,您可以从销售应用程序调用它。若要调用客户端的应用程序服务可以使用 AppServiceConnection API。AppServiceConnection 类的实例需要的应用程序服务终结点的名称和包系列名称在服务所在的包。将这两个值视为应用程序服务的地址。

图 7 显示销售应用程序的代码用于连接到的应用程序服务。请注意 AppServiceConnection.AppServiceName 属性设置为终结点名称在清单应用程序的包清单中声明。此外,清单应用程序的包系列名称均被插入到 AppServiceConnection.PackageFamilyName 属性。一旦准备就绪后,调用 AppServiceConnection.OpenAsync API 以打开的连接。OpenAsync API 返回完成后的状态,并使用此状态来确定是否已成功建立连接。

图 7 调用库存应用程序服务

using (var connection = new AppServiceConnection())
{
  // Set up a new app service connection
  connection.AppServiceName = "com.contoso.inventoryservice";
  connection.PackageFamilyName = "Contoso.Inventory_876gvmnfevegr";
  AppServiceConnectionStatus status = await connection.OpenAsync();
  // The new connection opened successfully
  if (status != AppServiceConnectionStatus.Success)
  {
    return;
  }
  // Set up the inputs and send a message to the service
  var inputs = new ValueSet();
  inputs.Add("Command", "GetProductUnitCountForRegion");
  inputs.Add("ProductId",productId);
  inputs.Add("RegionId", regionId);
  AppServiceResponse response = await connection.SendMessageAsync(inputs);
  // If the service responded with success display the result and walk away
  if (response.Status == AppServiceResponseStatus.Success)
  {
    var unitCount = response.Message["UnitCount"] as string;
    var lastUpdated = response.Message["LastUpdated"] as string;
    // Display values from service
  }
}

连接后,客户端发送的应用程序服务的一组值中 ValueSet 使用 AppServiceConnection.SendMessageAsync API。请注意,在设置的值中的命令属性设置为 GetProductUnitCountForRegion。这是应用程序服务可以理解的命令。SendMessageAsync 返回包含设置的值发回的应用程序服务的响应。解析出 UnitCount 和上次更新值并将其显示。就是这样。这是与应用程序服务进行通信所需的全部。将 AppServiceConnection 放在 using 块。这将调用 Dispose 方法在 AppServiceConnection 上尽快 using 块结束。调用 Dispose 是说它已完成的应用程序服务的通话和它现在可以终止该客户端的方法。

等一等,Microsoft 使用这些 Api 吗?

当然,Microsoft 使用这些 Api。作为 Windows 10 的一部分提供的大多数 Microsoft 应用程序实际上是通用的 Windows 平台应用程序。这包括如照片、 相机、 邮件、 日历、 Groove 音乐和应用商店的应用程序。开发人员编写这些应用程序使用许多此处所述实现集成方案的 Api。例如,是否注意到在 Groove 音乐应用程序的"在存储区中的 Get 音乐"链接吗? 如果点击或单击该链接 Groove 音乐应用程序使用 Launcher.LaunchUriAsync API 访问应用商店应用程序。

另一个典型示例是设置应用程序。当您进入帐户 |您的帐户和,请尝试使用相机拍照新配置文件,它使用称为 Launcher.LaunchUriForResultsAsync API 来启动相机应用程序要执行该图片。LaunchUriForResultsAsync 是一种专用的形式的 LaunchUriAsync aka.ms/launchforresults 更多详细信息中所述。

大量的应用程序还使用应用程序服务将信息传递给 Cortana 实时。例如,当应用程序尝试安装 Cortana 应响应的语音命令,它实际调用应用程序由 Cortana 提供服务。

总结

Windows 10 附带了功能强大的工具以帮助在同一个设备上运行的应用程序之间的通信。这些工具将不受限制或限制对哪些应用程序可以彼此通信,或者它们可以交换的数据类型。这是非常相像设计使然。目的是让应用程序定义他们自己的约定与每个其他和扩展对方的功能。这还允许应用程序开发人员将其应用程序分解为较小的、 分解易于维护、 更新和使用的体验。这是非常重要,因为用户越来越多地跨多个设备 live 他们生活和使用他们认为是一项任务到最适合的设备。所有这些 Api 是还通用的这意味着它们的工作在台式计算机、 便携式计算机、 平板电脑、 手机和很快 Xbox、 面中心和 HoloLens 上。


Arun Singh是通用的 Windows 平台构建团队的高级项目经理。关注他的 Twitter: @aruntalkstech 或阅读他的博客 aruntalkstech.com

衷心感谢以下技术专家参与本文的审阅: Hector Barbera、 Jill Bender、 Howard Kapustein、 Abdul Hadi Sheikh Stefan Wick 和 Jon Wiswall
Stefan Wick 是一名项目经理,从事应用程序执行和在 Microsoft 部署
Hector Barbera 是一名项目经理他致力于备份和在 Microsoft 漫游的应用程序
Howard Kapustein 是工程师和从事 microsoft 的应用程序状态管理
Abdul Hadi Sheikh 是工程师和从事 microsoft 的应用程序执行
Jill Bender 是一名项目经理,从事作为 Microsoft 的应用程序集成方案
Jon Wiswall 是工程师和从事应用程序模型在 Microsoft 的 Api