UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理
最近比较忙有一段时间没有更新了,再接再厉继续分享。
案例下载: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中断挂起以及复原处理的更多相关文章
- UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件
在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...
- UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中
前一段开发UWP应用的时候因为系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)浪费了不少时间.现象就是在手机版的详 ...
- UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享
在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...
- UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)
最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...
- UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)
最近UWP开发在海外很潮流,随着微软收购Xamarin,我们这些C#程序员也可以靠这杆小米枪挑战Android,IOS平台了. 那我们为什么选择MVVM做UWP开发?MVC,MVP,MVVM他们之间到 ...
- UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)
最近一直在做UWP开发,为了节省成本等等接触到MVVMlight,觉得有必要发点时间研究它的用法与实现原理才行.如果有问题的地方或者有好的建议欢迎提出来. 随着移动开发的热门,Mvvmlight在An ...
- UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解
{x:bind}是随着UWP被推出而被添加的,可以说是Win10 UWP开发专有扩展.虽然 {x:Bind} 缺少{Binding} 中的一些功能,但它运行时所花费的时间和使用的内存量均比 {Bind ...
- UWP开发之Mvvmlight实践三:简单MVVM实例开发(图文详解付代码)
在做MVVM各种框架对比之前,我觉得有必要先自己做一个简单的MVVM实现案例比较好,这样就可以看到自己实现的时候有那些不方便的地方.而各种框架又是怎么解决我们这些麻烦的. 案例介绍:用户登录画面,没有 ...
- UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)
前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...
随机推荐
- 【小程序分享篇 二 】web在线踢人小程序,维持用户只能在一个台电脑持登录状态
最近离职了, 突然记起来还一个小功能没做, 想想也挺简单,留下代码和思路给同事做个参考. 换工作心里挺忐忑, 对未来也充满了憧憬与担忧.(虽然已是老人, 换了N次工作了,但每次心里都和忐忑). 写写代 ...
- 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)
前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...
- Javascript实用方法二
承接上一篇, Object keys object的keys方法能够获取一个给定对象的所有键(key/属性名)并以数组的形式返回.这个方法可以用于键的筛选.匹配等. var basket = { st ...
- 【知识必备】RxJava+Retrofit二次封装最佳结合体验,打造懒人封装框架~
一.写在前面 相信各位看官对retrofit和rxjava已经耳熟能详了,最近一直在学习retrofit+rxjava的各种封装姿势,也结合自己的理解,一步一步的做起来. 骚年,如果你还没有掌握ret ...
- 将DataTable中的某列转换成数组或者List
string[] arrRate = dtRate.AsEnumerable().Select(d => d.Field<string>("arry")).ToA ...
- Hibernate中事务的隔离级别设置
Hibernate中事务的隔离级别,如下方法分别为1/2/4/8. 在Hibernate配置文件中设置,设置代码如下
- 安卓自定义组合控件--toolbar
最近在学习安卓APP的开发,用到了toolbar这个控件, 最开始使用时include layout这种方法,不过感觉封装性不好,就又改成了自定义组合控件的方式. 使用的工具为android stud ...
- Android之数据存储的五种方法
1.Android数据存储的五种方法 (1)SharedPreferences数据存储 详情介绍:http://www.cnblogs.com/zhangmiao14/p/6201900.html 优 ...
- OpenGL ES 3.0: 图元重启(Primitive restart)
[TOC] 背景概述 在OpenGL绘制图形时,可能需要绘制多个并不相连的图形.这样的情况下这几个图形没法被当做一个图形来处理.也就需要多次调用 DrawArrays 或 DrawElements. ...
- pycharm2016.3.1激活及汉化
pycharm快捷键 PyCharm设置python新建文件指定编码为utf-8 Python | 设置PyCharm支持中文 0, 注册码 43B4A73YYJ-eyJsaWNlbnNlSWQiOi ...