代码改变世界

Windows Phone Background Agent杂谈

2012-05-18 09:54 by Windie Chai, ... 阅读, ... 评论, 收藏, 编辑

Windows Phone从Mango开始开放了Background Agent,使得我们可以实现后台运行的任务。出于兴趣,我在第一时间使用这套API开发了一个应用——Human Calendar。随着时间的推移、功能的增加,Human Calendar越来越依赖于Background Agent,也逐渐遇到了更多让人头疼的问题。经过许多摸索和撞墙后,绝大多数问题都解决了,Human Calendar目前运行良好。所以我就用这篇文章来记录一下开发Background Agent过程中的一些事情,但我不会完整地介绍一个Background Agent的开发过程,你大可将本文看成一篇“吐槽”文。

 

限制

在开发Background Agent之前,必须要考虑到它的限制,因为这些限制极其严格,对于定期代理(PeriodicTask)来说,限制了:

  1. 有一些API不能使用,并不是说你不调用就可以了,只要你在同一个程序集里使用了这些API,就不会通过验证;
  2. 内存占用不能超过6MB,否则立即终止;
  3. 生效时间最长只有14天,过期后就需要重新计划。Human Calendar和一些天气应用都属于那种只要有Tile就可以一辈子不去打开的应用,但是由于这个限制,我还得想办法提醒用户:“亲,记住两周内重新打开应用一次哦,不然Tile就没法更新了哦”;
  4. 连续两次崩溃之后会被禁用,做好准备捕捉一切异常吧;
  5. 每30分钟运行一次,每次最多执行25秒,超时后立即终止,所以如果在后台任务里下载文件的话,要特别注意文件的大小;
  6. 节电模式会阻止执行;
  7. 手机中的后台任务数量是有上限的,最少为6个,当手机中已启用的后台任务达到上限后,就无法再启用新的后台任务了,这时候你还得提醒用户:“亲,到这里的这里的这里看看后台任务是不是超过6个了,是的话,挑一个禁用了,然后回来再试试”……

 

资源密集型代理

除了定期代理之外,Windows Phone还支持另外一种后台任务——资源密集型代理(ResourceIntensiveTask),不过我们通常都不会用到它,因为它简直就是个杯具,它的限制不仅多,而且都很变态,除了拥有定期代理的前4挑限制之外,它还限制了:

  • 电池电量不得低于90%,而且需要连接外部电源;
  • 需要非手机网络连接,连着WIFI或PC吧,亲;
  • 屏幕必须锁定,也不能接打电话,否则不执行;
  • 最多执行10分钟,超时立即终止;

想像一下,假如有个应用使用了资源密集型代理,它该怎么向用户解释?

“亲,想要使用我们的XXX功能,您得先充满电,再连着WIFI,锁住屏幕,安静的等待10分钟,期间千万别拔开电源线,也别解锁屏幕和接打电话哦。”

用户“哦”了一声,然后随手卸载了应用。

 

调试

可以使用ScheduledActionService.LaunchForTest方法来随时随地执行后台任务,籍此来进行调试。此方法在Debug和Release模式下均可执行,但在用户下载的应用中是无效的,所以不要妄想用它来突破Background Agent的时间限制。

虽然在Visual Studio中进行调试非常方便,但不可过于相信调试器。简单来说,附加了调试器的Background Agent就像被提升了权限一样,可以执行一些正常状态下不能执行的任务,譬如可以调用BitmapImage.SetSource方法,而在没有附加调试器的后台任务中调用这个方法是会抛出异常的(可以使用WriteableBitmapImage来代替)。

所以很有必要在不附加调试器的情况下检查Background Agent的运行情况。

这时就可以利用ScheduledActionService.LaunchForTest来将Background Agent安排在一段时间后执行,然后退出应用,关闭调试器(早期的SDK会在退出应用时自动关闭调试器),观察效果。

假如发现后台任务出现了问题,该怎么定位问题?有两种特别原始的方法:第一种是在执行后台任务的的同时写日志;第二种是Toast通知(我想起了史前程序员使用alert调试JavaScript的故事)。

由于第4条限制,所以很有必要勤快的使用try-catch,但比较不幸的是并不是所有异常都会被捕捉到,所有因为超出限制而导致的终止行为都无法捕捉到,而且在某些情况下,一些看起来很正常的异常也会跳过catch直接杀死后台任务。

所以日志和Toast通知就相当重要,你可以根据最后一条消息的位置来推断代码执行到了何处,从而找到出问题的代码,然后再研究为什么会出问题。

这里还有一个插曲,在早期的SDK中有这样一个问题,如果附加了调试器,Background Agent就永远不会执行,于是我眼睁睁的盯着模拟器浪费了一个大好的下午,人生苦短啊,还好这个问题现在已经修复了。

 

检测时间和内存占用

Background Agent在时间上有许多限制,对于第3条限制,我们别无他法,因为添加后台任务的API属于受限API,不能在Background Agent中调用,所以只能提醒用户每14天内至少要打开一次应用,然后在每次打开应用的时候自动重新启用后台任务(这样它就又能活14天了)。

对于第2条和第5条限制,我们只能想办法控制执行时间,尽量把任务细化成小任务,让每个小任务都有自己的唯一标识和完成状态,并且可以被持久化,然后用递归或者遍历的方式依次执行这些小任务,每次执行时都要检查当前已执行的时间和已占用的内存,如果发现接近某个临界值,就NotifyComplete,然后期待半小时后继续。

临界值可以根据第2条和第5条限制以及小任务们的平均执行时间和内存占用来设定,留出一定的空档以防不测。

此外,如果应用中使用了本地数据库,最好不要在后台任务中初始化甚至使用,它会带来极大的内存消耗。就拿Human Calendar来说,用IsolatedStorageSettings取代了本地数据库之后,内存竟然节省了一半(而且这个数据库还无比简单,只有一张表、五个列和七八条数据)。

 

资源

应用:Human Calendar,我承认这个应用很宅,不过还是欢迎捧场……

MSDN:《Windows Phone的后台代理》,写本文时候发现这篇文章在年初还更新了一次,增加了不少内容,MSDN上的Windows Phone文章更新速度还是蛮快的。

最后吐槽一次,MSDN上把Reference翻译成“书评”这种事情是哪个实习生干的?