内容预告:
Windows Phone应用程序在不同的状态间过渡的图示如下:
程序从点击开始屏幕的图标上启动,用户可以关闭程序,系统可能挂起你的程序(在程序失去焦点的时候),挂起的程序可能会进入墓碑,程序可能从挂起状态激活。
当用户启动一个新的程序的实例时,之前的挂起状态会丢失。比如当运行一个程序时,点到了Home键,再点击开始屏幕的图标上启动程序,按Home键之前挂起状态会丢失,正确的做法是按住Back键不动选择那个程序恢复状态。
在Windows Phone 8中,可以用快速恢复功能(Fast Application Resume)重新启动挂起的程序。
应用程序的生命周期事件:
private void Application_Launching(object sender, LaunchingEventArgs e) { }
Windows Phone应用程序环境会通过一些事件通知上述状态,在项目模板里的App.xaml里订阅了事件,并在App.xaml.cs处理了,初使化情况下处理逻辑是空的。
启动和关闭:Launching 和 Closing
当程序启动时,Applcation_Launching会被调用,程序结束时,Application_Closing会被调用,调试器在程序停止后还会继续运行,所以需要手动结束。
程序的Deactivation和Reactivation:
出于省电的考虑,任何时间只有一个程序运行在前台,用户可以deactivate程序也可以reactivate它们,程序需要处理Activated和Deactivated事件。
程序的休眠(Dormant):
用户可以随时休眠应用程序,然后运行其他程序,这个时候Application_Deactivated函数被调用,电话突然打来时,程序也会休眠,锁屏时程序也会休眠一段时间。用户也可以恢复休眠的程序。但是不保证一定能从休眠状态中恢复。
从休眠中重新激活:
处理休眠:
当程序被休眠时必须尽可能地保存数据因为程序可能会关闭,如果用户不再通过长按Back回到那个程序,Application_Deactivated就相当于Application_Closing了。
你的程序有5秒钟的机会清理现场(保存数据),之后程序会被从内存中清除掉。当程序长按Back恢复时,它会自动恢复到Deactivated时的那个页面,这是操作系统帮我们做的,但是,页面的内容并不会自动保存。
从休眠到墓碑:
一个程序会和其他程序一起在内存里休眠,如果操作系统的内存不够用了会释放最先休眠的程序的缓存状态,这个过程叫做“墓碑化”。
页面导航历史和缓存状态都被墓碑了的程序维护着。
当一个休眠了的程序恢复时,缓存状态会重新加载,程序会万利到它离开之前的那个页面。
当一个墓碑了的程序恢复时,它会重启离开之前的页面,但是所有的程序状态会丢失,你需要重新加载。
一个程序可以决定从哪个状态激活。
从休眠还是墓碑恢复的?可以在恢复前做一个判断
private void Application_Activated(object sender, ActivatedEventArgs e) { if (e.IsApplicationInstancePreserved) { // Dormant - objects in memory intact } else { // Tombstoned - need to reload } }
状态和墓碑:
当程序从休眠恢复时,程序会准确地恢复到离开时的页面,所有的对象和它们的状态都在内存里,你可能需要写一些逻辑来重置依赖于时间或网络的调用代码。
当程序从墓碑恢复时,程序只会恢复到离开时的页面,但所有对象和它们的状态都丢失了,所以需要重新加载控件的数据,这就是为什么需要保存状态,程序从内存中移除了系统也维护着状态。
当程序的一个新的实例启动时,状态是空的。如果一个先前的程序挂起了,那么那个程序存储的状态字典会丢失。
状态字典:
PhoneApplicationService.Current.State["Url"] = "www.robmiles.com";
休眠程序的状态信息存在一个状态字典里,如上述代码。
可以在Application_Deactivated函数里存储,然后在页面激活时读取。
所以Application_Deactivated有两件事情要做,保存数据以防程序不能重新激活,保存状态数据以保证程序恢复到正确的状态。
调试墓碑状态:
当休眠的时候,可以在Visual Studio里设置强制程序墓碑化(从内存中移除)。
你应该把这个做为测试引擎的一部分。
你也可以用模拟器操作锁屏,也可以让程序进入休眠。
模拟器操作面板:
模拟器可以模拟锁屏和解锁,会让程序进入Deactivated和Activated状态。
空闲处理:
Windows Phone操作系统会检测到程序的空闲状态,当手机进入空闲状态,会让手机锁屏。
用户可以配置多长时间后开始检测,也可以关闭检测。
当程序被检测到是空闲的话,将会进行Deactivated状态,当用户解锁后,手机会Activated。
程序可以禁止检测空闲,那样就可以在锁屏下运行了。
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
这样即使手机锁屏了也会有什么Activated和Deactivated了。
但如果是用户按了Start键还是会deactivated。
禁止空闲检测也不能同时运行两个程序。
检测遮挡事件:比如toast消息,锁屏,来电等。
App.RootFrame.Obscured += RootFrame_Obscured; ... void RootFrame_Obscured(object sender, ObscuredEventArgs e) { }
程序可以订阅遮挡事件,也可以订阅遮挡移除的事件。
导航和后退栈:
Windows Phone程序的导航模型使用起来很方便,可以通过链接到其他页面,可以通过后退键到上一个页面。
系统维护了一个后退的栈,当跳转到另一个页面时,之前的页面地址会被压入栈,当后退时,当前页面会出栈。
后退栈和Activated/Deactivated:
当程序deactivated时,系统会保持后退栈,包括当前的页面,但只有页面地址被保存,内容并不保存。
程序必须在OnNavigatedTo和OnNavigatedFrom事件里构建页面和保存数据。
从不同的地方进入程序:
一般情况下程序会在运行时显示首页,而且这个首页会被压入栈,且后退时会回到首页。
有些情况下可以不用进入首页,比如(第二磁贴,提醒,文件关联,搜索,钱包,语音),但同时带来新的问题
后退栈的问题:
有这样一个场景,当用户在后退栈里忆压入很多页面时,进入相册选择了相片,然后在相册界面按后退键,会首先回到程序进入相册前的最后一个页面,再后退,程序会退出。
这里需要特殊处理后退栈,比如模拟后退栈,因为在页面从后退栈清除时会有一个事件触发。
模拟后退栈:
private void PurgeBackStackButton_Click(object sender, RoutedEventArgs e) { while (NavigationService.CanGoBack) NavigationService.RemoveBackEntry(); }
程序可以质问后退栈是否可以后退,也可以把栈内所有页面清除光。
程序可以枚举后退栈的所有页面,但不能将页面压栈。
OnRemovedFromJournal:
protected override void OnRemovedFromJournal(JournalEntryRemovedEventArgs e) { base.OnRemovedFromJournal(e); }
在程序运动时,一个页面希望能够从子页面返回,但如果它从后退栈移除后就不是这个流程了。
可以用OnRemovedFromJournal事件捕获当本页面从后退栈里移除时做一些处理。
快速恢复FAR(Fast Application Resume):这个功能可以让程序在激活时速度更快。需要手动在WMAppManifest.xml里编辑
<Tasks> <DefaultTask Name ="_default" NavigationPage="MainPage.xaml"> <BackgroundExecution> <ExecutionType Name="LocationTracking" /> </BackgroundExecution> </DefaultTask> </Tasks>
FAR的两种下场:
通过把程序注册为LocationTracking可以实现FAR,但LocationTracking我们未必会用。
如果程序中用到了定位的类,那么当程序deactivated时会继续一直在后台运行,如果重新运行程序,程序会快速从当前休眠的状态恢复。
如果程序中没用定位的类,那么那么当程序deactivated时,程序像平常一样,如果重新运行程序,程序会从之前休眠的状态恢复。
标准的重新启动程序如上图所示:
真正的有后台定位的行为如上图所示:
后台无定位程序在运行的FAR程序如上图所示:
为什么不在所有程序上用FAR?
因为用的时候要非常小心页面导航的用户体验,
非FAR的程序中,如果启动程序到page2,那么page2是后退栈里唯一的页面,按后退会退出程序。
在FAR的程序中,如果启动程序到page2,但是之前挂起的程序的后退栈里有mainpage,page1,page2,page3,那么当运行新程序实例时,按后退键的话,程序会以mainpgae,pgae1,page2,page3,page2的程序退出。
FAR程序中主磁贴的行为:
用户在点击程序的磁贴后,程序会进入主页面,非FAR的程序会这样,之前没有挂起实例的FAR的程序也会这样。
如果是恢复到程序呢?应该是怎么样?恢复到首页还是最后一个页面?如果恢复到最后一页那怎么进入首页?这里有一个建议是在页面上加一个回到首页的链接。
检测FAR程序的恢复行为:
在真正的有后台运行定位的程序中,当程序被送到后台后会继续运行,程序会触发PhoneApplicationService.RunningInBackground事件而不是Application_Deactivated事件。
当FAR程序重新启动时,会以NavigationMode.Reset的方式导航到后退栈最顶部的页面,然后会以NavigationMode.New的方式定位到新启动的URL的页面,可以决定是否上传页面的后退栈,或取消导航到新页面。
清除FAR程序之前的实例运行的页面:在App.xaml.cs里加入
private void InitializePhoneApplication()
{
...
// Handle reset requests for clearing the backstack
RootFrame.Navigated += CheckForResetNavigation;
...
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
}
private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again
RootFrame.Navigated -= ClearBackStackAfterReset;
// Only clear the stack for 'new' (forward) and 'refresh' navigations
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;
// For UI consistency, clear the entire page stack
while (RootFrame.RemoveBackEntry() != null) { } // do nothing
}