UWP - 介绍App Service 与新功能

App Service 是一种背景工作运行的服务,提供给其他Apps 使用就像Web Service。它本身无使用介面(UI-less),允许Apps 在同一个设备被引用,甚至Windows 10 1607 开始允许remote devices 使用它。

[ 重点观念 ]

Windows 10, version 1607 开始, App Service 支持新模式:
可以与host App 运行在相同的process;(一般属于Background Task 执行在不同的process)
支援从App呼叫Remote Devices中的App Service;
想要App Service每次被启动都是新的instance,在Package.appmanifest加入宣告;uap4:SupportsMultipleInstances="true";但需要Windows 10, version 15063以上才支援
App Service 的生命周期,因为Process 有所不同:
后台任务(进程外):
当它被建立时会进入Run(),随着Run()执行完毕就会被结束
它被启动后,基本会维持活着约有30秒,可搭配呼叫GetDeferral()多加5秒来完成任务
In-app process model:生命周期则跟着呼叫者一起共存,让两个Apps 之间更容易沟通,不用再分成两份code 来串联与维护
App Service 的OnTaskCancel() 被触发有几个原因:
Client app释放AppServiceConnection
Client app 被 suspended
系统关闭或睡眠
系统执行该Task 用过高的资源
大略有概念之后,接着介绍怎么做基本的App Service (two process),再介绍怎么整合到App 的process 里面;

  • 如何建立App service 并使用它:

分成两个App 做说明:一个是拥有App Service 的Host App;一个是使用App Service 的Client App;

1建立一个Windows Runtime Component,并且加入AppServiceConnection 的处理逻辑:

public sealed class ServiceTask : IBackgroundTask
{
    private BackgroundTaskDeferral backgroundTaskDeferral;
    private AppServiceConnection appServiceconnection;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        // Background Task 被建立時,取得 deferral 拉長生命周期,避免被結束
        this.backgroundTaskDeferral = taskInstance.GetDeferral();

        // 一定要註冊處理 Canceled 事件來正確釋放用到的資源
        taskInstance.Canceled += OnTaskCanceled;

        // 根據被啓動的 Instance 類型,建立 App Service Connection,並註冊 Request 事件.
        var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
        appServiceconnection = details.AppServiceConnection;
        appServiceconnection.RequestReceived += OnRequestReceived;
    }

    private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
        if (this.backgroundTaskDeferral != null)
        {
            // Complete the service deferral.
            this.backgroundTaskDeferral.Complete();
        }
    }

    private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
    {
        // 當 App Service 收到請求時,該 method 就會被觸發
        // 先要求取得 取得 deferral 拉長生命周期
        var requestDeferral = args.GetDeferral();

        ValueSet message = args.Request.Message;
        string cmd = message["cmd"] as string;
        string id = message["id"] as string;

        ValueSet responseMsg = new ValueSet();

        switch (cmd)
        {
            case "Query":
                responseMsg.Add("id", "123456");
                responseMsg.Add("name", "pou");
                responseMsg.Add("status", "OK");
                var result = await args.Request.SendResponseAsync(responseMsg);
                break;
        }

        requestDeferral.Complete();
    }
}

2在Host App 的Package.manifest 宣告App Service 并设定Entry Point,记得把App Service 的专案加入到Host App 的专案参考:

<Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="ServiceHost.App">
        <uap:VisualElements  />
        <Extensions>
            <uap:Extension Category="windows.appService" EntryPoint="MyAppService.ServiceTask">
                <uap:AppService Name="com.pou.MyApService" />
            </uap:Extension>
        </Extensions>
    </Application>
</Applications>

 

加入专案参考这样在Host App被安装的时候才会一并加入App Service。利用Windows.ApplicationModel.Package.Current.Id.FamilyName在Host App拿到package family name,准备交给Client App。
3在 Client App 利用 AppServiceConnection 呼叫 App Service:

private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    AppServiceConnection connection = new AppServiceConnection();
    connection.AppServiceName = "com.pou.MyApService";
    connection.PackageFamilyName = "f9842749-e4c8-4c15-bac8-bc018db1b2ea_s1mb6h805jdtj";

    var status = await connection.OpenAsync();

    if (status != AppServiceConnectionStatus.Success)
    {
        Debug.WriteLine("Failed to connect");
        return;
    }

    var message = new ValueSet();
    message.Add("cmd", "Query");
    message.Add("id", "1234");

    AppServiceResponse response = await connection.SendMessageAsync(message);
    string result = "";

    if (response.Status == AppServiceResponseStatus.Success)
    {
        if (response.Message["status"] as string == "OK")
        {
            result = response.Message["name"] as string;
        }
    }
}

 

上面介绍的App Service 是比较一般的用法, 把App Service 放到Background Task 的架构。

  • 把App Service 合并到App.xaml.cs 里面,作为Same Process:

AppServiceConnection允许其他App叫醒在背景中自己的App并传入指令。它与上方的out-of-process最大不同有两个:

1Package.manifest 宣告 <uap:Extension Category="windows.appService"> 不用 Entry Point,改用 OnBackgroundActivated()。

<Package>
  <Applications>
      <Application>
          <Extensions>
              <uap:Extension Category="windows.appService">
                  <uap:AppService Name="com.pou.MyApService" />
              </uap:Extension>
          </Extensions>
      </Application>
  </Applications>

 

2在App.xaml.cs加入OnBackgroundActivated()的处理逻辑。

sealed partial class App : Application
{
    private AppServiceConnection appServiceConnection;
    private BackgroundTaskDeferral appServiceDeferral;

    protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
    {
        base.OnBackgroundActivated(args);

        AppServiceTriggerDetails appService = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;

        if (appService ==null)
        {
            return;
        }

        args.TaskInstance.Canceled += OnAppServicesCanceled;

        // appServiceDeferral 與 appServiceConnection 需要變成公用變數
        // 因爲其他時間需要用到,已維持連線的一致性
        appServiceDeferral = args.TaskInstance.GetDeferral();
        appServiceConnection = appService.AppServiceConnection;

        appServiceConnection.RequestReceived += AppServiceConnection_RequestReceived;
        appServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed;
    }

    private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
    {
        // 當 App Service 收到請求時,該 method 就會被觸發

        // 先要求取得 取得 deferral 拉長生命周期
        var requestDeferral = args.GetDeferral();

        ValueSet message = args.Request.Message;

        string cmd = message["cmd"] as string;
        string id = message["id"] as string;

        ValueSet responseMsg = new ValueSet();

        switch (cmd)
        {
            case "Query":
                responseMsg.Add("id", "123456");
                responseMsg.Add("name", "pou");
                responseMsg.Add("status", "OK");
                var result = await args.Request.SendResponseAsync(responseMsg);
                break;
        }

        requestDeferral.Complete();
    }

    private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
    {
        appServiceDeferral?.Complete();
        appServiceConnection?.Dispose();
    }

    private void OnAppServicesCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
        appServiceDeferral?.Complete();
        appServiceConnection?.Dispose();
    }
}

要支援in-process model就是这样简单,而且让原本的App Service逻辑回到App本身,让逻辑更干净。OnBackgroundActivated()负责处理App Service的启用,并储存Deferral保持服务的生命周期,详细可以参考Windows 10通用Windows平台(UWP) app周期。

UWP - 介绍App Service 与新功能

上一篇:关于@Mapper和@Repository的理解


下一篇:#浅析 JavaScript ES6 call(). apply(), bind()