参考文献:
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 白皮书:
http://www.microsoft.com/en-us/download/details.aspx?id=27411在使用锁屏应用的时候,你要保证你明白自己要做些什么准备:
1. 使用Wide Logo,即你要提供wide logo 310*150
2. 使用Badge, 即你要提供badge logo 24*24
3. 使用Background Task, 而且background task 支持的事件是Control channel, timer, push notification类型。
4. 设置你的Background Task的EntryPoint
5. 你需要在Manifest里面声明你使用LockScreen。
具体的操作步骤大家可以参考文献2。
锁屏应用比较简单,重要的一点是,如果你在程序中询问了用户是否允许使用锁屏应用,你将只有这一次修改的机会,不然你就只能通过PC Control进行设置了。但是如果你的Windows 8 没有激活的话,你只能卸载应用程序,重新安装一遍了。
代码如下:
1 //要先请求允许,如果允许的话,你才能更新Badge,如果没有这一步,Badge将 不会显示在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 });
运行,然后会弹出一个对话框,像这样
点击Allow之后,按下WIN+L你就可以看到锁屏上面出现的Badge图片和你的number了。
一般情况下,我们会使用后台任务(Background Task)来更新Lock Screen App的数据。
后台任务
1. 什么是后台任务? 后台任务是:即使程序已经被挂起或者不在运行了,还在默默地执行的任务。
2.
后台任务与前台任务的区别? 首先,前台任务占据了整个屏幕,用户直接与其进行交互。后台任务不能与用户交互,除了(Tile, Toast, 和Lock Screen)
其次,因为前台要与用户交互,它使用所有可用的系统资源,包括CPU time 和Network资源,并且不受限。后台任务使用系统资源的时候是受限制的。
再次,前台任务处理主要事务,后台任务处理短时间、轻量级的事务。
最后,后台任务不论前台任务是否处于Running状态,它都会运行。
3. 后台任务的目的? 大家都知道,Windows 8 应用程序的生命周期分为Running,Suspended,Terminated三种状态。App处于前台时,为Running状态,处于后台时,为Suspended状态,用户关闭App时或者在Suspended状态太久,系统自动关闭App时,为Terminated状态。
我们可以从Suspended状态将App变为Running状态,我们不能在后台运行太复杂,太耗时耗资源的程序,因为如果你这么做了,你将会非常耗费电量,并且,用户切换回前台时,会感觉到非常卡,有延迟。
因为上述的目的:延长电池用量,保证用户流畅的体验,我们需要限制后台任务对资源的使用,而且我们的后台任务要尽量精简。
4. 后台任务的执行环境? 一般情况下,我们都要把我们的后台任务作为一个Runtime Component,引用到主工程中去。这样,一个后台任务就是一个class library,一个in-proc server DLL。这个library可以在我们的App中运行,也可以在系统提供的主机环境中运行(BackgroundTaskHost.exe)。这个exe是在App相同的容器内运行,当它不需要的时候,会自动退出。
5. 后台任务的适合场景?
播放音乐,上传下载文件,刷新瓷贴、通知、LockScreen,应用程序间共享合约。下载Mail,VOIP、IM信息,用户改变系统设置
6. 后台任务基本概念?
Background task 一个实现了IBackgroundTask接口的类
A class or JavaScript page implemented by the app to provide functionality even if the app is not in the foreground.
Background trigger 一系列事件,每个后台任务都需要至少一个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 一些必须满足的条件,可以有,也可以没有condition
A set of zero or more conditions that need to be satisfied before the background task can run.
BackgroundTaskHost.exe 一个装载后台任务的容器
A system-provided host executable to run the background task.
EntryPoint 一个实现了IBackgroundTask接口的类的名字
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. 后台任务的运行原理?
后台任务的注册,运行,调试,请参阅参考文献1,2,3.这里主要介绍一下后台任务的运行原理。大家可以参考下图进行理解
虚线两边分别表示App和System,App就是我们的应用程序,System就是负责处理后台任务的Service。
首先,我们要注册Trigger,Trigger有多种,大家可以参考文献3。
其次,在应用程序中注册后台任务(包含了什么样的Trigger可以触发这个后台任务),注册之后,在System Infra中就永久保留了这个注册信息。不论你是否关闭了应用程序还是重新启动了电脑,这个注册信息都会存在。
再次,当合适的Trigger事件来临,System Infra 会搜索与这个Trigger相匹配的后台任务
最后,启动该后台任务。
我们可以在任务管理器中找到Background Task Infrastracture Service :
右键点击,Open Service,可以看到具体的描述:
可以看到,这个服务就是控制哪个后台任务可以在系统中运行,也就是我们图中的System Infra。
8 App状态与后台任务的关系 ?
看到上面的过程,大家或许会疑问,如果Trigger事件来临,App已经关闭,BackgroundTask还会执行么?答案是:一定会,不论你的App是Running还是Suspended或者是Terminated状态。
但是,还记得我们说过的,我们的后台任务可以运行于App中,也可以运行于系统提供的环境中么?
如果运行于系统提供的环境中,那么,答案同上,而且App保持原来的状态。例如:App是Terminate状态,后台任务运行,App不会启动,依旧是Terminate状态。
如果运行于App中,那么答案同上,但是App的状态会略有不同,这种不同只存在Terminate状态,如果App是Terminate状态的话,Trigger来临,App会被启动,但是其UI线程不会被启动,后台任务启动。也就是说App虽然启动了,但是不会将App带回前台。
也就是说,无论如何,当Trigger事件被触发,后台任务的启动是由系统来决定的,无论如何后台任务都会执行,App的状态在上述两种情况下会略有不同。
另外个人观点:App只是提供了一个注册后台任务的平台,注册了之后,后台任务的控制权就交由系统管理了。
9. 后台任务的执行环境 ?
之前我们已经提到,后台任务可以在App中运行,也可以在系统提供的主机中运行。如果没有特别声明,后台任务是默认在系统提供的主机中运行的,这样做有一些好处:
首先,它的启动同应用程序的状态无关;
其次,启动后台任务更快;
再次,使用的资源更少;
最后,比在App中启动拥有更高的性能。
如何控制后台任务在哪个容器下运行呢?这跟Trigger的类型相关:
不同的后台trigger对于在什么地方运行有不同的限制。对于默认的,没有指定Executable属性的时候,后台任务是运行在系统提供的主机中的。App是不能指定Executable属性的,如果它必须要在系统提供的主机中与性的。只有那些包含PushNotificationTrigger或者ControlChannelTrigger任务类型的后台任务才能指定Executable属性。
10. 后台任务注册的持久性?
只要你注册了一个后台任务,你就可以永久地拥有它,不论你的App是什么状态,也不论你的电脑是否关闭过,甚至,不论你的应用程序有没有更新过。也就是说后台任务注册的持久性可以跨越App的更新。
要实现这点必须要注意:你必须要保证你的EntryPoint的一致性,即在新的版本中,同样的EntryPoint一定要存在。如果不存在了,那么在后台任务执行中将会出现错误。另外,如果我们的新版本中已经不再相应某trigger的后台任务了,怎么办?新版本的App可以注册一个ServicingComplete Trigger的后台任务,用来提醒当App更新之后,一个未被注册的后台任务是失效的。
注意,一旦App被卸载,所有的后台任务将不复存在。
11. 后台任务实现中的一些知识点?
后台任务至少应该设置一个Trigger event。
后台任务可以没有condition,也可以有多个conditions。condition拥有门闩行为,意思是,必须所有的condition都满足,才能启动后台任务,而不管Trigger事件有没有被触发。这种行为像是锁住了trigger,等到conditions都满足了才launch 后台任务。
后台任务可以向前台报告其进度和完成情况:
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 }
但是需要注意的是,每次启动的时候都要将这两个事件关联到后台任务上,因为这两个事件不是持久性的,当App被关闭的时候,他们也会被销毁。
可以取消后台任务:
void SampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
{
taskInstance->Canceled += ref new BackgroundTaskCanceledEventHandler(this, &SampleBackgroundTask::OnCanceled);
//
}
void SampleBackgroundTask::OnCanceled(IBackgroundTaskInstance^ taskInstance, BackgroundTaskCancellationReason reason)
{
CancelRequested = true;
}
注意,取消事件和上述两个事件是在不同的类型对象中关联的。
12. 后台任务的资源管理?
如果App处于前台,用户正在与App进行交互的时候,那么后台任务没有资源限制。如果一个App没有在前台时,限制就出现了。
文章的开头已经介绍了后台任务的执行环境是资源受限制的环境,那么哪些资源是受限制的呢?
首先,CPU time,一个Lock Screen App 每15分钟会获得2S的CPU时间来处理所有的后台任务,一个非Lock Screen App每2小时获得1S的CPU事件来执行后台任务。这2S是15分钟的最后2S。如果App使用了所有的CPU事件,那么后台任务将会等待下一个15分钟,如果有分配的事件,那么将会执行。
其次,NetWork。如果App处于AC power模式,那么后台任务没有网络资源的限制。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的数据,每天下载45MB,在10Mbps带宽下每15分钟4.69MB,每天450MB
非LockScreen App 在1Mbps带宽下,每2小时可以下载0.625MB数据,每天下载7.5MB,在10Mbps带宽下每2小时6.25MB,每天75MB
13. 后台任务执行于App中的线程模型?去耦
对于一个非JavaScript App,后台任务位于in-proc DLL中在MTA中加载。一个真正的后台任务类可以是STA 或者 MAT线程类型的。因为后台任务可以在AppSuspended或者Terminated时运行,他们需要同前台App解耦。在一个分离的单元中加载后台任务的DLL将强制将后台任务从App中分离。
当一个App处于Suspended状态时,UI STA线程被Windows kernel阻塞。这个线程只有当App重新回到Runnning状态才被release。当app在后台,并且后台任务被触发,App中的所有线程都是非冻结的,除了UI STA线程,并且后台任务被激活于MTA线程中。UI STA持续被锁住。如果后台任务尝试着访问位于UI STA 线程中的对象是,将会出现死锁。为了避免这种情况,后台任务不能同App共享对象。任何共享的对象都要聚合FTM(Free Threaded Marshaler)。Control Channel trigger描述了这种应用。
在前台任务和后台任务之间共享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. 使用后台任务的建议?
⦁ 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 其他:编程需要注意的细节:?
如果你的后台任务运行任何的异步程序,那么你需要获得一个defferral。如果没有这个deferral,Run方法已经结束,异步方法还没有结束的情况下后台任务将会不正常结束。
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
查看后台任务有没有注册,没有的话注册一个:
for each (auto task in BackgroundTaskRegistration::AllTasks)//这里保存了本地的所有的注册了的后台任务。
{
if(task->Value->Name == "Class1")
{
isRegistered = true;
break;
}
}
if(!isRegistered)
{
RegisterBackgroundTask("TileUpdater", "BackgroundTasks.Class1");
}
这里使用foreach方法比较简单。
注册一个后台任务
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 }
我觉得这么总结一下,后台任务的原理神马的,大家应该稍微清楚一些了。关于后台任务,我也看了很久,之前都是糊里糊涂的,今天终于算整明白的,希望对大家有用。欢迎交流。
posted on 2013-01-08 13:00
Dino-Tech 阅读(3458)
评论(0) 编辑 收藏 引用 所属分类:
Windows 8