原文: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. NGUI简单背包系统的实现

    一.利用txt文件存储游戏物品信息 首先在asset下创建一个txt文件,这里我们命名为objectsInfoList.txt,并将其拖放到unity Project视图中. 其中txt中我们先存放一 ...

  2. NodeJS常用模块介绍

    收集了NodeJS开发中常用的一些模块. MVC框架 - Express Express 是轻量灵活的Nodejs Web应用框架,它可以快速地搭建网站.Express框架建立在Nodejs内置的Ht ...

  3. ATL 创COM物

    我原来以前写dll创建过程,而直接使用LoadLibrary加载动态库. 但ATL提出了一个非常重要的特点是引入COM对象的概念. 首先. ATL active template library该活动 ...

  4. Web API 数据流使用

    ASP.NET Web API 应用教程(一) ——数据流使用   相信已经有很多文章来介绍ASP.Net Web API 技术,本系列文章主要介绍如何使用数据流,HTTPS,以及可扩展的Web AP ...

  5. C奇淫技巧,——宏神奇

    一个.宏列表 当这个问题面临: 有一个标志变量.位代表对应的含义. 我们须要提供一组函数来訪问设置这些位.可是对于每一个标记位的操作函数都是相似的.若有32个位,难道要搞32套相似的操作函数么? 你或 ...

  6. hdu4185 Oil Skimming(偶匹配)

    <span style="font-family: Arial; font-size: 14.3999996185303px; line-height: 26px;"> ...

  7. MSSQL基础

    前言 最近看到一些关于sql的汇总博客,觉得还是很不错的.于是心血来潮,也想写一篇自己对这方面的一些认识(主要是点出一下自己比较少用demo写的,一般都是直接改设计时的,例如建表.该字段名). 一.数 ...

  8. SystemTrayDemo

    遗留问题:win7  java.awt.TrayIcon的displayMessage方法没有生效,通知消息不能正常弹出. 许多桌面环境都有一个区域用于放置在后台运行的程序的图标,这些程序偶尔会将某些 ...

  9. Cocos观察者设计模式和通报机制

    观察员(Observer)模式也称为公告/订阅(Publish/Subscribe)模式.这是 MVC( 模型-视图-控制器)模型的重要组成部分.天气一直讨论的英国最喜欢的话题,近期天气变化几年已成为 ...

  10. T-SQL性能调整——信息收集

    原文:T-SQL性能调整--信息收集 IO信息(自服务器启动开始) --Database IO analysis WITH IOFORDATABASE AS ( SELECT DB_NAME(VFS. ...