在手機上,使用后台,不像電腦上那么隨意,准確地講嘛,在移動平台上,后台任務都有嚴格的限制。至於說為什么會有這么多限制,我估計初衷很明顯——保證系統的性能不受某個或某幾個應用的負面影響;另外就是出於安全性考慮。
畢竟手機設備不同於電腦,一旦后台程序泛濫成災,是很難進行管理的,要防止這些不可預知的事件,只能從源頭上杜絕。因此,當初在RT應用上就是:允許后台任務,但你得按規矩做人。
無規矩不成方圓,沒有限制就會亂象橫生,無法控制,現在某手機系統就深陷這一問題無法自撥,WP沒有必要重蹈這一覆轍。
好,前面廢話了那么多,就是要告訴大家,在開發的時候一定要想清楚,到底該不該使用后台任務,使用后台任務的話,一定要自己親測一下,是否會明顯消耗電量,是否會產生龐大的網絡流量。大家一定要養成一個好習慣——以做流氓程序為恥。說到這里,不得不鄙視一下國內的某些公司。
既然后台任務是有限制的,那么,官方是通過什么方式來限制的呢?
觸發器:后台任務必須通過觸發器來執行。這個觸發器,可以理解為類似生物的條件反射。清單文件給出的觸發類型有:后台音頻、計時器、系統維護等。記得前面我寫過博客,弄了一個在點亮屏幕時播放音樂,雖然這種做法不盡合理,但也作為一種參考吧。那個示例就是通過系統觸發器中的UserPresent類型觸發的,就是當用戶把手機屏幕點亮這一行為發生后就會執行后台任務。
比如,比較常用的還有計時器,這個應該會用得較多,比如每隔一段時間提取一批新聞列表等,如每30分鍾就執行一次。當然這計時器是有限制的,常規要求是至少15分鍾。不然的話,一些人品不端正的開發者弄個每分鍾觸發一次,那還得了,如果它是提取廣告的話,那不斷地彈廣告就會把用戶彈傻了。大家千萬別干這種事,你要干這種事,我只能說你太沒出息。
凡是實現了IBackgroundTrigger接口的都是觸發器,常以“Trigger”結尾,並不是所有觸發器都能用,有些是需要申請的,比如ChatMessageNotificationTrigger,這也是防止惡意程序的做法。
執行條件:這個主要由SystemCondition類表示。執行條件與觸發器不同,觸發器是表明在什么事實發生后執行后台任務,而執行條件是在什么情況下才能執行后台任務。你聽起來好像意思接近,實際上是不同的。比如,如果你的后台任務需要從網絡上獲取數據,而且觸發器為每25分鍾一次的計時器,於是在注冊后台任務時,你可以考慮增加一個執行條件:在有網絡連接的前提下執行。雖然計時器的時間到了,但正好這個時候,由於用戶的手機卡欠下4G流量費8622000元,被停機了,沒法上網,那么系統檢測到不存在有效的網絡連接,就不執行后台任務了(可以考慮用WiFi)。
如何使用后台任務
前面扯了一堆臊話,主要是讓朋友們對后台任務有個大致的了解,現在向大家介紹如何用后台。
代表后台任務的代碼通常應該寫在一個獨立的組件中,這樣做也比較科學的,有些朋友在開發程序時,喜歡把所有功能都塞到一個主程序里,許多人在開發桌面程序時就喜歡這樣,把所有功能都塞進一個exe中,連.dll都不舍得多用一個,我不喜歡這樣做的,一般我是一個功能模板用一個獨立的.dll,至於.exe用來放UI或者一些核心處理。
因此,第一步是向解決方案中添加一個Windows運行時組件,看清楚,是運行時組件,最后會生成.wimd文件,如果是類庫就會生成.dll文件。后台任務都應寫在Windows運行時組件中。這個應該會了吧,就是在“解決方案資源管理器”中新建項目,然后選Windows運行時組件,輸入項目名字就可以了,如果你不會這個,我只能告訴你:基礎不扎實,舉步艱難。
然后在新建的運行時組件中定義一個類,一定要是public的,不要問我為什么(請復習程序集的可訪問性),這個類要實現IBackgroundTask接口,表示它用於執行后台任務。
public sealed class DemoTask : IBackgroundTask { public void Run ( IBackgroundTaskInstance taskInstance ) { } }
重點是實現Run方法,當后台任務執行時,就是調用這個方法的,你的后台任務要做什么就寫在Run里面。
注意到有個IBackgroundTaskInstance類型的參數,是個接口,我們不用管它哪個類實現,這個在系統調用時會自動賦值,只關心它有哪些成員即可。
一定要明白GetDeferral方法的作用,它可以返回一個BackgroundTaskDeferral對象,這個對象的作用,說簡單一點就是用來拖延時間的,就是拖延后台任務的時間,當你覺得后台任務已經完成了,就調用用BackgroundTaskDeferral的Complete方法,告訴系統:任務做完了,系統收到報告后,就可以清理該任務了。
現在我們來實現一下這個任務。
public void Run ( IBackgroundTaskInstance taskInstance ) { var def = taskInstance.GetDeferral(); // 獲取XML模板 XmlDocument docx = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); // 修改XML var eles = docx.GetElementsByTagName("text"); if (eles.Count < 2) { def.Complete(); return; } XmlElement text01 = (XmlElement)eles[0]; text01.AppendChild(docx.CreateTextNode("示例")); XmlElement text02 = (XmlElement)eles[1]; text02.AppendChild(docx.CreateTextNode("后台任務執行了。")); // 產生Toast通知 ToastNotification notification = new ToastNotification(docx); // 顯示通知 ToastNotificationManager.CreateToastNotifier().Show(notification); // 報告任務完成 def.Complete(); }
這個任務,不復雜,就是向用戶發一條Toast通知。
好,后台任務已經寫好了,現在要在主項目中對它進行引用,一定不要忘了,只有引用了上面寫的Windows運行時組件才能訪問它,這和以前.net項目一樣。
這個后台任務,我打算讓它每40分鍾執行一次,所以觸發器應選用TimeTrigger。下面先簡單設計一下UI。
<StackPanel> <Button Content="注冊后台任務" Click="開始注冊"/> <Button Content="取消后台任務" Click="取消注冊"/> <TextBlock x:Name="tb" FontSize="20" TextWrapping="Wrap"/> </StackPanel>
界面簡單,你看得懂的,其實事件處理的方法名是可以用中文的,在VS里面,類型名、命名空間名、成員名、參數名都可以用中文的,如果你不喜歡英文名字,可以用中文。
通常,注冊后台任務需要准備以下證件:
1、個人身份證,即后台任務的名字,這個名字必須唯一,不能與現有的任務重復。
2、戶口本。即入口點,指的是我們前面在Windows運行時組件中定義的那個類的類名,就是那個實現IBackgroundTask接口以及Run方法的類。
為了在輸入代碼時不容易輸入,最好先將這些內容聲明為常量。
/// <summary> /// 任務的唯一名稱 /// </summary> const string TASK_NAME = "haha_task"; /// <summary> /// 入口點 /// </summary> const string ENTRY_POINT = "BackTest.DemoTask";
注意,入口點的名字是要包含命名空間名字的。
好,前期手續基本辦完,現在可以正式注冊了。下面代碼分別用於注冊和取消注冊后台任務。
private async void 開始注冊 ( object sender, RoutedEventArgs e ) { var res = await BackgroundExecutionManager.RequestAccessAsync(); if (res != BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity) return; // 注冊 BackgroundTaskBuilder bd = new BackgroundTaskBuilder(); bd.Name = TASK_NAME; //任務名 bd.TaskEntryPoint = ENTRY_POINT; //入口點 // 設置觸發器為計時器 bd.SetTrigger(new TimeTrigger(40, false)); try { BackgroundTaskRegistration reg = bd.Register(); // 注冊成功,顯示結果 tb.Text = string.Format("任務名:{0}\n任務ID:{1}", reg.Name, reg.TaskId); } catch (Exception ex) { tb.Text = ex.Message; } } private void 取消注冊 ( object sender, RoutedEventArgs e ) { // 先查找一下是否已經注冊了任務 var task = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(t => t.Name == TASK_NAME); if (task != null) { // 取消 task.Unregister(true); } }
1、在注冊之前一定要調用BackgroundExecutionManager.RequestAccessAsync方法,是然不會彈出任何提示,但不要忘了,不調用是不能注冊的。
2、BackgroundTaskBuilder類,顧名思義,就是用來注冊后台任務的。
3、TimeTrigger觸發器的構造函數的第二個參數如果為true,那后台任務只運行一次,這里我希望它每40分鍾運行一次,所以為false。
現在,離成果不遠了,但是還有一步很關鍵,那就是配置清單文件。
打開Package.appxmanifest文件,切換到“聲明”選項頁,在可用的聲明列表中選“后台任務”,然后點擊添加按鈕。
在右邊的頁面中,按實際情況勾選,我這個例子是計時的,所以選計時器,然后在下面再填寫一下入口點,這個入口點和注冊后台任務時用的入口點是一樣的,可以直接從剛才的代碼中復制過來。
因為本例要用到Toast通知,所以要讓應用支持Toast。切換到“應用程序”選項卡,在下面有關通知設置的地方,將支持Toast通知設置為“是”。
最后,保存並關閉清單文件。
如何調試后台任務
像我這個例子,要等40分鍾才執行的,難道我在干等不成? 非也,我們不用在那里苦等40分鍾,VS會幫助我們進行調試的。
首先,運行應用程序,然后點頁面上的注冊按鈕,確保后台任務已經成功注冊。
然后回到VS,在“調試位置”工具上,點擊“生命周期事件”按鈕右邊的下拉箭頭,從下拉菜單中你會看到有后台任務的名字。如下圖。
如果看不到,你可以稍等一下,如果一直不出現,你可以重復上面的步驟。只要后台任務注冊成功的,肯定會顯示的(后台音頻和推送通知除外)。
所以,現在你調試后台任務就很方便,直從“調試位置”工具欄的下拉菜單中選中后台任務,后台任務就會執行了。
OK,相信通過這篇破文,初學者能夠初步認識后台任務。
示例源碼:http://files.cnblogs.com/tcjiaan/SampleAppBackTask.zip