Windows Phone 7 程序生命周期

     在Windows Phone上运行的应用程序从开始到程序运行结束,其整个生命周期都是由Windows phone的执行模型所支配。执行模型被设计的目的就是为终端用户实时提供 快速的、反应灵敏的体验。为了实现这样的初衷,Windows Phone仅仅允许在前台运行一个应用程序—即与用户进行交互的可见的当前应用程序。这样做就消除了用户在其移动终端设备后台上运行多个应用程序而导致的程序间竞争有限的系统资源,而使用户的移动终端设备处于较低的性能和电池电量极具减少的可能性。

     Windows phone执行模型还维护着用户使用应用程序和查看页面所产生 的日志。利用该日志就能定义电话后退键所遵循的后退路径,从而提供给用户一个能够回退到不同应用程序和浏览页面的后退选项。 这个功能在用户使用一个应用程序访问某一站点或者其他电话服务时是非常有用的,用户能够在任何时候轻易地通过点击回退键而返回到之前的体验当中。

     正如上面提到的,仅允许在前台运行一个应用程序并且在后台不允许其他第三方程序运行。因此,当用户离开当前的应用程序,无论是使用图片选配,还是启动某种功能,比如打电话。Windows phone将会在有限的时间里暂停其当前应用程序。如果操作系统需要当前资源,那么正在使用中的应用程序还可能会被终止。

     当用户通过导航键功能离开某一应用程序时,操作系统执行挂起该程序的步骤 被称之为tombstoning(墓碑机制)。操作系统会维护应用程序状态信息。如果用户返回到被挂起的应用程序中,此时操作系统会继续执行被挂起应用程序的进程(或者重新启动它,这取决于该程序之前是否被终止)并传递状态数据给此应用程序。这就保证了用户能够准确无误地从之前与程序交互时的暂存挂起点继续执行应用程序的进程。

     这个实验关注的是Windows Phone 应用程序生命周期相关的tombstone(或者tombstoning)方面,主要处理tombstone及其产生的结果。

通过这个实验您将会:

  • 熟悉与Windows Phone7 应用程序生命周期的tombstoning功能相关方面的内容。
  • 理解应用程序是如何执行启动、激活、禁止以及结束事件等操作。
  • 创建一个简单的Silverlight 应用程序示例,该示例利用相关技术来保存用户离开当前应用程序时的状态,并能根据已保存的状态帮助用户恢复到原应用程序。

2011021423201774.jpg2011021423202853.jpg2011021423214549.jpg

代码

MainPage.xaml

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
<!-- LayoutRoot is the root grid where all page content is placed -->
< Grid x:Name ="LayoutRoot" Background ="Transparent" >
< Grid.Resources >
< local:DateTimeToStringConverter
x:Key ="dateTimeToStringConverter" />
</ Grid.Resources >
< Grid.RowDefinitions >
< RowDefinition Height ="Auto" />
< RowDefinition Height ="*" />
</ Grid.RowDefinitions >

<!-- TitlePanel contains the name of the application and page title -->
< StackPanel x:Name ="TitlePanel" Grid.Row ="0" Margin ="12,17,0,28" >
< TextBlock x:Name ="ApplicationTitle" Text ="TRAVEL REPORT" Style =" {StaticResource PhoneTextNormalStyle} " />
< TextBlock x:Name ="PageTitle" Text ="My Trip" Margin ="9,-7,0,0" Style =" {StaticResource PhoneTextTitle1Style} " />
</ StackPanel >

<!-- ContentPanel - place additional content here -->
< Grid x:Name ="ContentPanel" Grid.Row ="1" Margin ="12,0,12,0" >
< Grid.RowDefinitions >
< RowDefinition Height ="80" />
< RowDefinition Height ="80" />
< RowDefinition Height ="80" />
< RowDefinition />
< RowDefinition />
</ Grid.RowDefinitions >
< StackPanel Grid.Row ="0" Orientation ="Horizontal" >
< TextBlock Text ="Trip to:" Style =" {StaticResource PhoneTextLargeStyle} " VerticalAlignment ="Center" />
< TextBox x:Name ="txtDestination" Text =" {Binding Destination, Mode=TwoWay} " Width ="362" InputScope ="AddressCity" />
</ StackPanel >
< StackPanel Grid.Row ="1" Orientation ="Horizontal" >
< TextBlock Text ="First Day:" Style =" {StaticResource PhoneTextLargeStyle} " VerticalAlignment ="Center" />
< TextBox x:Name ="txtFromDate" Text =" {Binding FirstDay, Mode=TwoWay, Converter={StaticResource dateTimeToStringConverter}, ValidatesOnExceptions=True, NotifyOnValidationError=True} " Width ="330" InputScope ="Digits" BindingValidationError ="Date_BindingValidationError" />
</ StackPanel >
< StackPanel Grid.Row ="2" Orientation ="Horizontal" >
< TextBlock Text ="Last Day:" Style =" {StaticResource PhoneTextLargeStyle} " VerticalAlignment ="Center" />
< TextBox x:Name ="txtToDate" Text =" {Binding LastDay, Mode=TwoWay, Converter={StaticResource dateTimeToStringConverter}, ValidatesOnExceptions=True, NotifyOnValidationError=True} " Margin ="2,0,0,0" Width ="330" InputScope ="Digits" BindingValidationError ="Date_BindingValidationError" />
</ StackPanel >
< StackPanel Grid.Row ="3" >
< TextBlock Text ="Justification:" Style =" {StaticResource PhoneTextLargeStyle} " />
< TextBox x:Name ="txtJustification" Text =" {Binding Justification, Mode=TwoWay} " AcceptsReturn ="True" Height ="160" VerticalScrollBarVisibility ="Auto" />
</ StackPanel >
< StackPanel Grid.Row ="4" Orientation ="Horizontal" HorizontalAlignment ="Center" VerticalAlignment ="Bottom" Height ="100" >
< Button Content ="Next" x:Name ="btnNext" Width ="230" Click ="btnNext_Click" />
< Button Content ="Cancel" x:Name ="btnCancel" Width ="230" Click ="btnCancel_Click" />
</ StackPanel >
</ Grid >
</ Grid >
ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace ApplicationLifecycle
{
public partial class MainPage : PhoneApplicationPage
{
// 构造
public MainPage()
{
InitializeComponent();
}

private void Date_BindingValidationError( object sender, ValidationErrorEventArgs e)
{
e.Handled
= true ;
MessageBox.Show(
" Invalid date value.\nPlease try again " , " Invalid date value or format " , MessageBoxButton.OK);
}

private void btnNext_Click( object sender, RoutedEventArgs e)
{
// 导航到页面SecondPage.xaml
NavigationService.Navigate( new Uri( " /SecondPage.xaml " , UriKind.Relative));
}

private void btnCancel_Click( object sender, RoutedEventArgs e)
{
Utils.ClearTravelReport(((App.Current.RootVisual
as PhoneApplicationFrame).DataContext as TravelReportInfo));
}

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
base .OnBackKeyPress(e);

// 询问是否保存当前的数据
MessageBoxResult res = MessageBox.Show( " Do you want to save your work before? " , " You are exiting the application " , MessageBoxButton.OKCancel);

if (res == MessageBoxResult.OK)
Utils.SaveTravelReport((App.Current.RootVisual
as PhoneApplicationFrame).DataContext as TravelReportInfo,
" TravelReportInfo.dat " , true );
else
Utils.ClearTravelReport((App.Current.RootVisual
as PhoneApplicationFrame).DataContext as TravelReportInfo);
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// 追中导航的时间
Utils.Trace( " Navigated To MainPage " );

// 检查页面的状态是否保存
if (State.ContainsKey( " FocusedElement " ))
{
Control focusedElement
= this .FindName(State[ " FocusedElement " ] as string ) as Control;

if ( null != focusedElement)
focusedElement.Focus();
}

TravelReportInfo travelReportInfo
= ((App.Current.RootVisual as PhoneApplicationFrame).DataContext as TravelReportInfo);
if (State.ContainsKey( " txtDestination " ))
travelReportInfo.Destination
= State[ " txtDestination " ] as string ;

if (State.ContainsKey( " txtJustification " ))
travelReportInfo.Justification
= State[ " txtJustification " ] as string ;

if (State.ContainsKey( " txtToDate " ))
travelReportInfo.FirstDay
= DateTime.Parse(State[ " txtToDate " ] as string );

if (State.ContainsKey( " txtFromDate " ))
travelReportInfo.LastDay
= DateTime.Parse(State[ " txtFromDate " ] as string );

base .OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Utils.Trace(
" Navigated From MainPage " );

// 移走前一次的事件操作
if (State.ContainsKey( " FocusedElement " ))
State.Remove(
" FocusedElement " );

// 如果有焦点输入保存这个页面的状态
object obj = FocusManager.GetFocusedElement();
if ( null != obj)
{
string focusedControl = (obj as FrameworkElement).Name;
State.Add(
" FocusedElement " , focusedControl);
}

if (State.ContainsKey( " txtDestination " ))
State.Remove(
" txtDestination " );

State.Add(
" txtDestination " , txtDestination.Text);

if (State.ContainsKey( " txtJustification " ))
State.Remove(
" txtJustification " );

State.Add(
" txtJustification " , txtJustification.Text);

if (State.ContainsKey( " txtFromDate " ))
State.Remove(
" txtFromDate " );

State.Add(
" txtFromDate " , txtFromDate.Text);

if (State.ContainsKey( " txtToDate " ))
State.Remove(
" txtToDate " );

State.Add(
" txtToDate " , txtToDate.Text);

base .OnNavigatedFrom(e);
}
}
}

SecondPage.xaml

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
<!-- LayoutRoot is the root grid where all page content is placed -->
< Grid x:Name ="LayoutRoot" Background ="Transparent" >
< Grid.RowDefinitions >
< RowDefinition Height ="Auto" />
< RowDefinition Height ="*" />
</ Grid.RowDefinitions >

<!-- TitlePanel contains the name of the application and page title -->
< StackPanel x:Name ="TitlePanel" Grid.Row ="0" Margin ="12,17,0,28" >
< TextBlock x:Name ="ApplicationTitle" Text ="TRAVEL REPORT" Style =" {StaticResource PhoneTextNormalStyle} " />
< TextBlock x:Name ="PageTitle" Text ="My Trip" Margin ="9,-7,0,0" Style =" {StaticResource PhoneTextTitle1Style} " />
</ StackPanel >

<!-- ContentPanel - place additional content here -->
< Grid x:Name ="ContentPanel" Grid.Row ="1" Margin ="12,0,12,0" >
< Grid.RowDefinitions >
< RowDefinition Height ="*" />
< RowDefinition Height ="Auto" />
</ Grid.RowDefinitions >
< StackPanel VerticalAlignment ="Top" >
< TextBlock Text ="Report Summary:" Style =" {StaticResource PhoneTextLargeStyle} " />
< TextBox x:Name ="txtSummary" Text =" {Binding Summary, Mode=TwoWay} " AcceptsReturn ="True" Height ="460" VerticalScrollBarVisibility ="Auto" />
</ StackPanel >
< StackPanel Grid.Row ="1" Orientation ="Horizontal" Margin ="10" HorizontalAlignment ="Center" Height ="100" >
< Button Content ="Save" x:Name ="btnSave" Width ="230" Click ="btnSave_Click" />
< Button Content ="Cancel" x:Name ="btnCancel" Width ="230" Click ="btnCancel_Click" />
</ StackPanel >
</ Grid >
</ Grid >
ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace ApplicationLifecycle
{
public partial class SecondPage : PhoneApplicationPage
{
public SecondPage()
{
InitializeComponent();
}

private void btnSave_Click( object sender, RoutedEventArgs e)
{
Utils.SaveTravelReport(
(App.Current.RootVisual
as PhoneApplicationFrame).DataContext as TravelReportInfo,
" TravelReportInfo.dat " ,
false );
}

private void btnCancel_Click( object sender, RoutedEventArgs e)
{
Utils.ClearTravelReport(((App.Current.RootVisual
as PhoneApplicationFrame).DataContext as TravelReportInfo));
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Utils.Trace(
" Navigated To SecondPage " );

if (State.ContainsKey( " FocusedElement " ))
{
Control focusedElement
= this .FindName(State[ " FocusedElement " ] as string ) as Control;

if ( null != focusedElement)
focusedElement.Focus();
}

base .OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Utils.Trace(
" Navigated From MainPage " );

if (State.ContainsKey( " FocusedElement " ))
State.Remove(
" FocusedElement " );

object obj = FocusManager.GetFocusedElement();
if ( null != obj)
{
string focusedControl = (obj as FrameworkElement).Name;
State.Add(
" FocusedElement " , focusedControl);
}

base .OnNavigatedFrom(e);
}
}
}

◦       DateTimeToStringConverter用来实现旅行开始和结束日期与用户界面(UI)空间之间的绑定,同时完成必需的SL数据转换。

◦       TravelReportInfo是一个模型类,用来表示一个旅程;它包含了描述旅行的数据域

◦       Utils正如它的命名一样,是一个泛型类,它被整个工程中各种各样的函数广泛使用。

 

重载OnBackKeypress的缺省行为 –-缺省的行为假设用户正要通过导航键离开当前应用程序并正在被问及是否要保存当前的旅行报告数据 。然而,如果用户在应用程序第一个页面就点击Back同时此页面也是应用程序日志记录中的第一条,那么应用程序就不会询问用户是否保存旅行报告数据而直接退出。

 

应用程序执行模型

除了响应速度,应用程序执行模型 还为用户提供了在不同应用程序之间始终如一的导航切换体验。在Windows Phone上,用户可以从已安装应用程序列表或者在开始屏幕上的小方块菜单来实现向前导航功能。用户同样可以在运行中的应用程序某一页面或者在许多正在运行的应用程序中,通过点击电话设备上的Back键来实现向后导航的功能。

通过限制只允许一个应用程序在前台运行,Windows Phone 提供了一个无论在任何时候用户都能享受的准确无误的导航体验。为了实现这一目标, Windows Phone动态的激活和禁止应用程序,并把事件信息暴露给开发人员,使得他们能对当前应用程序状态发生的变化做出响应。通过实现这些事件处理程序,应用程序在不同状态之间进行转换时开发人员能够获得保存和恢复应用程序的状态信息。这种行为就创建了一种体验,用户认为他们可以从上次执行程序的挂起点接着执行这个应用程序。这个练习实验在细节上介绍了Windows Phone应用程序的生命周期,并描述了这样一种开发策略:开发人员可以充分利用执行模型事件所带来的优势。

当用户离开当前应用程序,操作系统则会终止该应用程序的进程,这一过程被称之为tombstone 或者 tombstoning 。操作系统会维护应用程序最后状态的信息,包括最后浏览的页面以及导航浏览的日志。如果用户返回到刚才执行的应用程序中,操作系统会重新启动相关应用程序进程并把状态数据传递给应用程序。

重要的: 请注意当您的应用程序处于tombstoning时,应用程序被“终止”了。您的应用程序不是挂起或“搁置”;运行您的应用程序的进程已经完全被终止,包括数据以及进程已经完全从内存里被清除了。

 

开发人员利用tombstone 事件来保存应用程序状态页面状态。利用这些状态,开发人员可以把应用程序恢复到最后一个正确的状态。

  • 应用程序状态 是应用程序的一种状态且并不与任何特定页面有关联。应用程序状态是在PhoneApplicationService类公开的事件中管理的。
  • 页面状态是一种应用程序页面可见的状态。它包含了诸如ScrollViewer 控件中滚轴的位置和TextBox控件中的内容等信息。页面的状态管理应该由OnNavigatedTo 和 OnNavigatedFrom事件处理程序来处理。

 

什么时候一个应用程序会被逻辑删除(tombstoned)呢? 

一般来讲,一个应用程序会在用户切换离开它时被逻辑删除。然而除了这个一般规则仍然存有一些例外。操作系统可能需要额外的资源保证前台运行程序的正常活动,那么此时在操作系统后台运行的应用程序就有可能在任何时候被逻辑删除。

当一个应用程序调用一个体验操作,对于用户来讲这个体验感觉就像原始应用程序的一个扩展,那么此时应用程序将不会自动调用逻辑删除。这些体验仅仅是本地流,是来帮助用户完成一些工作,比如选择照片。这些情况下不会被逻辑删除的原因是为了确保应用程序与相关体验之间的平滑的工作流。【例如 ,在选择照片和返回应用程序来使用这个照片之间不应该有延迟】

下面是一个体验列表,当他们被调用时,操作系统不会触发调用程序中的一个自动的tombstone:

  • PhotoChooserTask
  • CameraCaptureTask
  • MediaPlayerLauncher
  • EmailAddressChooserTask
  • PhoneNumberChooserTask
  • Multiplayer Game Invite [games]
  • Gamer You Card [games]

 

 

请注意:在一般情况下调用应用程序并不会被逻辑删除,但是当操作系统开启一个体验的时候发现需要比当前可用资源更多的资源时,那么应用程序就可能被逻辑删除。

除上述操作体验外,如果应用程序正在后台运行,那么将会被立即逻辑删除。出现那样场景的详情如下:

  • 用户申请导航操作而离开一个应用程序【例如,按Start键】
  • 应用程序调用一些Launchers/Choosers操作,且这些操作在以上并没有列出来
  • 系统需要更多的资源来保证前台程序的正常运行

 

 

模拟一个应用程序的生命周期 

本节将通过模拟一个Windows Phone应用程序的整个生命周期,包括对用户操作而导致一个应用程序的状态改变的描述,以及突出开发人员为给用户建立一个完整的体验,而在其代码中添加对事件的处理。 在本节中所有和生命周期相关的事件(启动、运行、关闭、禁止和激活)都是Microsoft.Phone.Shell命名空间下PhoneApplicationService类的成员。

启动(Launching)

当用户点击了手机上应用程序安装列表里的某一应用程序,或者点击了开始屏幕上的代表某一应用程序的小方块图标,此时一个Windows Phone应用程序就被启动了。无论用户使用哪种方式启动一个应用程序,该应用程序的实例已经被创建了。当应用程序被启动了,也就是一个启动事件被触发了。处理这个启动事件时,应用程序应该从一个独立的存储中读取所有必要的数据来为用户创建一个新的应用程序的会话进程。应用程序不应该试图从以前的应用程序实例中恢复瞬时状态。当用户启动一个应用程序,就出现了一个新的应用程序实例。

注意 启动和激活事件是互斥的。

 

运行(Running)

当启动事件被触发了,一个应用程序就开始运行了。 应用程序处于运行状态时,用户进行浏览该应用程序的页面等相关操作,此时应用程序会自己管理自己的状态。如果应用程序处于运行状态,那么与执行模型相关的唯一操作就是逐步的保存设置以及其他应用程序持久化数据(Persisting data),这样做的目的是为了避免当应用程序的状态发生改变时需要保存大量的数据。这是可选的,因为当应用程序只有少量的持久化数据—像这个练习实验一样,这个操作就不是必需的。

关闭 (Closing)

应用程序处于运行状态之后的状态是取决于用户采取了哪种操作。一个可能的操作是用户按下手机上的回退(Back)键从而回退到应用程序的前一页面,甚至翻过了应用程序的第一个页面。 当这种情况发生时,关闭事件将会被触发,此时应用程序被终止了。 处理关闭事件,应用程序应该把所有的持久化数据保存到独立的存储中。此时没有必要保存瞬时状态数据,即那些只和前应用程序实例相关的数据。因为用户如果要返回一个已经被终止的应用程序,唯一的方式就是重新启动它,打开它的首页。正如前面章节中提到的,当用户启动应用程序,它将会以一个全新的实例出现。

禁止 (Deactivating)

 如果一个应用程序正在运行,随后在操作系统前台被另一个应用程序或体验替代—例如,锁屏或者启动一个Chooser,这时第一个应用程序将会被禁止。

有好多种方法能够实现应用程序的禁止状态。当用户点击手机开始键或者手机由于超时导致设备自动处于锁屏状态都会使当前应用程序处于禁止状态—在这种状态下应用程序被逻辑删除了。当用户调用一个Launcher或者Chooser时,当前应用程序同样会被禁止,辅助应用程序允许用户执行拍照或者发送电子邮件等常见任务。无论以上哪种情况,当前运行的应用程序将会被禁止,禁止事件被触发了。并不像应用程序被终止一样,一个被禁止的应用程序可能会被逻辑删除。这就意味着应用程序不再运行。这个应用程序的进程已经被挂起或者终止(正如在“模拟一个应用程序的生命周期”一节中描述的样子),操作系统会保存能够代表应用程序的记录以及其一系列状态数据。这就使用户返回一个被禁止的应用程序成为可能性,应用程序才能被再次激活并把上次用户浏览的页面呈现出来。

在禁止状态事件处理过程中,一个应用程序应该存储其当前状态信息,状态信息是从PhoneApplicationService 类中的State属性公开的状态信息词典中获得的。 在状态信息词典中存储的数据是瞬时状态数据或者是能帮助应用程序在被禁止时恢复其状态的数据。由于并不能保证一个被禁止的应用程序会被重新激活,所以在此事件的处理中应用程序需要一直把持久化数据保存到一个独立的存储空间。

禁止事件处理程序所进行的所有操作必须在10秒钟内完成,否则操作系统将会直接终止应用程序而不是逻辑删除他。正是出于这个原因,当应用程序有大量的持久化数据需要保存,应用程序会在运行过程中对数据逐步地进行保存。

激活 (Activating)

当一个应用程序被禁止后,有可能这个应用程序永远不会被再次激活。用户可能会从头重新启动该应用程序,从而得到一个新的应用程序实例。或者用户可以启动其他几个应用程序,这样就会把处在应用程序堆栈最后的即使利用回退按键也不可能到达的欺骗性程序关闭掉。 

当然用户也有继续要使用原应用程序的可能性。这种情况可能发生在用户不停地敲击回退键直到指定的应用程序 。或者,一个Launcher或Chooser操作导致了当前应用程序被禁止,用户可以完成这个操作任务或取消这个新的操作任务。当用户返回一个处于逻辑删除状态的应用程序,该程序将会被重新激活,激活事件将会被触发。在此事件中,用户的应用程序将会从一独立存储中取回应用程序的持久化数据。这个应用程序同样也会从PhoneApplicationService类的状态信息词典中读取状态信息从而恢复到被禁止的应用程序之前的状态。 

下面的图表阐述了Windows Phone应用程序的生命周期:

gg477428.16(zh-tw,MSDN.10).jpg

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值