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

案例下载: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. C# MVC 5 - 生命周期(应用程序生命周期&请求生命周期)

    本文是根据网上的文章总结的. 1.介绍 本文讨论ASP.Net MVC框架MVC的请求生命周期. MVC有两个生命周期,一为应用程序生命周期,二为请求生命周期. 2.应用程序生命周期 应用程序生命周期 ...

  2. ExtJS 4.2 Grid组件的单元格合并

    ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ...

  3. WPF 微信 MVVM 【续】发送部分QQ表情

    今天主要记录的就是发送QQ表情, WPF 微信 MVVM里写了,后期为了发送QQ表情,需要把TextBox替换为RichTextBox,接下来就说说替换的过程. 一.支持Binding的RichTex ...

  4. Ngrok让你的本地Web应用暴露在公网上

    1.Ngrok介绍 Ngrok是一个反向代理,通过在公共的端点和本地运行的Web服务器之间建立一个安全的通道.Ngrok可捕获和分析所有通道上的流量,便于后期分析和重放.简单来说,利用 Ngrok可以 ...

  5. (一)开篇—杂谈WebGIS

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.前言 我相信大家对百度地图,谷歌地图等相关应用已经是非常熟悉了.通过 ...

  6. Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误——SHH框架

    SHH框架工程,Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误 1.查看配置文件web.xml中是否配置.or ...

  7. 2Sum

    用哈希表(unordered_map)使得时间复杂度从O(n*n)降到O(n),空间复杂度从O(1)增到O(n):一边找一边插入哈希表 注意 在C++11以前要使用unordered_map需要 #i ...

  8. Zabbix 漏洞分析

    之前看到Zabbix 出现SQL注入漏洞,自己来尝试分析. PS:我没找到3.0.3版本的 Zabbix ,暂用的是zabbix 2.2.0版本,如果有问题,请大牛指点. 0x00 Zabbix简介 ...

  9. [PHP源码阅读]count函数

    在PHP编程中,在遍历数组的时候经常需要先计算数组的长度作为循环结束的判断条件,而在PHP里面对数组的操作是很频繁的,因此count也算是一个常用函数,下面研究一下count函数的具体实现. 我在gi ...

  10. android内部培训视频_第三节(3)_常用控件(ViewPager、日期时间相关、ListView)

    第三节(2):常用控件之ViewPager.日期时间相关.ListView  一.ViewPager 实例:结合PagerAdapter滑动切换图片  二.日期时间相关:AnalogClock\Dig ...