最先的時候App Service被定義爲一種後臺服務,相似於極簡版的Windows Service。App Service做爲Background Task在宿主UWP APP中運行,向其餘UWP APP提供服務,可用於UWP APP間通信及交換數據。html
早期的App Service應用場景較爲單一,但隨着Win10 1607版本對In Process AppService的支持,以及從Visual Studio2017開始支持的Desktop Extension和MSIX Package等一系列技術的應用,現在的App Service能夠用於UWP和非UWP程序間的直接通信,達到無限接近傳統桌面程序的能力。咱們今天就先來看一下In Process App Service。
In Process,顧名思義咱們不須要額外建立專門的Project用來寫App Service的代碼。而是直接包含在主UWP工程。首先咱們建立空的UWP工程FrontUWPApp,而後添加一個簡單的幫助類AppServiceHandler:git
class AppServiceHandler { private AppServiceConnection AppServiceConnection { get; set; } private BackgroundTaskDeferral AppServiceDeferral { get; set; } public event EventHandler<string> MessageReceivedEvent; private static AppServiceHandler instance; public static AppServiceHandler Instance { get { if (instance == null) { instance = new AppServiceHandler(); } return instance; } } private AppServiceHandler() { } public void BackgroundActivated(IBackgroundTaskInstance taskInstance) { AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails; AppServiceDeferral = taskInstance.GetDeferral(); AppServiceConnection = appService.AppServiceConnection; AppServiceConnection.RequestReceived += OnAppServiceRequestReceived; AppServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed; } private void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { AppServiceDeferral messageDeferral = args.GetDeferral(); var message = args.Request.Message; string text = message["response"] as string; MessageReceivedEvent?.Invoke(this, text); messageDeferral.Complete(); } private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args) { AppServiceDeferral.Complete(); } public async Task<AppServiceResponse> SendRequestAsync(string message) { var valueSet = new ValueSet(); valueSet.Add("request", message); return await AppServiceConnection.SendMessageAsync(valueSet); } }
這其中最重要的方法是github
public void BackgroundActivated(IBackgroundTaskInstance taskInstance)
該方法將在App.xaml.cs經過windows
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); AppServiceHandler.Instance.BackgroundActivated(args.TaskInstance); }
將BackgroundTask的實例傳遞進來。再保存這個Instance中AppService的AppServiceConnection對象。在取得AppServiceConnection對象後,便可以經過事件後端
public event TypedEventHandler<AppServiceConnection, AppServiceRequestReceivedEventArgs> RequestReceived;
來監聽消息,同時又能夠經過方法app
public IAsyncOperation<AppServiceResponse> SendMessageAsync(ValueSet message);
來發送消息。實現一個雙向的通信過程。
僅經過代碼也許不可思議要作的事情,不妨由界面來推導出邏輯,下圖是UWP工程FrontUWPApp的界面,咱們但願發送文字消息給非UWP工程BackgroundNetProcess。再由BackgroundNetProcess處理消息後,主動經AppService推給FrontUWPApp。async
首先咱們在MainPage的OnNavigatedTo方法中經過desktop extension的方式,來啓動.NET Framework的Console程序BackgroundNetProcess(若是對UWP如何使用desktop extension不夠了解,請參考這篇《遷移桌面程序到MS Store(9)——APPX With Desktop Extension》)。同時給AppServiceHandler訂閱MessageReceivedEvent。ide
protected async override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0)) { await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(); AppServiceHandler.Instance.MessageReceivedEvent += Instance_MessageReceivedEvent; } }
Instance_MesssageReceivedEvent就是簡單的把從BackgroundNetProcess中返回的消息顯示在界面上。this
private async void Instance_MessageReceivedEvent(object sender, string e) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { textBoxResponses.Text += e + "\r\n"; }); }
同時MainPage上的Button按鈕會經過AppServiceHandler實例中保存的AppServiceConnection對象來發送request給BackgroundNetProcess進程。spa
private async void Button_Click(object sender, RoutedEventArgs e) { var response = await AppServiceHandler.Instance.SendRequestAsync(textBoxRequest.Text); }
咱們轉到BackgroundNetProcess工程,在Main方法中僅僅是建立類BackgroundProcess的實例,而且讓Console保持運行。
static void Main(string[] args) { var backgroundProcess = new BackgroundProcess(); Console.ReadKey(); }
而在BackgroundProcess類中,咱們經過InitializeAsync方法來建立AppServiceConnection對象,在成功打開Connection的狀況下,訂閱ReqeustReceived事件。這是爲了能接受到上文提到的,UWP APP發送過來的request。
public class BackgroundProcess { private AppServiceConnection Connection { get; set; } public Task InitializeTask { get; private set; } public BackgroundProcess() { InitializeTask = InitializeAsync(); } public async Task InitializeAsync() { Connection = new AppServiceConnection(); Connection.PackageFamilyName = Package.Current.Id.FamilyName; Connection.AppServiceName = "NotificationAppService"; AppServiceConnectionStatus status = await Connection.OpenAsync(); if (status != AppServiceConnectionStatus.Success) { Console.WriteLine(status); } else { Console.WriteLine(status); Connection.RequestReceived += Connection_RequestReceived; } } private async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var deferral = args.GetDeferral(); var content = args.Request.Message["request"]; var message = new ValueSet(); message.Add("response", $"Received request content: {content}"); await Connection.SendMessageAsync(message); deferral.Complete(); } }
這裏須要注意的是,Connection.AppServiceName須要和最終Package.appmanifest文件中配置的ServiceName一致(appmanifest文件的修改咱們後面一點再介紹)。
在BackgroundProcess類中,一旦咱們收到了UWP APP發來的request,就會觸發Connection_RequestReceived方法。在該方法裏,咱們對收到的字符串作了簡單處理,而後經過SendMessageAsync方法反向給UWP APP發送消息。
固然,並無規定收到request就必定要當即返回消息。咱們能夠在BackgroundProcess這樣的desktop extension進程中,實現一些UWP限制的功能,諸如查詢註冊表,啓動其餘exe程序等等。甚至能夠掛個鍵盤鉤子,在捕捉到熱鍵時,通知UWP APP。
先後端的FrontUWP和BackgroundNetProcess都介紹完了,接着就是經過Packaging工程將它們整合打包成MSIX package。
記得在Package工程的Applications中,添加對FrontUWPApp和BackgroundNetProcess的引用。同時設置FrontUWPApp爲入口點。
最後咱們來編輯Package工程的appxmanifest文件,主要就是添加Extensions節點。
<Extensions> <uap:Extension Category="windows.appService"> <uap:AppService Name="NotificationAppService" /> </uap:Extension> <desktop:Extension Category="windows.fullTrustProcess" Executable="BackgroundNetProcess\BackgroundNetProcess.exe"></desktop:Extension> </Extensions>
在完成以上操做以後,咱們的AppServiceCommunicaton工程就編寫完畢了。在Visual Studio 2019中按F5運行的話,應該能夠實現FrontUWPApp和BackgroundNetProcess之間的消息傳遞了。
本篇的示例代碼依然放在這個Repository中,Clone後經過VS打開,找到InProcessAppService文件夾便可。
https://github.com/manupstairs/UWPSamples