原文:Windows Phone 8.1 应用生命周期

一、“后退键”不会终止应用

关于 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 == || (int)virtualKey == ))
{
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 == && noModifiers) ||
(virtualKey == VirtualKey.Left && onlyAlt))
{
e.Handled = true;
this.GoBackCommand.Execute(null);
}
else if (((int)virtualKey == && 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;
}
}

NavigationHelper

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(, 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)
{ }
}

SuspensionManager

四、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;
}
}

五、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[]; 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();
}

六、注意

以上的方法尽量使用异步操作,不要进行大量的复杂操作。

Windows Phone 8.1 应用生命周期的更多相关文章

  1. Windows Phone 应用程序的生命周期(二)

    一.App.xaml.cs /// <summary> /// Application 对象的构造函数. /// </summary> public App() { // 未捕 ...

  2. 重新想象 Windows 8 Store Apps (70) - 其它: 文件压缩和解压缩, 与 Windows 商店相关的操作, app 与 web, 几个 Core 的应用, 页面的生命周期和程序的生命周期

    [源码下载] 重新想象 Windows 8 Store Apps (70) - 其它: 文件压缩和解压缩, 与 Windows 商店相关的操作, app 与 web, 几个 Core 的应用, 页面的 ...

  3. Windows 8 动手实验系列教程 实验5:进程生命周期管理

    动手实验 实验5:进程生命周期管理 2012年9月 简介 进程生命周期管理对构建Windows应用商店应用的开发者来说是需要理解的最重要的概念之一.不同于传统的Windows应用(它们即使在后台仍然继 ...

  4. 与众不同 windows phone (27) - Feature(特性)之搜索的可扩展性, 程序的生命周期和页面的生命周期, 页面导航, 系统状态栏

    原文:与众不同 windows phone (27) - Feature(特性)之搜索的可扩展性, 程序的生命周期和页面的生命周期, 页面导航, 系统状态栏 [索引页][源码下载] 与众不同 wind ...

  5. 快速构建Windows 8风格应用30-应用生命周期管理

    原文:快速构建Windows 8风格应用30-应用生命周期管理 引言 Windows 8 中可以启动多个应用并在其中切换,我们没有必要担心降低系统速度或消耗电池电量. 因为系统会自动挂起(有时会终止) ...

  6. Windows PowerShell 学习之——Cmdlet处理生命周期

    这一次介绍一下Cmdlet处理过程的生命周期 总共分为六个部分 1.概述 2. 命令行输入绑定参数(parameters) 3. 开始指令处理 4. 接受管道输入绑定参数 5. 处理记录 6. 处理记 ...

  7. Windows Phone 8.1 生命周期调试

    之前重装了机子,今天调试时突然找不到调试生命周期的菜单栏了.最后找了5分钟,终于找回来了,特此记录以免以后重装后再出现这种状况. 项目启动调试后: 这样是没有显示调试生命周期的,接下来在工具栏右键: ...

  8. Xamarin.Android活动的生命周期

    一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没有丢失.可以看出app的“生命”是掌握在系统手上的, ...

  9. 2000条你应知的WPF小姿势 基础篇<22-27 WPF生命周期, 基础类等>

    端午长假在家陪着女朋友, 幸福感满满,生活对于一只饱经忧患的程序猿来说也是非常重要的,也就暂时没有更新博客.休假结束,回归奋斗的日子了,开始继续更新WPF系列. 在正文开始之前需要介绍一个人:Sean ...

随机推荐

  1. 【Java GUI】Java GUI基金会

    AWT和Swing Swing是个AWT改进和扩展. 书写GUI规划.Swing和AWT曾效力.他们共存 Java基础类(JFC)于. 虽然AWT和Swing都提供了构造图形界面元素的类.但它们的虫药 ...

  2. Linux的proc文件系统

    proc,用户空间和内核空间能够通过该接口通信, 与普通文件不同的是.这些虚拟文件的内容都是动态创建的. proc文件系统是一个伪文件系统,它仅仅存在内存其中,而不占用外存空间. 它以文件系统的方式为 ...

  3. STL源代码剖析(一) - 内存分配

    Allocaor allocator 指的是空间配置器,用于分配内存.STL中默认使用SGI STL alloc作为STL的内存分配器,尽管未能符合标准规格,但效率上更好.SGI STL也定义有一个符 ...

  4. NYOJ 118 路方案(第二小的跨越)

    修路方案 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描写叙述 南将军率领着很多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N.因为交通不太便利,南将军准备修 ...

  5. VirtualBox创建虚拟电脑、执行Genymotion模拟器报错

    当安装完Genynition关于Android应用的调试模拟器之后,在Genymotion执行的平台virtualBox:VirtualBox创建虚拟电脑.执行Genymotion模拟器报错: 错误卖 ...

  6. 【Shell剧本练习】得出的结论是当前用户

    推断是否当前用户root.假设是暗示root用户,假设而不是提示对于普通用户 #!/bin/bash #title: testus.sh #author: orangleliu #date: 2014 ...

  7. hadoop得知;block数据块;mapreduce实现样例;UnsupportedClassVersionError变态;该项目的源代码相关联

    对于开源的东西.特别是刚出来不久.我认为最好的学习方法是能够看到源代码,doc,样品测试 为了方便查看源代码,导入与项目相关的源代码 watermark/2/text/aHR0cDovL2Jsb2cu ...

  8. JavaIO流程--创建文件和目录的实例

    *创建函数:  *public boolean createNewFile():创建文件 本文假设存在.不创造(转让file.createNewFile()返回false)  *public bool ...

  9. Java于 初始化序列?

    我们正处于java于 Java中初始化的顺寻? java代码: package sru.love.c; class Person { String name = "Person"; ...

  10. JAVA基础实例(三)--排序

    冒泡排序 是一种简单的排序算法.它反复地走訪过要排序的数列,一次比較两个元素.假设他们的顺序错误就把他们交换过来.走訪数列的工作是反复地进行直到没有再须要交换,也就是说该数列已经排序完毕. 这个算法的 ...