最近比较忙有一段时间没有更新了,再接再厉继续分享。

案例下载:https://github.com/NewBLife/UWP/tree/master/SuspendSample

先我们看看App在生命周期中会出现那些状态:

详细介绍参考官网:App lifecycle  https://msdn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle

一般情况:

比如用新闻APP看新闻的时候突然收到邮件,然后跳转到邮件APP查看邮件,查看完了再回到APP继续看新闻。

这个时候如果不做中断挂起处理的话,是很难保证APP会恢复到跳转之前的状态。之所以说很难保证是因为如果手机内存够大够用系统就不会关闭没用的app让它驻留在内存中,下次直接从内存恢复,这样可以恢复到跳转之前的状态。如果内存不够用系统会关闭app回收资源,这个时候没有中断保存进度处理再次启动的时候就会从新开始,无法恢复到跳转前的状态。

正是为了这样的人性化处理才有中断恢复处理的必要性。

中断复原的原理:

数据保存时机:从Running--->Suspended的时候触发Suspending事件做画面进度保存处理,

数据恢复时机:如果内存没回收直接触发Resuming事件不需要做任何事情,如果回收的情况下在调用app的OnLaunched中通过判断Terminated状态做数据恢复处理。

中断复原实现:(MVVM实现方式以后待续)

在Win8.1开发StoreApp的时候默认会在项目中添加SuspensionManager.cs文件并且配置好,然而在UWP开发中模板没有自带这个文件,为什么?难道还有其他处理方法?

在网上找了下没找到原因,如果有知道欢迎介绍。虽然没找到结果无意发现了一个很好的开源框架:Template10,UWP很多常用的开发技巧都封装好了,减少不少工作量。Template10详细介绍参考:https://github.com/Windows-XAML/Template10/wiki

那我们的中断处理就有两种方法:

  • SuspensionManager.cs形式
  • Template10 插件

第一种SuspensionManager.cs形式实现:

找到SuspensionManager.cs文件,在微软的UWP例子里头有:https://github.com/Microsoft/Windows-universal-samples/blob/master/Samples/ApplicationData/cs/SuspensionManager.cs。把这个文件趴下来或者新建一个win8.1的app从它里面拷贝过来,别忘记改命名空间。

在App.xaml.cs文件确认OnSuspending事件是否注册了。

public App()
{
Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
Microsoft.ApplicationInsights.WindowsCollectors.Session);
this.InitializeComponent();
this.Suspending += OnSuspending;
}

在App.xaml.cs中找到OnSuspending事件添加中断保存处理SuspensionManager.SaveAsync()。由于是非同期操作事件名前记得加上async。

/// <summary>
/// 在将要挂起应用程序执行时调用。 在不知道应用程序
/// 无需知道应用程序会被终止还是会恢复,
/// 并让内存内容保持不变。
/// </summary>
/// <param name="sender">挂起的请求的源。</param>
/// <param name="e">有关挂起请求的详细信息。</param>
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// 保存应用状态
await SuspensionManager.SaveAsync();
deferral.Complete();
}

在App.xaml.cs中添加恢复处理。默认OnLaunched方法中是已经留好位置的,找到【//TODO: 从之前挂起的应用程序加载状态】替换为SuspensionManager.RestoreAsync()。以及关联设置SuspensionManager.RegisterFrame(rootFrame, "AppFrame")。由于是非同期操作事件名前记得加上async。

/// <summary>
/// 在应用程序由最终用户正常启动时进行调用。
/// 将在启动应用程序以打开特定文件等情况下使用。
/// </summary>
/// <param name="e">有关启动请求和过程的详细信息。</param>
protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = false;
}
#endif
Frame rootFrame = Window.Current.Content as Frame; // 不要在窗口已包含内容时重复应用程序初始化,
// 只需确保窗口处于活动状态
if (rootFrame == null)
{
// 创建要充当导航上下文的框架,并导航到第一页
rootFrame = new Frame(); //将框架与 SuspensionManager 键关联
SuspensionManager.RegisterFrame(rootFrame, "AppFrame"); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// 数据恢复
await SuspensionManager.RestoreAsync();
} // 将框架放在当前窗口中
Window.Current.Content = rootFrame;
} if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// 当导航堆栈尚未还原时,导航到第一页,
// 并通过将所需信息作为导航参数传入来配置
// 参数
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// 确保当前窗口处于活动状态
Window.Current.Activate();
}
}

这样简单的中断挂机以及恢复处理就配置完成了。

简单使用,在MainPage.xaml.cs里面重载OnNavigatedFrom和OnNavigatedTo方法。

/// <summary>
/// 保存数据
/// </summary>
/// <param name="e"></param>
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
var _pageKey = "Page-" + this.GetType().ToString();
var pageState = new Dictionary<String, Object>(); pageState[nameof(txtInput)] = txtInput.Text; frameState[_pageKey] = pageState;
}
/// <summary>
/// 恢复数据
/// </summary>
/// <param name="e"></param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e); if (e.NavigationMode == NavigationMode.New)
{ }
else
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
var _pageKey = "Page-" + this.GetType().ToString(); var data = (Dictionary<String, Object>)frameState[_pageKey];
if (data != null && data.ContainsKey(nameof(txtInput)))
{
txtInput.Text = data[nameof(txtInput)].ToString();
}
}
}

SuspensionManager.SaveAsync()代码如下:

主要原理—>通过遍历每个Frame调用frame.GetNavigationState()方法收集画面数据到_sessionState,然后将_sessionState字典数据序列化保存到LocalState下的_sessionState.xml文件中。

/// <summary>
/// 保存当前 <see cref="SessionState"/>。 任何 <see cref="Frame"/> 实例
/// (已向 <see cref="RegisterFrame"/> 注册)都还将保留其当前的
/// 导航堆栈,从而使其活动 <see cref="Page"/> 可以
/// 保存其状态。
/// </summary>
/// <returns>反映会话状态保存时间的异步任务。</returns>
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); // 获取 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);
}
}

SuspensionManager.RestoreAsync()代码如下:

主要原理—>读取LocalState下_sessionState.xml文件的内容反序列化保存到_sessionState字典中。然后调用frame.SetNavigationState((String)frameState["Navigation"])重新加载数据。

/// <summary>
/// 还原之前保存的 <see cref="SessionState"/>。 任何 <see cref="Frame"/> 实例
/// (已向 <see cref="RegisterFrame"/> 注册)都还将还原其先前的导航
/// 状态,从而使其活动 <see cref="Page"/> 可以还原其
/// 状态。
/// </summary>
/// <param name="sessionBaseKey">标识会话类型的可选密钥。
/// 这可用于区分多个应用程序启动方案。</param>
/// <returns>反映何时读取会话状态的异步任务。
/// 在此任务完成之前,不应依赖 <see cref="SessionState"/>
/// 完成。</returns>
public static async Task RestoreAsync(String sessionBaseKey = null)
{
_sessionState = new Dictionary<String, Object>(); try
{
// 获取 SessionState 文件的输入流
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);
}
}

★做序列化和反序列化的时候用到了_knownTypes,这个是重点。如果要对自定义类型,列表数据做中断保存处理时需要添加自定义类型的Type到_knownTypes,否则序列化会失败。

_knownTypes详细使用后续章节待续…

UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理的更多相关文章

  1. UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件

    在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...

  2. UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中

    前一段开发UWP应用的时候因为系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)浪费了不少时间.现象就是在手机版的详 ...

  3. UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享

    在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...

  4. UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)

    最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...

  5. UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)

    最近UWP开发在海外很潮流,随着微软收购Xamarin,我们这些C#程序员也可以靠这杆小米枪挑战Android,IOS平台了. 那我们为什么选择MVVM做UWP开发?MVC,MVP,MVVM他们之间到 ...

  6. UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)

    最近一直在做UWP开发,为了节省成本等等接触到MVVMlight,觉得有必要发点时间研究它的用法与实现原理才行.如果有问题的地方或者有好的建议欢迎提出来. 随着移动开发的热门,Mvvmlight在An ...

  7. UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解

    {x:bind}是随着UWP被推出而被添加的,可以说是Win10 UWP开发专有扩展.虽然 {x:Bind} 缺少{Binding} 中的一些功能,但它运行时所花费的时间和使用的内存量均比 {Bind ...

  8. UWP开发之Mvvmlight实践三:简单MVVM实例开发(图文详解付代码)

    在做MVVM各种框架对比之前,我觉得有必要先自己做一个简单的MVVM实现案例比较好,这样就可以看到自己实现的时候有那些不方便的地方.而各种框架又是怎么解决我们这些麻烦的. 案例介绍:用户登录画面,没有 ...

  9. UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)

    前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...

随机推荐

  1. SQLSERVER走起微信公众帐号全新改版 全新首页

    SQLSERVER走起微信公众帐号全新改版 全新首页 今天,SQLSERVER走起微信公众帐号增加了首页功能 虽然还是订阅号,不过已经对版面做了比较大的修改,希望各位亲用得放心.用得安心O(∩_∩)O ...

  2. 异常处理汇总 ~ 修正果带着你的Net飞奔吧!

    经验库开源地址:https://github.com/dunitian/LoTDotNet 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983 ...

  3. Carousel 旋转画廊特效的疑难杂症

    疑难杂症 该画廊特效的特点就是前后元素有层级关系. 我想很多人应该看过或者用过这个插件carousel.js,网上也有相关的教程.不知道这个插件的原型是哪个,有知道的朋友可以告诉我. 该插件相对完美, ...

  4. 【绝对干货】仿微信QQ设置图形头像裁剪,让你的App从此炫起来~

    最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue ...

  5. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  6. SHA-1算法

    SHA-1.h #ifndef _SHA1_H #define _SHA1_H #include<iostream> using namespace std; //4个函数 #define ...

  7. Ajax部分

    Ajax的概念 AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML),是一种用于创建快速动态网页的技术. 动态网页:是指 ...

  8. WebStorm 2016 最新版激活(activation code方式)

    WebStorm 2016 最新版激活(activation code方式) WebStorm activation code WebStorm 最新版本激活方式: 今天下载最新版本的WebStorm ...

  9. [AlwaysOn Availability Groups]健康模型 Part 1——概述

    健康模型概述 在成功部署AG之后,跟踪和维护健康状况是很重要的. 1.AG健康模型概述 AG的健康模型是基于策略管理(Policy Based Management PBM)的.如果不熟悉这个特性,可 ...

  10. C++常考面试题汇总

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...