首页 > 代码库 > 【WP8.1开发】认识后台任务

【WP8.1开发】认识后台任务

在手机上,使用后台,不像电脑上那么随意,准确地讲嘛,在移动平台上,后台任务都有严格的限制。至于说为什么会有这么多限制,我估计初衷很明显——保证系统的性能不受某个或某几个应用的负面影响;另外就是出于安全性考虑。

毕竟手机设备不同于电脑,一旦后台程序泛滥成灾,是很难进行管理的,要防止这些不可预知的事件,只能从源头上杜绝。因此,当初在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

 

【WP8.1开发】认识后台任务