一、“后退键”不会终止应用
关于 Windows Phone 8.1 的应用生命周期,第一个要知道的关键就是:“后退键”不会终止应用!
在 8.0 时代,不断的按下“后退键”就可以完全的关闭并且终止应用,但在 8.1 中,这样的行为只会让应用处在 Suspended(挂起)状态,可以通过长按“后退键”进入多任务界面查看。
那如果还想像 8.0 一样终止应用呢?(虽然不推荐也没有必要)可以在多任务界面点击应用右上角的“叉叉”或者向下滑。
二、应用生命周期
应用的三个状态分别是:
A:NotRunning
也就是还没开启过应用,在多任务界面没有该应用时。
B:Running
在屏幕上显示的应用就是 Running 状态,同时只会有 1 个应用处于 Running 状态。
C:Suspended
不在屏幕上显示并能在多任务界面查看的应用则处于 Suspended(挂起)状态。
三种状态间切换的操作:
(1)NotRunning -> Running
要从 NotRunning 切换到 Running 状态,其实也就是开启应用,可通过点击应用磁贴、应用间协议启动、Cortana等方式。
在状态的切换过程中会触发 OnLaunched 事件。
(2)Running -> Suspended
当应用不再占据屏幕时则从 Running 切换到 Suspended 状态,可以是“Win”键、“返回键”,有电话打来时也会挂起。
在状态的切换过程中会触发 OnSuspending 事件。
(3)Suspended -> Running
如果在应用挂起状态时没有因为某些原因(比如内存不足)导致应用终止的话,点击磁贴或者多任务切换都会让应用从 Suspender 返回到 Running 状态。
在状态的切换过程中会依次触发 OnResuming 和 OnLaunched 事件。
(4)Suspended -> NotRunning
如果在应用挂起状态时因为某些原因(比如内存不足)导致应用终止的话,则会从 Suspended 变成 NotRunning 状态。
在这过程不会触发任何事件。
三、OnSuspending
因为应用在挂起状态时,并不能预测应用是否会因为某些原因(比如内存不足)而终止,而在这终止过程中也没有事件让开发者处理应用数据,所以只能在应用将要挂起时准备。因此 OnSuspending 事件变得十分重要。
若要使用 OnSuspending 方法则先要在构造函数中添加对其的引用:
public App() { this.InitializeComponent(); this.Suspending += OnSuspending; }
而在 OnSuspending 方法中可以根据需要保存页面数据,比如输入框内的文本、页面导航历史等,可以通过保存在应用独立存储中或利用 NavigationHelper 和 SuspensionManager 类等:
async void OnSuspending(object sender, SuspendingEventArgs e) { SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral(); await this.SaveStateToLocalFile(Data.Value); await SuspensionManager.SaveAsync(); deferral.Complete(); }
如果只想保存某个页面的信息则可以在 SaveState 中保存:
private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e) { e.PageState["isEditing"] = true; e.PageState["currentText"] = this.viewModel.DataItem.Title; }
NavigationHelper 和 SuspensionManager 类是添加基本页时 Visual Studio 自动添加的:
public class NavigationHelper : DependencyObject { private Page Page { get; set; } private Frame Frame { get { return this.Page.Frame; } } public NavigationHelper(Page page) { this.Page = page; this.Page.Loaded += (sender, e) => { WINDOWS_PHONE_APP Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed; e if }; this.Page.Unloaded += (sender, e) => { WINDOWS_PHONE_APP Windows.Phone.UI.Input.HardwareButtons.BackPressed -= HardwareButtons_BackPressed; e if }; } #region Navigation support RelayCommand _goBackCommand; RelayCommand _goForwardCommand; public RelayCommand GoBackCommand { get { if (_goBackCommand == null) { _goBackCommand = new RelayCommand( () => this.GoBack(), () => this.CanGoBack()); } return _goBackCommand; } set { _goBackCommand = value; } } public RelayCommand GoForwardCommand { get { if (_goForwardCommand == null) { _goForwardCommand = new RelayCommand( () => this.GoForward(), () => this.CanGoForward()); } return _goForwardCommand; } } public virtual bool CanGoBack() { return this.Frame != null && this.Frame.CanGoBack; } public virtual bool CanGoForward() { return this.Frame != null && this.Frame.CanGoForward; } public virtual void GoBack() { if (this.Frame != null && this.Frame.CanGoBack) this.Frame.GoBack(); } public virtual void GoForward() { if (this.Frame != null && this.Frame.CanGoForward) this.Frame.GoForward(); } #if WINDOWS_PHONE_APP private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e) { if (this.GoBackCommand.CanExecute(null)) { e.Handled = true; this.GoBackCommand.Execute(null); } } #else private void CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs e) { var virtualKey = e.VirtualKey; if ((e.EventType == CoreAcceleratorKeyEventType.SystemKeyDown || e.EventType == CoreAcceleratorKeyEventType.KeyDown) && (virtualKey == VirtualKey.Left || virtualKey == VirtualKey.Right || (int)virtualKey == 166 || (int)virtualKey == 167)) { var coreWindow = Window.Current.CoreWindow; var downState = CoreVirtualKeyStates.Down; bool menuKey = (coreWindow.GetKeyState(VirtualKey.Menu) & downState) == downState; bool controlKey = (coreWindow.GetKeyState(VirtualKey.Control) & downState) == downState; bool shiftKey = (coreWindow.GetKeyState(VirtualKey.Shift) & downState) == downState; bool noModifiers = !menuKey && !controlKey && !shiftKey; bool onlyAlt = menuKey && !controlKey && !shiftKey; if (((int)virtualKey == 166 && noModifiers) || (virtualKey == VirtualKey.Left && onlyAlt)) { e.Handled = true; this.GoBackCommand.Execute(null); } else if (((int)virtualKey == 167 && noModifiers) || (virtualKey == VirtualKey.Right && onlyAlt)) { e.Handled = true; this.GoForwardCommand.Execute(null); } } } private void CoreWindow_PointerPressed(CoreWindow sender, PointerEventArgs e) { var properties = e.CurrentPoint.Properties; if (properties.IsLeftButtonPressed || properties.IsRightButtonPressed || properties.IsMiddleButtonPressed) return; bool backPressed = properties.IsXButton1Pressed; bool forwardPressed = properties.IsXButton2Pressed; if (backPressed ^ forwardPressed) { e.Handled = true; if (backPressed) this.GoBackCommand.Execute(null); if (forwardPressed) this.GoForwardCommand.Execute(null); } } #endif #endregion #region Process lifetime management private String _pageKey; public event LoadStateEventHandler LoadState; public event SaveStateEventHandler SaveState; public void OnNavigatedTo(NavigationEventArgs e) { var frameState = SuspensionManager.SessionStateForFrame(this.Frame); this._pageKey = "Page-" + this.Frame.BackStackDepth; if (e.NavigationMode == NavigationMode.New) { var nextPageKey = this._pageKey; int nextPageIndex = this.Frame.BackStackDepth; while (frameState.Remove(nextPageKey)) { nextPageIndex++; nextPageKey = "Page-" + nextPageIndex; } if (this.LoadState != null) { this.LoadState(this, new LoadStateEventArgs(e.Parameter, null)); } } else { if (this.LoadState != null) { this.LoadState(this, new LoadStateEventArgs(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey])); } } } public void OnNavigatedFrom(NavigationEventArgs e) { var frameState = SuspensionManager.SessionStateForFrame(this.Frame); var pageState = new Dictionary<String, Object>(); if (this.SaveState != null) { this.SaveState(this, new SaveStateEventArgs(pageState)); } frameState[_pageKey] = pageState; } #endregion } public delegate void LoadStateEventHandler(object sender, LoadStateEventArgs e); public delegate void SaveStateEventHandler(object sender, SaveStateEventArgs e); public class LoadStateEventArgs : EventArgs { public Object NavigationParameter { get; private set; } public Dictionary<string, Object> PageState { get; private set; } public LoadStateEventArgs(Object navigationParameter, Dictionary<string, Object> pageState) : base() { this.NavigationParameter = navigationParameter; this.PageState = pageState; } } public class SaveStateEventArgs : EventArgs { public Dictionary<string, Object> PageState { get; private set; } public SaveStateEventArgs(Dictionary<string, Object> pageState) : base() { this.PageState = pageState; } }