參考文獻(xiàn):
1. 31 days of Windows 8#12 Background Task:
http://www.jeffblankenburg.com/2012/11/12/31-days-of-windows-8-day-12-background-tasks/
2. 31 days of Windows 8 #11 Lock Screen :
http://www.jeffblankenburg.com/2012/11/11/31-days-of-windows-8-day-11-lock-screen-apps/
3. Background Task 白皮書(shū):
http://www.microsoft.com/en-us/download/details.aspx?id=27411在使用鎖屏應(yīng)用的時(shí)候,你要保證你明白自己要做些什么準(zhǔn)備:
1. 使用Wide Logo,即你要提供wide logo 310*150
2. 使用Badge, 即你要提供badge logo 24*24
3. 使用Background Task, 而且background task 支持的事件是Control channel, timer, push notification類(lèi)型。
4. 設(shè)置你的Background Task的EntryPoint
5. 你需要在Manifest里面聲明你使用LockScreen。
具體的操作步驟大家可以參考文獻(xiàn)2。
鎖屏應(yīng)用比較簡(jiǎn)單,重要的一點(diǎn)是,如果你在程序中詢(xún)問(wèn)了用戶(hù)是否允許使用鎖屏應(yīng)用,你將只有這一次修改的機(jī)會(huì),不然你就只能通過(guò)PC Control進(jìn)行設(shè)置了。但是如果你的Windows 8 沒(méi)有激活的話,你只能卸載應(yīng)用程序,重新安裝一遍了。
代碼如下:
1 //要先請(qǐng)求允許,如果允許的話,你才能更新Badge,如果沒(méi)有這一步,Badge將 不會(huì)顯示在LockScreen上面
2 create_task(BackgroundExecutionManager::RequestAccessAsync()).then([this](BackgroundAccessStatus status)
3 {
4 //這一步可以不要
5 //BackgroundAccessStatus status1 = BackgroundExecutionManager::GetAccessStatus();
6 if((status == BackgroundAccessStatus::AllowedWithAlwaysOnRealTimeConnectivity)||
7 (status == BackgroundAccessStatus::AllowedMayUseActiveRealTimeConnectivity))
8 {
9 XmlDocument^ badgeData = BadgeUpdateManager::GetTemplateContent(BadgeTemplateType::BadgeNumber);
10 XmlNodeList^ badgeXML = badgeData->GetElementsByTagName("badge");
11 ((XmlElement^)badgeXML->Item(0))->SetAttribute("value","Playing");
12
13 BadgeNotification^ badge = ref new BadgeNotification(badgeData);
14 BadgeUpdateManager::CreateBadgeUpdaterForApplication()->Update(badge);
15 }
16 });
運(yùn)行,然后會(huì)彈出一個(gè)對(duì)話框,像這樣

點(diǎn)擊Allow之后,按下WIN+L你就可以看到鎖屏上面出現(xiàn)的Badge圖片和你的number了。
一般情況下,我們會(huì)使用后臺(tái)任務(wù)(Background Task)來(lái)更新Lock Screen App的數(shù)據(jù)。
后臺(tái)任務(wù)
1. 什么是后臺(tái)任務(wù)? 后臺(tái)任務(wù)是:即使程序已經(jīng)被掛起或者不在運(yùn)行了,還在默默地執(zhí)行的任務(wù)。
2.
后臺(tái)任務(wù)與前臺(tái)任務(wù)的區(qū)別? 首先,前臺(tái)任務(wù)占據(jù)了整個(gè)屏幕,用戶(hù)直接與其進(jìn)行交互。后臺(tái)任務(wù)不能與用戶(hù)交互,除了(Tile, Toast, 和Lock Screen)
其次,因?yàn)榍芭_(tái)要與用戶(hù)交互,它使用所有可用的系統(tǒng)資源,包括CPU time 和Network資源,并且不受限。后臺(tái)任務(wù)使用系統(tǒng)資源的時(shí)候是受限制的。
再次,前臺(tái)任務(wù)處理主要事務(wù),后臺(tái)任務(wù)處理短時(shí)間、輕量級(jí)的事務(wù)。
最后,后臺(tái)任務(wù)不論前臺(tái)任務(wù)是否處于Running狀態(tài),它都會(huì)運(yùn)行。
3. 后臺(tái)任務(wù)的目的? 大家都知道,Windows 8 應(yīng)用程序的生命周期分為Running,Suspended,Terminated三種狀態(tài)。App處于前臺(tái)時(shí),為Running狀態(tài),處于后臺(tái)時(shí),為Suspended狀態(tài),用戶(hù)關(guān)閉App時(shí)或者在Suspended狀態(tài)太久,系統(tǒng)自動(dòng)關(guān)閉App時(shí),為T(mén)erminated狀態(tài)。
我們可以從Suspended狀態(tài)將App變?yōu)镽unning狀態(tài),我們不能在后臺(tái)運(yùn)行太復(fù)雜,太耗時(shí)耗資源的程序,因?yàn)槿绻氵@么做了,你將會(huì)非常耗費(fèi)電量,并且,用戶(hù)切換回前臺(tái)時(shí),會(huì)感覺(jué)到非???,有延遲。
因?yàn)樯鲜龅哪康模貉娱L(zhǎng)電池用量,保證用戶(hù)流暢的體驗(yàn),我們需要限制后臺(tái)任務(wù)對(duì)資源的使用,而且我們的后臺(tái)任務(wù)要盡量精簡(jiǎn)。
4. 后臺(tái)任務(wù)的執(zhí)行環(huán)境? 一般情況下,我們都要把我們的后臺(tái)任務(wù)作為一個(gè)Runtime Component,引用到主工程中去。這樣,一個(gè)后臺(tái)任務(wù)就是一個(gè)class library,一個(gè)in-proc server DLL。這個(gè)library可以在我們的App中運(yùn)行,也可以在系統(tǒng)提供的主機(jī)環(huán)境中運(yùn)行(BackgroundTaskHost.exe)。這個(gè)exe是在App相同的容器內(nèi)運(yùn)行,當(dāng)它不需要的時(shí)候,會(huì)自動(dòng)退出。
5. 后臺(tái)任務(wù)的適合場(chǎng)景?
播放音樂(lè),上傳下載文件,刷新瓷貼、通知、LockScreen,應(yīng)用程序間共享合約。下載Mail,VOIP、IM信息,用戶(hù)改變系統(tǒng)設(shè)置
6. 后臺(tái)任務(wù)基本概念?
Background task 一個(gè)實(shí)現(xiàn)了IBackgroundTask接口的類(lèi)
A class or JavaScript page implemented by the app to provide functionality even if the app is not in the foreground.
Background trigger 一系列事件,每個(gè)后臺(tái)任務(wù)都需要至少一個(gè)Trigger
A system-defined event that an app can associate with a background task. When the trigger is fired by the system, an app background task that is associated with the trigger is launched.
Background condition 一些必須滿足的條件,可以有,也可以沒(méi)有condition
A set of zero or more conditions that need to be satisfied before the background task can run.
BackgroundTaskHost.exe 一個(gè)裝載后臺(tái)任務(wù)的容器
A system-provided host executable to run the background task.
EntryPoint 一個(gè)實(shí)現(xiàn)了IBackgroundTask接口的類(lèi)的名字
The name of the C# or C++ class that implements the background task.
Executable
The name of the executable that hosts the background task class.
Foreground app
The app that the user is actively interacting with.
Lock screen Win+L就可以看到你的Lock Screen了
This is the first screen shown following a Windows 8 boot, resuming from sleep, or locking your PC. It presents a user-customizable surface that both conveys information and protects against accidental logon attempts.
Start page
The name of the JavaScript page that implements the background task.
7. 后臺(tái)任務(wù)的運(yùn)行原理?
后臺(tái)任務(wù)的注冊(cè),運(yùn)行,調(diào)試,請(qǐng)參閱參考文獻(xiàn)1,2,3.這里主要介紹一下后臺(tái)任務(wù)的運(yùn)行原理。大家可以參考下圖進(jìn)行理解

虛線兩邊分別表示App和System,App就是我們的應(yīng)用程序,System就是負(fù)責(zé)處理后臺(tái)任務(wù)的Service。
首先,我們要注冊(cè)Trigger,Trigger有多種,大家可以參考文獻(xiàn)3。
其次,在應(yīng)用程序中注冊(cè)后臺(tái)任務(wù)(包含了什么樣的Trigger可以觸發(fā)這個(gè)后臺(tái)任務(wù)),注冊(cè)之后,在System Infra中就永久保留了這個(gè)注冊(cè)信息。不論你是否關(guān)閉了應(yīng)用程序還是重新啟動(dòng)了電腦,這個(gè)注冊(cè)信息都會(huì)存在。
再次,當(dāng)合適的Trigger事件來(lái)臨,System Infra 會(huì)搜索與這個(gè)Trigger相匹配的后臺(tái)任務(wù)
最后,啟動(dòng)該后臺(tái)任務(wù)。
我們可以在任務(wù)管理器中找到Background Task Infrastracture Service :

右鍵點(diǎn)擊,Open Service,可以看到具體的描述:

可以看到,這個(gè)服務(wù)就是控制哪個(gè)后臺(tái)任務(wù)可以在系統(tǒng)中運(yùn)行,也就是我們圖中的System Infra。
8 App狀態(tài)與后臺(tái)任務(wù)的關(guān)系 ?
看到上面的過(guò)程,大家或許會(huì)疑問(wèn),如果Trigger事件來(lái)臨,App已經(jīng)關(guān)閉,BackgroundTask還會(huì)執(zhí)行么?答案是:一定會(huì),不論你的App是Running還是Suspended或者是Terminated狀態(tài)。
但是,還記得我們說(shuō)過(guò)的,我們的后臺(tái)任務(wù)可以運(yùn)行于App中,也可以運(yùn)行于系統(tǒng)提供的環(huán)境中么?
如果運(yùn)行于系統(tǒng)提供的環(huán)境中,那么,答案同上,而且App保持原來(lái)的狀態(tài)。例如:App是Terminate狀態(tài),后臺(tái)任務(wù)運(yùn)行,App不會(huì)啟動(dòng),依舊是Terminate狀態(tài)。
如果運(yùn)行于App中,那么答案同上,但是App的狀態(tài)會(huì)略有不同,這種不同只存在Terminate狀態(tài),如果App是Terminate狀態(tài)的話,Trigger來(lái)臨,App會(huì)被啟動(dòng),但是其UI線程不會(huì)被啟動(dòng),后臺(tái)任務(wù)啟動(dòng)。也就是說(shuō)App雖然啟動(dòng)了,但是不會(huì)將App帶回前臺(tái)。
也就是說(shuō),無(wú)論如何,當(dāng)Trigger事件被觸發(fā),后臺(tái)任務(wù)的啟動(dòng)是由系統(tǒng)來(lái)決定的,無(wú)論如何后臺(tái)任務(wù)都會(huì)執(zhí)行,App的狀態(tài)在上述兩種情況下會(huì)略有不同。
另外個(gè)人觀點(diǎn):App只是提供了一個(gè)注冊(cè)后臺(tái)任務(wù)的平臺(tái),注冊(cè)了之后,后臺(tái)任務(wù)的控制權(quán)就交由系統(tǒng)管理了。
9. 后臺(tái)任務(wù)的執(zhí)行環(huán)境 ?
之前我們已經(jīng)提到,后臺(tái)任務(wù)可以在App中運(yùn)行,也可以在系統(tǒng)提供的主機(jī)中運(yùn)行。如果沒(méi)有特別聲明,后臺(tái)任務(wù)是默認(rèn)在系統(tǒng)提供的主機(jī)中運(yùn)行的,這樣做有一些好處:
首先,它的啟動(dòng)同應(yīng)用程序的狀態(tài)無(wú)關(guān);
其次,啟動(dòng)后臺(tái)任務(wù)更快;
再次,使用的資源更少;
最后,比在App中啟動(dòng)擁有更高的性能。
如何控制后臺(tái)任務(wù)在哪個(gè)容器下運(yùn)行呢?這跟Trigger的類(lèi)型相關(guān):
不同的后臺(tái)trigger對(duì)于在什么地方運(yùn)行有不同的限制。對(duì)于默認(rèn)的,沒(méi)有指定Executable屬性的時(shí)候,后臺(tái)任務(wù)是運(yùn)行在系統(tǒng)提供的主機(jī)中的。App是不能指定Executable屬性的,如果它必須要在系統(tǒng)提供的主機(jī)中與性的。只有那些包含PushNotificationTrigger或者ControlChannelTrigger任務(wù)類(lèi)型的后臺(tái)任務(wù)才能指定Executable屬性。
10. 后臺(tái)任務(wù)注冊(cè)的持久性?
只要你注冊(cè)了一個(gè)后臺(tái)任務(wù),你就可以永久地?fù)碛兴徽撃愕腁pp是什么狀態(tài),也不論你的電腦是否關(guān)閉過(guò),甚至,不論你的應(yīng)用程序有沒(méi)有更新過(guò)。也就是說(shuō)后臺(tái)任務(wù)注冊(cè)的持久性可以跨越App的更新。
要實(shí)現(xiàn)這點(diǎn)必須要注意:你必須要保證你的EntryPoint的一致性,即在新的版本中,同樣的EntryPoint一定要存在。如果不存在了,那么在后臺(tái)任務(wù)執(zhí)行中將會(huì)出現(xiàn)錯(cuò)誤。另外,如果我們的新版本中已經(jīng)不再相應(yīng)某trigger的后臺(tái)任務(wù)了,怎么辦?新版本的App可以注冊(cè)一個(gè)ServicingComplete Trigger的后臺(tái)任務(wù),用來(lái)提醒當(dāng)App更新之后,一個(gè)未被注冊(cè)的后臺(tái)任務(wù)是失效的。
注意,一旦App被卸載,所有的后臺(tái)任務(wù)將不復(fù)存在。
11. 后臺(tái)任務(wù)實(shí)現(xiàn)中的一些知識(shí)點(diǎn)?
后臺(tái)任務(wù)至少應(yīng)該設(shè)置一個(gè)Trigger event。
后臺(tái)任務(wù)可以沒(méi)有condition,也可以有多個(gè)conditions。condition擁有門(mén)閂行為,意思是,必須所有的condition都滿足,才能啟動(dòng)后臺(tái)任務(wù),而不管Trigger事件有沒(méi)有被觸發(fā)。這種行為像是鎖住了trigger,等到conditions都滿足了才launch 后臺(tái)任務(wù)。
后臺(tái)任務(wù)可以向前臺(tái)報(bào)告其進(jìn)度和完成情況:
1 void SampleBackgroundTask::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
2 {
3 auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
4 {
5 auto progress = "Progress: " + args->Progress + "%";
6 BackgroundTaskSample::SampleBackgroundTaskProgress = progress;
7 UpdateUI();
8 };
9 task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
10
11 auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
12 {
13 UpdateUI();
14 };
15 task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
16 }
但是需要注意的是,每次啟動(dòng)的時(shí)候都要將這兩個(gè)事件關(guān)聯(lián)到后臺(tái)任務(wù)上,因?yàn)檫@兩個(gè)事件不是持久性的,當(dāng)App被關(guān)閉的時(shí)候,他們也會(huì)被銷(xiāo)毀。
可以取消后臺(tái)任務(wù):
void SampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &SampleBackgroundTask::OnCanceled);
//
}
void SampleBackgroundTask::OnCanceled(IBackgroundTaskInstance^ taskInstance, BackgroundTaskCancellationReason reason)
{
CancelRequested = true;
}
注意,取消事件和上述兩個(gè)事件是在不同的類(lèi)型對(duì)象中關(guān)聯(lián)的。
12. 后臺(tái)任務(wù)的資源管理?
如果App處于前臺(tái),用戶(hù)正在與App進(jìn)行交互的時(shí)候,那么后臺(tái)任務(wù)沒(méi)有資源限制。如果一個(gè)App沒(méi)有在前臺(tái)時(shí),限制就出現(xiàn)了。
文章的開(kāi)頭已經(jīng)介紹了后臺(tái)任務(wù)的執(zhí)行環(huán)境是資源受限制的環(huán)境,那么哪些資源是受限制的呢?
首先,CPU time,一個(gè)Lock Screen App 每15分鐘會(huì)獲得2S的CPU時(shí)間來(lái)處理所有的后臺(tái)任務(wù),一個(gè)非Lock Screen App每2小時(shí)獲得1S的CPU事件來(lái)執(zhí)行后臺(tái)任務(wù)。這2S是15分鐘的最后2S。如果App使用了所有的CPU事件,那么后臺(tái)任務(wù)將會(huì)等待下一個(gè)15分鐘,如果有分配的事件,那么將會(huì)執(zhí)行。

其次,NetWork。如果App處于AC power模式,那么后臺(tái)任務(wù)沒(méi)有網(wǎng)絡(luò)資源的限制。Network resource constraints are a function of the amount of energy used by the network interface, which is modeled as the amount of time the network is used to transfer bytes (for download or upload); it is not based on any throughput metrics. The throughput and energy usage of a network interface varies based on factors like link capacity, physical position of the device, or the current load on the WiFi network. WiFi networks typically have lower energy consumption per byte sent and received when compared to cellular mobile networks

LockScreen App在1Mbps帶寬下,每15分鐘可以下載0.469MB的數(shù)據(jù),每天下載45MB,在10Mbps帶寬下每15分鐘4.69MB,每天450MB
非LockScreen App 在1Mbps帶寬下,每2小時(shí)可以下載0.625MB數(shù)據(jù),每天下載7.5MB,在10Mbps帶寬下每2小時(shí)6.25MB,每天75MB
13. 后臺(tái)任務(wù)執(zhí)行于App中的線程模型?去耦
對(duì)于一個(gè)非JavaScript App,后臺(tái)任務(wù)位于in-proc DLL中在MTA中加載。一個(gè)真正的后臺(tái)任務(wù)類(lèi)可以是STA 或者 MAT線程類(lèi)型的。因?yàn)楹笈_(tái)任務(wù)可以在AppSuspended或者Terminated時(shí)運(yùn)行,他們需要同前臺(tái)App解耦。在一個(gè)分離的單元中加載后臺(tái)任務(wù)的DLL將強(qiáng)制將后臺(tái)任務(wù)從App中分離。
當(dāng)一個(gè)App處于Suspended狀態(tài)時(shí),UI STA線程被Windows kernel阻塞。這個(gè)線程只有當(dāng)App重新回到Runnning狀態(tài)才被release。當(dāng)app在后臺(tái),并且后臺(tái)任務(wù)被觸發(fā),App中的所有線程都是非凍結(jié)的,除了UI STA線程,并且后臺(tái)任務(wù)被激活于MTA線程中。UI STA持續(xù)被鎖住。如果后臺(tái)任務(wù)嘗試著訪問(wèn)位于UI STA 線程中的對(duì)象是,將會(huì)出現(xiàn)死鎖。為了避免這種情況,后臺(tái)任務(wù)不能同App共享對(duì)象。任何共享的對(duì)象都要聚合FTM(Free Threaded Marshaler)。Control Channel trigger描述了這種應(yīng)用。
在前臺(tái)任務(wù)和后臺(tái)任務(wù)之間共享state
Sharing state between the background task and the foreground app
Another aspect to keep in mind if the background task is loaded within the app instead of the default system-provided host executable is that it cannot rely on accessing the memory of the foreground app. Background tasks run regardless of the current state of the app, so background tasks cannot rely on having the app around when they run. The only reliable way for the background task to share state with the app is to use persistent storage, such as ApplicationData, or files.
14. 使用后臺(tái)任務(wù)的建議?
? Design background tasks to be short lived.
? Design the lock screen user experience as described in the “Guidelines and checklists for lock screen tiles.”
? Do not specify the Executable attribute to ensure the task launches in the system-provided host.
? Describe the background task class name or JavaScript file name accurately in the manifest.
? Look in the event viewer for error messages if the background task is not being activated.
? Use persistent storage to share data between the background task and the app.
? Register for progress and completion handlers in the app.
? Register for a background task cancellation handler in the background task class.
? Register for the ServicingComplete trigger if you expect to update the app.
? Ensure that the background task class library is referenced in the main project and its output type is winmd.
? Describe the triggers in the background manifest accurately.
? Verify if the app needs to be on the lock screen.
? Do not display UI other than toast, tiles or badges from a background task.
? Do not rely on user interaction in background tasks.
15 其他:編程需要注意的細(xì)節(jié):?
如果你的后臺(tái)任務(wù)運(yùn)行任何的異步程序,那么你需要獲得一個(gè)defferral。如果沒(méi)有這個(gè)deferral,Run方法已經(jīng)結(jié)束,異步方法還沒(méi)有結(jié)束的情況下后臺(tái)任務(wù)將會(huì)不正常結(jié)束。
1 void ExampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
2 {
3 BackgroundTaskDeferral^ deferral = taskInstance->GetDeferral();
4
5 //
6 // TODO: Modify the following line of code to call a real async function.
7 // Note that the task<void> return type applies only to async
8 // actions. If you need to call an async operation instead, replace
9 // task<void> with the correct return type.
10 //
11 task<void> myTask(ExampleFunctionAsync());
12
13 myTask.then([=] () {
14 deferral->Complete();
15 });
16 }
17
查看后臺(tái)任務(wù)有沒(méi)有注冊(cè),沒(méi)有的話注冊(cè)一個(gè):
for each (auto task in BackgroundTaskRegistration::AllTasks)//這里保存了本地的所有的注冊(cè)了的后臺(tái)任務(wù)。
{
if(task->Value->Name == "Class1")
{
isRegistered = true;
break;
}
}
if(!isRegistered)
{
RegisterBackgroundTask("TileUpdater", "BackgroundTasks.Class1");
}
這里使用foreach方法比較簡(jiǎn)單。
注冊(cè)一個(gè)后臺(tái)任務(wù)
1 void MainPage::RegisterBackgroundTask(String^ taskName, String^ entryPoint)
2 {
3 BackgroundTaskBuilder^ btb = ref new BackgroundTaskBuilder();
4 btb->Name = taskName;
5 btb->TaskEntryPoint = entryPoint;
6 btb->SetTrigger(ref new SystemTrigger(SystemTriggerType::InternetAvailable,false));
7
8 BackgroundTaskRegistration^ task = btb->Register();
9 }
我覺(jué)得這么總結(jié)一下,后臺(tái)任務(wù)的原理神馬的,大家應(yīng)該稍微清楚一些了。關(guān)于后臺(tái)任務(wù),我也看了很久,之前都是糊里糊涂的,今天終于算整明白的,希望對(duì)大家有用。歡迎交流。
posted on 2013-01-08 13:00
Dino-Tech 閱讀(3485)
評(píng)論(0) 編輯 收藏 引用 所屬分類(lèi):
Windows 8