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