[翻译]WP7 QuickStart-第十一篇-在后台运行程序(墓碑效应)
【译者注:这篇文章是翻译自微软官方的WP7 QuickStart的第十一篇,讲述WP下的程序的墓碑效应。部分内容加入了自己的理解和表达习惯。而翻译此系列的主要目的一是为了练习英语,二是让自己作为一个 BI开发者对WP7的开发有一个了解。部分翻译不当的地方望各位高人指出批评指正】
【译者注:Tombstoning,早先在电路行业有使用,译为墓碑效应,即小型片状(如电阻器或电容器的)表面贴装器件,因其两端的金属封头与板面焊盘之间在可焊性或焊锡力量上可能有差异存在,经过红外线或热风熔焊后,偶尔会出现一端焊牢而另一端被拉起的浮开现象,称为墓碑效应或吊桥效应(drawbridging effect) 、曼哈顿效应(Manhattan effect) 等。
这里为了避开我们语言中比较忌讳的译法,所以遵从这种翻译方式。】
Windows Phone操作系统一次只允许一个应用程序运行。当用户从一个程序导航到另个一个程序时,操作系统会终止程序。为了提供给用户更好的体验,操作系统提供了当程序被重新激活时帮助你恢复程序到之前的展现状态。此篇将描述墓碑效应的过程,应用程序生命周期以及如何恢复状态。
其中主要包含以下部分:
墓碑效应概览
应用程序生命周期
应用程序状态
页面状态
最佳实践
墓碑效应概览
Windows Phone操作系统不允许任何第三方程序在后台运行,也就是说,一次只能运行一个程序。这样做的原因是保持电池的使用时间并且确保用户界面的连贯和快速响应。【译者注:关于这点,你怎么理解都可以,但相信微软下一个版本会解决这个问题】当用户从程序导航到另外一个程序,Windows Phone反激活程序,这就是墓碑效用。它是一个操作系统当用户导航到其它程序时反激活程序的一个过程。在下列情况下程序会被反激活:
1. 有电话呼入。
2. 电话进入休眠状态。
3. 用户按到了开始或者搜索按钮,或者收到了一个通知。
4. 程序调用了一个外部的任务。
当墓碑效应中的程序重新激活,它必须看上去跟墓碑效应之前的一模一样,这样会给用户一个比较好的体验。理想状况下,用户不应该感觉到这是一个从墓碑效应中重新被激活的一个全新的过程。比如,设想你打开了一个很长的页面,并且已经向下滚动阅读了很多的内容,然后你导航到了另外一个页面,当你从这个页面返回的时候,先前滚动到的位置消失,而且回到了页面的顶端,这是多么闹心的事。另一方面,当你在填写一个内容很多的表单,你明显不希望看到你遭遇了一个小错误而所有的填写的内容全部丢失。
为了解决这种情况,当程序被反激活后,操作系统维护了一些关于程序在内存中的状态信息,用这个信息就可以在程序被重新激活的时候被恢复呈现状态。
Windows Phone提供了一些方法和事件来恢复程序和页面状态信息。此篇主要描述这些方法和事件。
应用程序生命周期
因为Windows Phone程序可以被墓碑效应化,所以你需要理解一下程序的生存周期。操作系统提供了四个事件代表程序在生命周期中的状态。在程序中可以采取适当的操作,这些事件是Launching,Closing,Deactivated和Activated,下图显示这四个事件的发生。
Launching-开始运行
当程序从开始屏幕被启动后,就可以说其被(Launched)执行了。不管用户什么时候运行的程序,一个新的实例都将被创建。当程序被启动之后,Launching事件就被出发了。下面的代码是App.xaml.cs文件的Launching事件部分。
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
你可以在这个事件中增量地保存一些设置以及其它持久化的数据来降低当程序的状态更改时需要保存的数据量。这是可选的,假如程序的持久化信息比较少,这个是不必要的。
Closing-关闭
单击设备的后退按钮后,导航模型允许用户返回,或者导航到程序的前一个页面,甚至跨程序回退。然而,一旦用户进入到程序的第一个页面,按后退按钮会触发Closing事件并且终止程序。可以在这个事件里保存持久化数据到独立村粗中。下面是App.xaml.cs中的Closing事件代码部分。
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
反激活
当另一个程序在前端运行的时候,当前的程序可以被反激活(非关闭),比如有来电呼入,或者用户点击了开始按钮。在这种情况下,Deactivated事件就被触发了。下面的代码是App.xaml.cs文件中Deactivated事件部分。
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
不同于程序的关闭,反激活会使程序进入到墓碑效应阶段。也就是说虽然不在运行了,但是操作系统保留了程序的一个记录,促出程序的一系列状态信息。用户很有可能会返回到墓碑效应阶段的程序,也就是重新激活。所以,你需要存在Deactivated事件处理代码中用PhoneApplicationService.State属性储程序当前的信息。存储的数据是短期的状态数据,也就是说,是会帮助程序重新建立在其被反激活之前的状态。由于墓碑效应化的程序也不一定会被重新激活,所以你应该也将程序的持久信息保存到独立存储中。所有的这些在Deactivated事件中的操作必须在10秒之内完成,否则操作系统会终止程序。所以当你的程序有大量的持久化数据的时候,最好是在程序运行的时候就保存些增量。
程序不被墓碑效应而反激活也是有可能的,比如用户按开始按钮然后又快速连续地按了后退按钮。因为这个原因,最好不在Deactivated事件处理代码中做任何销毁性的操作。程序会在Deactivated事件处理完毕后,不调用Activated而继续运行。
Activated
当程序被反激活和墓碑效应化之后,用户可以通过单击后退按钮返回到程序。当用户回到墓碑效应化的程序后,程序会被重新激活并且触发Activated事件。可以用这个事件从PhoneApplicationService.State属性中读取在反激活时用户界面的值。下面代码是App.xaml.cs中Activated事件部分。
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
应用程序状态
应用程序状态是程序的状态而不是某个特定页面的状态。如果你用到了全局数据,比如登录信息或者程序的设置信息,是在程序中很多页面中都被用到的。这着被认为是应用程序部分的状态信息。当程序从墓碑效应中被重新激活后,如果你不特意存一下的话,这些数据会丢失。通常使用Launching,Closing,Deactivated和Activated事件来初始化和维护应用程序状态。当程序被墓碑效应化后,操作系统将内容通过PhoneApplicationService.State属性保存。可以存储应用程序级别的状态信息在Deactivated事件的PhoneApplicationService.State属性中,然后在Activated事件中通过PhoneApplicationService.State处理数据。
如果程序有持久化数据,可以考虑将数据存入到独立存储中。独立存储是一个独立沙箱,位于设备上的文件存储位置,类似于桌面文件系统。数据存储到独立存储中会话费一些时间来读取,所以你应该仅把需要长时间存储到设备中的数据和不可被序列化的对象。此外,需要仔细考虑在哪里使用独立存储代码以最小化延迟的压缩。比如,程序有很多数据需要存储,在程序退出时应该避免一次存入大量数据,而是以增量的方式存入。通常,应该在其可用前尽快或者尽早地将其存入独立存储区域。
页面状态
页面状态是程序中页面的视觉状态。当用户按后退按钮返回到已经被墓碑效应的程序时,Windows Phone操作系统会自动显示程序中用户上次浏览的页面。然而,需要确保页面的状态被保存或恢复到用户导航到其它程序之前的样子。页面状态包括比如文本框的文本以及滚动条的位置。这部分主要描述如何保存和恢复页面状态以提供更好的用户体验。
保存和恢复页面信息,会用到页面的构造函数,OnVavigatedTo方法和OnVavigatedFrom方法。页面的构造函数当页面第一次被导航到的时候调用。OnNavigatedTo在每次程序或者页面完成加载并且可用时被调用。OnNavigatedFrom在程序和页面还没有被反加载时从当前页面导航到另外的页面的时候被调用。你可以在页面的构造函数里初始化页面级的信息,并且在OnNavigated方法中恢复页面状态,在OnNavigatedFrom中存储页面的状态信息。
保存和恢复页面信息,通常要遵循如下几步:
1. 填加一个布尔类型的变量到页面(比如newPageInstance),设置成false。这个变量将用来判定UI状态是否需要恢复。
2. 在页面的构造函数中,设置这个页面级的变量为true。页面的构造函数会在页面在程序实例中第一次被创建或者页面从程序的墓碑效应化中被重新激活时被调用。页面的构造函数不会在同一个程序中点击后退的时候调用,因为这个页已经在内存中存在了。
3. 重写并且实现OnNavigatedTo方法。在这个方法中,从PhoneApplicationPage.State属性恢复页面级状态数据。
4. 重写并且实现OnNavigatedFrom方法。在这个方法中,存储恢复页面级状态数据到PhoneApplicationPage.State属性中。
下面的示例包含两个页面,Home和Second页面。当单击Home页面的Go to the Second Page按钮的时候,程序导航到Second页面。第二个页面有一个文本框,两个单选按钮和一个复选框。如果在Second页面的这些字段中输入一些数据然后按下开始按钮退出程序,程序进入到墓碑效应状态。此时通过后退按钮导航回到Second页面,可以看到先前输入的数据已经恢复到界面上了。
下图显示的是不包含页面数据恢复代码的效果。
在Seond页面中加入下面的代码实现页面状态的保存和恢复。
C#
public partial class SecondPage : PhoneApplicationPage
{
bool newPageInstance = false;
public SecondPage()
{
InitializeComponent();
newPageInstance = true;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (State.ContainsKey("textbox") && newPageInstance == true)
{
MyTB.Text = (string)State["textbox"];
MyRB1.IsChecked = (bool?)State["radiobutton1"];
MyRB2.IsChecked = (bool?)State["radiobutton2"];
MyCB.IsChecked = (bool?)State["checkbox"];
}
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
newPageInstance = false;
State["textbox"] = MyTB.Text;
State["radiobutton1"] = MyRB1.IsChecked;
State["radiobutton2"] = MyRB2.IsChecked;
State["checkbox"] = MyCB.IsChecked;
base.OnNavigatedFrom(e);
}
}
最佳实践
下面是在开发Windows Phone程序时可以遵循的一些最佳实践:
1. 当程序无论从Tile或者应用程序列表中运行的时候,页面都应该被定位到一直的根体验节点【译者注:初始页?】。应该让用户很明确是在体验一个程序的新实例。
2. 当用户从墓碑状态中激活时,用户所看到的界面应该和界面被反激活时所看到的界面一致,而不应该使用户明显感觉到程序是被终止并且重新启动的。
3. 当程序被墓碑化后,用户也许不会直接返回到程序。这样,就需要在Deactivated事件处理和Closing事件处理中将持久化状态存入到独立存储中。为了避免代码重复,可以创建一个单独的方法来保存这些信息到独立存储然后在这两个事件处理代码中对其进行调用。
4. 在Deactivated事件处理代码中的所有操作应都在10秒内完成。超过10秒的会被操作系统强行终止,并且通过后退按钮也是无法返回的。所以建议在开发程序的时候关注较简洁的窗体。如果程序确实需要超过几秒才能存储完成的信息,那么最好是在程序运行的时候就把他们增量地存入到独立存储区域中。
来自博客园的aspnetx宋卫东