在新的Win10 App开发中,微软提供给我们很多Services,UWP中有很多大量的API来帮助我们做些事情,比如发邮件服务,Uri关联启动服务,启动设置服务,启动文件管理器服务等……
然而,微软又提出了一种新的Services叫做 App Services,如下图
App Servcies允许App不在前台运行的情况下提供出一个或多个对外服务供其他App使用,这看起来就好像Web开发中的Web Api。
通过对外提供服务的形式,可以使App更好的完成一些其他App所拥有的专业性操作,而不必自己再去实现服务所做的操作。一些企业用户可以提供复杂的服务,比如云识别和云存储来供开发者使用。这样使开发成本大大降低,也可以为服务提供商带来更多的用户。比如我们可以调用二维码识别服务(如下图,假设其他App提供二维码服务,虽然自己实现也很简单,这里仅仅举个例子),将图片转送到二维码识别服务中,然后服务返回我们需要的数据展示给用户,然后用户的一些云数据也可以使用云数据服务方便的进行云数据操作等……
下面我们将要创建一个具备App Service的App,简单的实现来帮助我们计算两数之和的服务( 如果想要深入了解App Services 请参阅:https://msdn.microsoft.com/library/windows/apps/xaml/windows.applicationmodel.appservice.aspx ),了解下App Service是怎么运行的。
首先我们先创建一个App Service,App Service是运行在后台任务中的,所以我们需要在后台任务中创建。新建一个类集成后台任务:
public sealed class AppServiceTask : IBackgroundTask { private static BackgroundTaskDeferral _serviceDeferral; public void Run(IBackgroundTaskInstance taskInstance) { //订阅关闭事件 taskInstance.Canceled += TaskInstance_Canceled; _serviceDeferral = taskInstance.GetDeferral(); var appService = taskInstance.TriggerDetails as AppServiceTriggerDetails; //验证调用者 if (appService.Name == "appServiceUWP-calculate" && appService.Name != null) { //订阅调用者请求 appService.AppServiceConnection.RequestReceived += AppServiceConnection_RequestReceived; } } }
通过获取后台任务参数的 TriggerDetails 属性来转换为AppServiceTriggerDetails对象,然后验证下调用者的身份,可以使用加密验证也可以使用秘钥验证,这里使用字符串简单验证下,如果通过验证则订阅调用者对服务的请求事件,事件中处理调用者的请求逻辑如下:
private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var message = args.Request.Message; string command = message["Command"] as string; switch (command) { case "CalcSum": var messageDeferral = args.GetDeferral(); int value1 = (int)message["Value1"]; int value2 = (int)message["Value2"]; var returnMessage = new ValueSet(); returnMessage.Add("Rusult", value1 + value2); //回应调用者 var responseStatus = await args.Request.SendResponseAsync(returnMessage); messageDeferral.Complete(); break; case "Quit": _serviceDeferral.Complete(); break; } }
和App To App 服务一样,App Service中传递的数据也是ValueSet类型,我们获取到操作指令Command的值,然后选择怎么处理该任务,处理结果通过SendResponseAsync()方法将包含数据的ValueSet发送到调用方。
注:更多App To App 服务请参阅:http://www.wangchenran.com/?p=237
至此,逻辑代码已完成,然后我们需要在App Services的提供方App中声明该App Service,打开Package.appxmanifest文件,声明App Service如下:
在这里我们可以声明很多个AppService,格式类似上面。
Extension.Category设置为appService类型,EntryPoint设置为App Service类的NameSpace.ClassName,AppService.Name设置你的服务的名字,要保证这个名字是唯一的,调用者需要知道这个名字,正式环境下,可以把这个名字存储到UWP的公共存储空间中,使所有App都能访问到。
至此,服务端code已经完成,下面我们创建一个新项目作为调用方Client。首先我们在界面上放置两个文本框和一个按钮如下图:
后台代码如下:
public sealed partial class MainPage : Page { //声明一个AppService连接对象 private readonly AppServiceConnection connection = new AppServiceConnection { AppServiceName = "appServiceUWP-calculate", PackageFamilyName = "b8df40a7-e5b3-46a2-a65a-28f502255fc5_md3s7cn435nw2" }; public MainPage() { this.InitializeComponent(); this.Loaded += MainPage_Loaded; } private async void MainPage_Loaded(object sender, RoutedEventArgs e) { var connectionStatus = await connection.OpenAsync(); if (connectionStatus == AppServiceConnectionStatus.Success) { //订阅双向通信 connection.RequestReceived += Connection_RequestReceived; } else { //可以提示并导航到商店 让用户下载该服务 await new MessageDialog("服务连接不成功").ShowAsync(); } } private void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { // To do something } private async void Button_Click(object sender, RoutedEventArgs e) { var message = new ValueSet(); message.Add("Command", "CalcSum"); message.Add("Value1", int.Parse(tbNum1.Text)); message.Add("Value2", int.Parse(tbNum2.Text)); AppServiceResponse response = await connection.SendMessageAsync(message); if (response.Status == AppServiceResponseStatus.Success) { string sum = response.Message["Rusult"].ToString(); await new MessageDialog(sum).ShowAsync(); } else { } } }
首先我们声明一个App Service连接对象,为App Service的Name赋值为我们设置的AppService.Name,PackageFamilyName我们可以在提供App Service 的App中使用如下code获取。
Debug.WriteLine(Package.Current.Id.FamilyName);
这个FamilyName我们同样可以存放到UWP的公共存储空间中,以方便其他App能够直接获取到。
Ok,我们连接指定Name和FamilyName的App Service,然后开启连接管道,如果连接不成功,可以根据连接状态返回值来提示用户相关信息,如果App Service 未找到,也可以引导用户下载该服务App。
然后我们在点击事件中创建一个ValueSet对象,添加字典数据进去后发送到服务端,然后等待服务端响应。
让我们看下效果: