標籤:
在以往版本中,背景工作都是以獨立的專用進程來運行,因此,定義背景工作代碼的類型都要位於 Windows 運行時組件項目中。
不過,在14393中,SDK 作了相應的擴充,不僅支援以前的獨立進程中運行背景工作,也允許背景工作與應用程式位於同一個進程中執行,即單進程背景工作(Single – Process)。
聽起來很高深?其實很Easy,和以往的多進程模式的背景工作差不多,只是有以下兩點不同:
- 對於獨立進程的背景工作,實現方法是實現 IBackgroundTask 介面,然後實現 Run 方法;而如果你希望讓背景工作在應用所在的進程中執行,可以重寫 Application 類的 OnBackgroundActivated 方法就可以了,它類似於 IBackgroundTask 的 Run 方法。在OnBackgroundActivated方法中,你可以通過方法參數獲得一個IBackgroundTaskInstance執行個體,所以與Run方法的處理是一樣的。
- 在配置資訊清單檔時,獨立進程中執行的背景工作是必須指明進入點的,即背景工作類的類型名,包含命名空間路徑。而如果背景工作是在應用進程中執行的話,就不需要指點進入點,因為背景工作的進入點與應用相同,就是App類。
只要明白了以上兩點,你就明白了95%了,剩下的5%,就等老周來示範給大夥瞧吧。
App Service 的實現跟背景工作差不多,本次表演,老周就選用AppService來試水吧。
這個樣本只有小學二年級水平,它分為兩個應用,一個應用具備app service,另一個應用調用它。service的功能是計算兩個整數的乘積,所以說是小學二年級水平。
先看app service的應用實現,項目模板會為我們產生一個App類,基類是Application,很簡單,直接重寫OnBackgroundActivated方法就行了。
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { …… }
這個方法就相當於Run方法,所以,和以前一樣,在其中添加處理代碼。
IBackgroundTaskInstance taskInstance = args.TaskInstance; var taskDef = taskInstance.GetDeferral(); taskInstance.Canceled += (ca, cb) => taskDef.Complete(); if (taskInstance.TriggerDetails != null && taskInstance.TriggerDetails is AppServiceTriggerDetails) { AppServiceTriggerDetails details = taskInstance.TriggerDetails as AppServiceTriggerDetails; string appsvName = details.Name; if (appsvName == "my.multip") { AppServiceConnection conn = details.AppServiceConnection; conn.RequestReceived += async (r1, r2) => { var svdef = r2.GetDeferral(); var request = r2.Request; ValueSet inputs = request.Message; int x = Convert.ToInt32(inputs["a"]); int y = Convert.ToInt32(inputs["b"]); int o = x * y; ValueSet sendBack = new ValueSet(); sendBack["r"] = o; await request.SendResponseAsync(sendBack); svdef.Complete(); taskDef.Complete(); }; conn.ServiceClosed += (k1, k2) => { Debug.WriteLine("app service串連關閉。"); taskDef.Complete(); }; } }
然後就完事了,注意在資訊清單檔中,不要指定進入點了。
<Extensions> <uap:Extension Category="windows.appService"> <uap:AppService Name="my.multip" /> </uap:Extension> </Extensions>
現在可以在另一個應用中調用了。
AppServiceConnection conn = new AppServiceConnection(); conn.AppServiceName = "my.multip"; conn.PackageFamilyName = txtPkname.Text; var state = await conn.OpenAsync(); if (state == AppServiceConnectionStatus.Success) { ValueSet input = new ValueSet(); input["a"] = ComputeObject.Num1; input["b"] = ComputeObject.Num2; var response = await conn.SendMessageAsync(input); if (response.Status == AppServiceResponseStatus.Success) { ValueSet res = response.Message; ComputeObject.Result = Convert.ToInt32(res["r"]); } }
調用App Service 時,先new一個AppServiceConnection,然後指定包含app service的應用的Package的名字,這個包名可以用以下方法來擷取:
- 在包含app service的應用中,訪問這個靜態屬性擷取:Windows.ApplicationModel.Package.Current.Id.FamilyName。
- 用VS產生項目,然後開啟【輸出】視窗,顯示來源選擇“產生”,這樣你就能看到包含應用服務的應用程式套件名了。如。
不能使用資訊清單檔中的包名,因為那個包名不完整。不過,你得注意了,通過【輸出】視窗擷取包名的時候,包的名字中要去掉版本號碼和平台描述,比如,我的項目中輸出的產生的包名為:
62da1ba5-7faf-4109-b82a-7a6027dbc3a3_1.0.0.0_x86__6pcpwfmxf0rfc
其中,1.0.0.0是版本號碼,要去掉,x86是平台描述,也要幹掉,後面的6pcpwfmxf0rfc可能是開發人員的標識,不能去掉。最終得到需要的包名為:
62da1ba5-7faf-4109-b82a-7a6027dbc3a3_6pcpwfmxf0rfc
前面的GUID是應用程式套件名字,後面要接一個底線,然後是6pcpwfmxf0rfc。
把這個最終取得的名字賦值給AppServiceConnection的PackageFamilyName屬性即可,AppServiceName屬性工作表示要調用的app service的名字。
準備好參數後,調用OpenAsync方法開啟串連,一定要先開啟串連,才能調用應用服務。使用SendMessageAsync方法發送輸入參數,參數是一個ValueSet對象,其實是個字典,可以自訂參數結構。在本例中,既然要計算乘法運算,當然是要傳遞兩個整數值了。
SendMessageAsync方法調用後,會非同步返回一個AppServiceResponse執行個體,該執行個體中包含著一些從應用服務返回的內容,訪問Message屬性就得到應用服務響應的ValueSet,並可從中取出需要的資料,該例子中,是取出計算結果。
好,項目幹完了,咱們來試試,同時運行兩個應用,然後試著調用一下應用服務。
效果已達到, 這時候,大夥可能會疑惑,如果包含app serivce的應用進程退出後,還能調用應用服務嗎?沒事,許多事情就是試出來的,試試看。把包含應用服務的應用進程結束掉,然後再調用一次,發現是可以成功調用的。
有了這一招,定義背景工作就靈活很多了,既可以在獨立進程中完成,也可以在應用進程中完成,具體採用哪一種,就看實際情況了。一切東西都是靈活運用的,千萬不要把技術學死了。那些整天吃飽了撐著,想把什麼東西都變成公式化的思想是幼稚的、死板的,這個世界上,不可以量化的事情多得很。
不過,老周可以發表一些低見,僅作參考。如果背景工作的觸發源與應用程式關係不大,比如使用者登入/登出時執行的,每隔一段時間執行的(定時),這些情況,建議把背景工作寫到獨立的Windows運行時組件項目中,讓它以獨立的進程進行。
要是背景工作是應用程式主動觸發的,比如後台轉碼(音/視頻處理),或者由應用程式使用Application Trigger觸發的背景工作,都可以考慮把它歸入應用程式進程中,即本文所講述的情況。
好了,今天的牛逼吹完了,該去喝點茶了(白開水最好喝,集天地靈氣,無雜質,無負作用),下一篇文章咱們聊聊開機前的事情。
樣本原始碼下載
【Win 10 應用開發】在App所在的進程中執行背景工作