1、「後退鍵」不會終止應用session
關於 Windows Phone 8.1 的應用生命週期,第一個要知道的關鍵就是:「後退鍵」不會終止應用!異步
在 8.0 時代,不斷的按下「後退鍵」就能夠徹底的關閉而且終止應用,但在 8.1 中,這樣的行爲只會讓應用處在 Suspended(掛起)狀態,能夠經過長按「後退鍵」進入多任務界面查看。async
那若是還想像 8.0 同樣終止應用呢?(雖然不推薦也沒有必要)能夠在多任務界面點擊應用右上角的「叉叉」或者向下滑。ide
2、應用生命週期函數
應用的三個狀態分別是:this
A:NotRunningspa
也就是還沒開啓過應用,在多任務界面沒有該應用時。code
B:Runningxml
在屏幕上顯示的應用就是 Running 狀態,同時只會有 1 個應用處於 Running 狀態。blog
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 狀態。
在這過程不會觸發任何事件。
3、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; } }
internal sealed class SuspensionManager { private static Dictionary<string, object> _sessionState = new Dictionary<string, object>(); private static List<Type> _knownTypes = new List<Type>(); private const string sessionStateFilename = "_sessionState.xml"; public static Dictionary<string, object> SessionState { get { return _sessionState; } } public static List<Type> KnownTypes { get { return _knownTypes; } } public static async Task SaveAsync() { try { foreach (var weakFrameReference in _registeredFrames) { Frame frame; if (weakFrameReference.TryGetTarget(out frame)) { SaveFrameNavigationState(frame); } } MemoryStream sessionData = new MemoryStream(); DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes); serializer.WriteObject(sessionData, _sessionState); StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(sessionStateFilename, CreationCollisionOption.ReplaceExisting); using (Stream fileStream = await file.OpenStreamForWriteAsync()) { sessionData.Seek(0, SeekOrigin.Begin); await sessionData.CopyToAsync(fileStream); } } catch (Exception e) { throw new SuspensionManagerException(e); } } public static async Task RestoreAsync(String sessionBaseKey = null) { _sessionState = new Dictionary<String, Object>(); try { StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(sessionStateFilename); using (IInputStream inStream = await file.OpenSequentialReadAsync()) { DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes); _sessionState = (Dictionary<string, object>)serializer.ReadObject(inStream.AsStreamForRead()); } foreach (var weakFrameReference in _registeredFrames) { Frame frame; if (weakFrameReference.TryGetTarget(out frame) && (string)frame.GetValue(FrameSessionBaseKeyProperty) == sessionBaseKey) { frame.ClearValue(FrameSessionStateProperty); RestoreFrameNavigationState(frame); } } } catch (Exception e) { throw new SuspensionManagerException(e); } } private static DependencyProperty FrameSessionStateKeyProperty = DependencyProperty.RegisterAttached("_FrameSessionStateKey", typeof(String), typeof(SuspensionManager), null); private static DependencyProperty FrameSessionBaseKeyProperty = DependencyProperty.RegisterAttached("_FrameSessionBaseKeyParams", typeof(String), typeof(SuspensionManager), null); private static DependencyProperty FrameSessionStateProperty = DependencyProperty.RegisterAttached("_FrameSessionState", typeof(Dictionary<String, Object>), typeof(SuspensionManager), null); private static List<WeakReference<Frame>> _registeredFrames = new List<WeakReference<Frame>>(); public static void RegisterFrame(Frame frame, String sessionStateKey, String sessionBaseKey = null) { if (frame.GetValue(FrameSessionStateKeyProperty) != null) { throw new InvalidOperationException("Frames can only be registered to one session state key"); } if (frame.GetValue(FrameSessionStateProperty) != null) { throw new InvalidOperationException("Frames must be either be registered before accessing frame session state, or not registered at all"); } if (!string.IsNullOrEmpty(sessionBaseKey)) { frame.SetValue(FrameSessionBaseKeyProperty, sessionBaseKey); sessionStateKey = sessionBaseKey + "_" + sessionStateKey; } frame.SetValue(FrameSessionStateKeyProperty, sessionStateKey); _registeredFrames.Add(new WeakReference<Frame>(frame)); RestoreFrameNavigationState(frame); } public static void UnregisterFrame(Frame frame) { SessionState.Remove((String)frame.GetValue(FrameSessionStateKeyProperty)); _registeredFrames.RemoveAll((weakFrameReference) => { Frame testFrame; return !weakFrameReference.TryGetTarget(out testFrame) || testFrame == frame; }); } public static Dictionary<String, Object> SessionStateForFrame(Frame frame) { var frameState = (Dictionary<String, Object>)frame.GetValue(FrameSessionStateProperty); if (frameState == null) { var frameSessionKey = (String)frame.GetValue(FrameSessionStateKeyProperty); if (frameSessionKey != null) { if (!_sessionState.ContainsKey(frameSessionKey)) { _sessionState[frameSessionKey] = new Dictionary<String, Object>(); } frameState = (Dictionary<String, Object>)_sessionState[frameSessionKey]; } else { frameState = new Dictionary<String, Object>(); } frame.SetValue(FrameSessionStateProperty, frameState); } return frameState; } private static void RestoreFrameNavigationState(Frame frame) { var frameState = SessionStateForFrame(frame); if (frameState.ContainsKey("Navigation")) { frame.SetNavigationState((String)frameState["Navigation"]); } } private static void SaveFrameNavigationState(Frame frame) { var frameState = SessionStateForFrame(frame); frameState["Navigation"] = frame.GetNavigationState(); } } public class SuspensionManagerException : Exception { public SuspensionManagerException() { } public SuspensionManagerException(Exception e) : base("SuspensionManager failed", e) { } }
4、OnResuming
既然在 OnSuspending 和 SaveState 方法中保存了必要數據,就能夠在 OnResuming 和 LoadState 方法中獲取以前保存的數據:
void OnResuming(object sender, object e) { Data.Value += this.CalculateOffsetTimeInDecimalSeconds(this.suspensionTime); }
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) { if ((e.PageState != null) && e.PageState.ContainsKey("isEditing")) { this.viewModel.SetEditMode(); this.viewModel.DataItem.Title = e.PageState["currentText"] as string; } }
5、OnLaunched
首先,在 OnLaunched 方法中能夠經過 e.PreviousExecutionState 瞭解到應用以前的狀態。
狀態包括:
(1)CloseByUser:被用戶主動在多任務界面中關閉
(2)NotRunning:沒有啓動過
(3)Running:啓動中
(4)Terminated:掛起狀態時因內存不足被系統終止
(5)Suspended:掛起狀態
所以,能夠經過對此的判斷,根據不一樣狀況處理應用:
protected async override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; if (rootFrame == null) { rootFrame = new Frame(); SuspensionManager.RegisterFrame(rootFrame, "AppFrame"); rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { try { await SuspensionManager.RestoreAsync(); } catch (SuspensionManagerException) { } } Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { rootFrame.Navigate(typeof(MainPage), e.Arguments); } Window.Current.Activate(); }
6、注意
以上的方法儘可能使用異步操做,不要進行大量的複雜操做。