自Windows 10正式推出後,不僅帶給使用著一個全新的體驗,對我們來開發人員來說,更關注的則是Windows APP的進化。整體而言,Windows 10 APP(以下簡稱為WIN10 APP) 還是延續著Windows 8.1 APP(以下簡稱為WIN8.1 APP)的開發架構,更將整合手持裝置與PC端APP統整為通用應用程式,並增加且擴充更多的函式庫,因此原本有WIN8.1 APP開發經驗的人員不會覺得太陌生。本篇即針對當中一項新的資料傳遞模型-App Service做初步介紹及示範,而在提到App Service前可以追溯到App與App之間的溝通,看看這個概念如何從WIN8.1APP演化而來。
WIN8.1 APP TO APP
在WIN 8.1 APP的開發中,有時會需要啟動其他APP或是執行某個檔案,通常會使用LaunchUriAsync搭配URI/Protocol的方式來達到呼叫其他APP,或是以LaunchFileAsync來開啟檔案(可搭配FileExtension)。
Launcher.LaunchUriAsync(new Uri(“sampleapp:?ID=aea6“));
Launcher.LaunchFileAsync(file);
另外一種情境是, APP之間想要做資料傳遞,在WIN8.1也可以做到,也就是分享的概念,中間資料是透過DataPackage來做為傳遞,只要APP中有支援資料的分享,就可以接收來源APP傳遞的資料,是一種單向的資料分享。
WIN 10 APP TO APP
即使演進到了WIN10 APP的開發工作,Launcher依然是叫用其他應用程式或檔案的不二方法,同樣也透過URI/Protocol,但呼叫實作上略有不同,例如想要啟動某個APP時,可以這樣做:
var options = new LauncherOptions();
options.TargetApplicationPackageFamilyName = “24919.InstapaperIt”;
var launchUri = new Uri(“instapaper:?AddUrl=http%3A%2F%2Fbing.com”);
await Launcher.LaunchUriAsync(launchUri, options);
這當中透露出一個訊息,WIN10 APP將PackageFamilyName夠作為APP的唯一的識別名稱,這樣可以很正確而直接的呼叫目標APP,實務上可以透過APP註冊至Store中以取得。
另一個好處是,當前裝置若未安裝此目標APP時,會藉由Store並以該呼叫目標APP之PackageFamilyName作為識別並將使用者導覽至該APP之安裝畫面。
另外在APP間檔案的傳遞上,WIN10也有了新的設計方式-FILE TOKEN,這樣的方式讓我們僅需要告知目標APP檔案的FILE TOKEN,目標APP就能夠透過TOKEN直接取得該檔案,而不必大費周章地想辦法傳遞整個檔案,畢竟都是在同一台裝置上的資源,像是這樣:
var token = SharedStorageAccessManager.AddFile (gpxFile);
ValueSet inputData = new ValueSet();
inputData.Add(“Token”, token);
var launchUri = new Uri(“instapaper:?AddUrl=http%3A%2F%2Fbing.com”);
await Launcher.LaunchUriAsync(launchUri, options, inputData);
APP SERVICE
最後進入這次的主題-APP SERVICE,雖然我們在WIN8.1APP中已經可以達到訊息的傳遞,但往往有時我們需要的不只是單向傳遞資料或命令,因此開始衍伸了請別的APP來處理工作的需要,APP SERVICE便是處理這類問題的最佳選擇。
有過開發WEB經驗的人,相信對WEB SERVICE不會陌生,因為往往有需要透過傳遞要求與資料至WEB SERVICE並將結果送回呼叫端。APP SERVICE概念即如同WEB SERVICE,只是APP SERVICE是執行於裝置上,提供裝置中的其他APP進行呼叫。
SERVICE SIDE
APP SERVICE本身也是一個APP,因此也具有前端的特性,但一定會包含一個背景程式來執行任務,只要收到來自其他APP(CLIENT)的連線請求,便會啟動這個背景程式來完成工作。
首先我們要將APP宣告為APP SERVICE,並且設定連線呼叫成功後程式的進入點,以及指定APP SERVICE的名稱。
以上可至該專案的package.appxmanifest進行設定:
<Applications>
<Application Id=“App“
<Extensions>
<uap:Extension Category=“windows.appService“
EntryPoint=“AppServicesDemoTask.MyBackgroundTask“>
<uap:AppService Name=”MyAppService” />
</uap:Extension>
</Extensions>
</Application>
</Applications>
然後方才指定之進入點的類別會繼承IBackgrundTask介面並且實作,此時我們會先宣告相關物件及事件如下:
BackgroundTaskDeferral
IBackgroundTaskInstance.Canceled (任務中止)
AppServiceConnection.RequestReceived (接收連線請求)
public sealed class MyBackgroundTask : IBackgroundTask
{
private BackgroundTaskDeferral backgroundTaskDeferral;
private AppServiceConnection appServiceconnection;
public void Run(IBackgroundTaskInstance taskInstance)
{
this.backgroundTaskDeferral = taskInstance.GetDeferral();
taskInstance.Canceled += OnTaskCanceled;
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
…
RequestReceived事件中可進行接收到資料後的處理,並透過AppServiceRequestReceivedEventArgs.Request.Message取得呼叫端送來的資料,連線中無論接收或是傳回的資料,都是使用ValueSet封裝資料並來進行交換, AppServiceRequestReceivedEventArgs.Request.SendResponseAsync()則是最後負責將結果(ValueSet)傳回至呼叫端,至此以完成一次APP Service的呼叫。
private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
ValueSet message = args.Request.Message;
ValueSet returnData = new ValueSet();
string command = message[“Command”] as string;
switch (command)
{
case ” Command1″:
{
returnData.Add(“Result”, “ResultA”);
returnData.Add(“Status”, “OK”);
break;
}
case ” Command2″:
{
returnData.Add(“Result”, “ResultB”);
returnData.Add(“Status”, “OK”);
break;
}
}
await args.Request.SendResponseAsync(returnData);
messageDeferral.Complete();
}
CLIENT SIDE
接著介紹CLIENT端APP的呼叫方式,首先建立連線是首要工作,為避免所有SERVICE都能任意呼叫,前面介紹過一項很重要的參數設定,也就是PackageFamilyName,在這時候會需要派上用場,並且搭配在APP Service設定之AppServiceName,指定於AppServiceConnection物件PackageFamilyName與AppServiceName之屬性,然後使用OpenAsync()開啟與APP SERVICE間的連線,並以該方法回傳之AppServiceConnectionStatus來判斷連線狀態是否成功。 再來當連線建立後,當然就是要把我們要傳送至APP SERVICE的資料打包並送出,這邊當然還是以Key-Value的組合加入至ValueSet中。並以SendMessageAsync()將資料送出並得到APP SERVICE回傳之結果。
private AppServiceConnection inventoryService;
if (this.inventoryService == null)
{
this.inventoryService = new AppServiceConnection();
this.inventoryService.AppServiceName = “MyAppService”;
this.inventoryService.PackageFamilyName = “(APP Service’s PackageFamilyName)”;
var status = await this.inventoryService.OpenAsync();
if (status != AppServiceConnectionStatus.Success)
{
return;
}
}
var message = new ValueSet();
message.Add(“Command”, “Commamd1”);
AppServiceResponse response = await this.inventoryService.SendMessageAsync(message);
string result = “”;
if (response.Status == AppServiceResponseStatus.Success)
{
if (response.Message[“Status”] as string == “OK”)
{
result = response.Message[“Result”] as string;
}
}
…
以上就是App Service端與App Client端的運行結構與呼叫方式,基本在使用上並不困難。並且我們已經透過AppServiceName與PackageFamilyName來限制呼叫端,以至於不會讓任何App都能自由的呼叫App Service,不過我們能在App Service中設定所謂白名單,透過指定App的PackageFamilyName,來過濾特定的App能夠呼叫並且建立連線。
最後提供一個開發時對於App Service的偵錯技巧,先到App Service的專案屬性中,在偵錯標籤中勾選「不啟動,但在我的程式碼啟動時進行偵錯」,接著於背景任務中設定中斷點,然後對該專案選擇偵錯 > 開始新執行個體。之後啟動部屬在裝置上的Client App然後執行App Service的呼叫,便可進入到Service設定的中斷點以進行偵錯。
伴隨Windows 10的誕生,催生了詮力科技PowerNAS Windows版本,因此在未來Windows App應用的重要性更顯得重要,如本篇介紹之App Service,即可提供一個整合性的NAS App服務端,提供其他PowerNAS Client App呼叫並且取得資料甚至執行命令。讓原先App的應用不再只是單打獨鬥,而更加強了App間的互動與協調性。
Reference:
https://msdn.microsoft.com/zh-tw/library/windows/apps/mt187314.aspx
http://www.microsoftvirtualacademy.com/training-courses/a-developers-guide-to-windows-10