posts - 70, comments - 142, trackbacks - 0, articles - 0
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

06,Windows Phone 8程序的生命周期

Posted on 2012-12-12 17:35  淡如水wp  阅读(...)  评论(... 编辑 收藏

内容预告:

  • 启动(Launching)和关闭(Closing)
  • Deactviating和Activating
  • Dormant和Tombstoned
  • 用模拟器模拟这些事件
  • 空闲检测
  • 快速恢复
  • 生命周期规划
  • 页面导航和后退栈

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  
 }