Windows 8为应用程序引入了一个新的生命周期,完全不同于传统的桌面应用程序的生命周期。在Windows 8.1中有些变化,在Windows 10中又有一些变化。如果使用Windows 10和平板电脑模式,应用程序的生命周期与桌面模式是不同的。在平板电脑模式中,应用程序通常全屏显示。分离键盘(对于平板电脑设备,如Microsoft Surface),或在Action Center中使用Tablet Mode按钮,可以自动切换到平板模式。在平板模式下运行应用程序时,如果应用程序进入后台(用户切换到另一个应用程序),就会暂停,它不会得到任何更多的CPU利用率。这样,应用程序不消耗任何电力。应用程序在后台时,只使用内存,一旦用户切换到这个应用程序,应用程序就再次激活。
当内存资源短缺时,Windows可以终止暂停应用程序的进程,从而终止该应用程序。应用程序不会收到任何消息,所以不能对此事件做出反应。因此,应用程序应该在进入暂停模式前做一些处理工作,保存其状态。等到应用程序终止时进行处理就晚了。
当收到暂停事件时,应用程序应该将其状态存储在磁盘上。如果再次启动应用程序,应用程序可以显示给用户,好像它从未终止。只需要把页面堆栈的信息存储到用户退出的页面上,恢复页面堆栈,并把字段初始化为用户输入的数据,就允许用户返回。
本节的示例应用程序ApplicationLifeline就完成这个任务。在这个程序中,允许在多个页面之间的导航,可以输入状态。应用程序暂停时,存储页面堆栈和状态,在启动应用程序时恢复它们。
1. 应用程序的执行状态
应用程序的状态使用ApplicationExecutionState枚举定义。该枚举定义了NotRunning、Running、Suspended、Terminated和CloseByUser状态。应用程序需要知道并存储自己的状态,因为用户在返回应用程序时希望继续原来的操作。
在App类的OnLaunched方法中,可以使用LauchActivatedEventArgs参数的PreviousExecutionState属性获取应用程序的前一个执行状态。如果应用程序是在安装后第一次启动,在重启计算机后启动,或者用户上一次在任务管理器中终止了其进程,那么该应用程序的前一个状态是NotRunning。如果用户单击应用程序的图标时应用程序已经激活,或者应用程序通过某个激活协定激活,则其前一个执行状态为Running。如果应用程序被暂停,那么激活它时PreviousExecutionState属性会返回Suspended。一般来说,在这种情况下不需要执行什么特殊操作。因为状态仍然在内存中可用。在暂停状态下,应用程序不使用CPU循环,也没有磁盘访问。
注意:
应用程序可以实现一个或多个协定,然后用其中一个协定激活应用程序。这类协定的一个例子是共享,使用这个协定,用户可以共享另一个应用程序的一些数据,并使用它作为共享目标,启动一个Windows应用程序。实现共享协定参见本章的"共享数据"一节。
2. 在页面之间导航
展示Windows应用程序的生命周期的示例应用程序(ApplicationLifetime)从Blank App模板开始。创建项目后,添加页面Page 1和Page 2,实现页面之间的导航。
在MainPage中,添加两个按钮控件来导航Page 2和Page 1,再添加两个文本框控件,在导航时传递数据:
<StackPanel Spacing="5"> <Button Content="Page 1" Click="{x:Bind GoToPage1,Mode=OneTime}"/> <TextBox Header="Parameter 1" Text="{x:Bind ParameterPage1,Mode=TwoWay}"/> <Button Content="Page 2" Click="{x:Bind GoToPage2,Mode=OneTime}"/> <TextBox Header="Parameter 2" Text="{x:Bind ParameterPage2,Mode=TwoWay}"/> </StackPanel>代码隐藏文件包含事件处理程序、Page 1和Page 2的导航代码,以及参数的属性:
public string ParameterPage1 { get; set; } public string ParameterPage2 { get; set; } public void GoToPage1() => Frame.Navigate(typeof(Page1), ParameterPage1); public void GoToPage2() => Frame.Navigate(typeof(Page2), ParameterPage2);Page 1的UI元素显示在导航到这个页面时接收到的数据、允许用户导航到Page 2的按钮,以及允许用户输入一些状态信息的一个文本框,应用程序终止时会保存这些状态信息:
<StackPanel Spacing="5"> <TextBlock Text="Page 1" Style="{StaticResource HeaderTextBlockStyle}"/> <TextBlock Text="{x:Bind ReceivedContent,Mode=OneTime}" Style="{StaticResource BodyTextBlockStyle}"/> <TextBox Text="{x:Bind Parameter,Mode=TwoWay}"/> <Button Content="Navigate to Page 2" Click="{x:Bind GoToPage2,Mode=OneTime}"/> <!--<TextBox Header="Session State 1" Text="{x:Bind Data.Session1,Mode=TwoWay}"/> <TextBox Header="Session State 2" Text="{x:Bind Data.Session2,Mode=TwoWay}"/>--> </StackPanel>类似于MainPage ,Page 1的导航代码为导航时传递的数据定义了一个自动实现的属性,和实现导航到Page 2的一个事件处理程序:
public void GoToPage2() => Frame.Navigate(typeof(Page2), Parameter); public string Parameter { get; set; }在代码隐藏文件中,导航参数在OnNavigatedTo()方法的重写版本中接收。接收到的参数分配给自动实现的属性ReceivedContent:
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); ReceivedContent = e.Parameter?.ToString(); } public string ReceivedContent { get; set; }在导航的实现代码中,Page 2非常类似于Page 1,所以这里不重复它的实现。
这里,标题栏后退按钮的可见性和处理程序在类BackButtonManager中定义。如果当前Frame的CanGoBack属性返回true,那么构造函数的实现代码使后退按钮可见。如果堆栈可用,就实现OnBackRequested方法,返回页面堆栈:
public class BackButtonManager : IDisposable { private SystemNavigationManager _navigationManager; private Frame _frame; public BackButtonManager(Frame frame) { _frame = frame ?? throw new ArgumentNullException(nameof(frame)); _navigationManager = SystemNavigationManager.GetForCurrentView(); _navigationManager.AppViewBackButtonVisibility = _frame.CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed; _navigationManager.BackRequested += OnBackRequested; } private void OnBackRequested(object sender, BackRequestedEventArgs e) { if (_frame.CanGoBack) { _frame.GoBack(); e.Handled = true; } } public void Dispose() { _navigationManager.BackRequested -= OnBackRequested; } }在所有的页面中,通过在OnNavigatedTo方法中传递Frame来实例化BackButtonManager,它在OnNavigatedFrom方法中销毁:
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); _backButtonManager = new BackButtonManager(Frame); } protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { base.OnNavigatingFrom(e); _backButtonManager.Dispose(); }有了所有这些代码,用户可以在3个不同的页面之间后退和前进。下一步需要记住页面和页面堆栈,把应用程序导航到用户最近一次访问的页面上。