轉: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週期。

https://blog.csdn.net/wwwktxh8com/article/details/81053153

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章