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

案例下载: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. CMS模板应用调研问卷

    截止目前,已经有数十家网站与我们合作,进行了MIP化改造,在搜索结果页也能看到"闪电标"的出现.除了改造方面的问题,MIP项目组被问到最多的就是:我用了wordpress,我用了织 ...

  2. Sublime配置python开发环境

    Package Control Package Control 是Sublime 里直接安装附加插件的包管理器.可以通过以下步骤手动安装: 1.点击Preferences > Browse Pa ...

  3. 深入研究Visual studio 2017 RC新特性

    在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...

  4. Angular源码分析之$compile

    @(Angular) $compile,在Angular中即"编译"服务,它涉及到Angular应用的"编译"和"链接"两个阶段,根据从DO ...

  5. AbpZero--2.如何启动

    1.直接启动 VS中直接启动 2.IIS站点 IIS中配置一个站点来启动(推荐) 3.登录 系统默认创建2个用户 默认用户名:admin 密码:123qwe 租户:Default  默认用户名:adm ...

  6. cocos2dx调用浏览器打开网址

    安卓端cocos2dx/platform/android路径下CCApplication.h: virtual void openURL(const char* pszUrl); CCApplicat ...

  7. Nginx反向代理,负载均衡,redis session共享,keepalived高可用

    相关知识自行搜索,直接上干货... 使用的资源: nginx主服务器一台,nginx备服务器一台,使用keepalived进行宕机切换. tomcat服务器两台,由nginx进行反向代理和负载均衡,此 ...

  8. 总结iOS开发中的断点续传那些事儿

    前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...

  9. Android 扫描条形码(Zxing插件)

    使用Android Studio 一.在build.gradle(Module:app)添加代码  下载,调用插件 1 apply plugin: 'com.android.application' ...

  10. Linux模块编程框架

    Linux是单内核系统,可通用计算平台的外围设备是频繁变化的,不可能将所有的(包括将来即将出现的)设备的驱动程序都一次性编译进内核,为了解决这个问题,Linux提出了可加载内核模块(Loadable ...