Winrt 延迟加载

  • Post author:
  • Post category:其他


很多winrt组件都提供了虚函数或接口供实现,另外,很多winrt的类暴露了一些事件让你的app注册回调。当你的app返回时,windows相信你的代码已经完成,而后windows可能做一些操作。比如:当你的app将要挂起时,windows会给app一个事件,收到通知后,你的app可能想保持一些app的状态到用户的硬盘。当你异步执行这些操作时,线程在异步操作完成前返回。但是,当线程返回到windows时,windows相信你的app成功地挂起了自己然后再挂起你的app的线程,阻止这些线程执行更多代码。

要处理这些,winrt提供了一个机制叫做deferral(延迟),deferral允许指示当操作还没有完成时一个方法返回到windows,阻止windows执行下一步动作,然后,在app完成操作之后,它也完成deferral,告诉windows它现在可以执行下一步动作。Deferral应该只用于某些异步操作时,这里是一个在挂起事件里使用deferral的代码例子:

private async void OnSuspending(object sender, SuspendingEventArgs e) {
// A deferral tells Windows the thread may return but the work is not done
var deferral = e.SuspendingOperation.GetDeferral();
22 Part I Core concepts
// TODO: perform async operation(s) here...
var result = await XxxAsync(); // Thread returns but app is NOT suspended
deferral.Complete(); // Now, tell Windows we’re done (app is suspended)
}

记住:deferral变量指的是RCW,内部指的是winrt组件。所以,如果你的代码不调用complete方法,垃圾回收将最终清除对象,但是当挂起时,如果你的app已经终止,垃圾回收不能运行,winrt定义了几种deferral类,C#程序员相关的是:

Windows.ApplicationModel.SuspendingDeferral
Windows.ApplicationModel.Background.BackgroundTaskDeferral
Windows.ApplicationModel.Calls.LockScreenCallEndCallDeferral
Windows.ApplicationModel.DataTransfer.DataProviderDeferral
Windows.ApplicationModel.DataTransfer.DataRequestDeferral
Windows.ApplicationModel.Search.SearchPaneSuggestionsRequestDeferral
Windows.ApplicationModel.Search.SearchSuggestionsRequestDeferral
Windows.Devices.Printers.Extensions.PrintTaskConfigurationSaveRequestedDeferral
Windows.Devices.SmartCards.SmartCardPinResetDeferral
Windows.Graphics.Printing.PrintTaskRequestedDeferral
Windows.Graphics.Printing.PrintTaskSourceRequestedDeferral
Windows.Media.PlayTo.PlayToSourceDeferral
Windows.Storage.SetVersionDeferral
Windows.Storage.Pickers.Provider.PickerClosingDeferral
Windows.Storage.Pickers.Provider.TargetFileRequestDeferral
Windows.Storage.Provider.FileUpdateRequestDeferral
Windows.UI.StartScreen.VisualElementsRequestDeferral

当你需要延迟执行OS操作到你的代码完成异步操作后时,你可以重写OnSuspending方法,可以像下面这样重写:

private void OnSuspending(object sender, SuspendingEventArgs e) {
// TODO: perform blocking async operation(s) here...
var result = XxxAsync().AsTask.GetAwaiter().GetResult();
} // App is suspended

代码很简单,而且你不得不自问,危害是什么?这段代码阻塞了UI线程,意味着UI可能会无响应。但是在这个case中,用户不与app交互,这也是为什么被挂起在第一个地方。另外, 这个方法的第一个版本使用一个异步方法,让代码更大且降低了app的性能,这个版本不使用async方法,并且挂起时,你的app已经只有几秒去完成,否则windows会终止你的app。因此,让你的代码在这里快速运行,后台任务有一个类似的时间限制,所以你想让后台代码越快越好。

另外,很多deferral类用于非GUI线程,因此,UI无响应不是一个问题,例如,后台任务从不执行GUI线程,所以没理由用BackgroundtaskDeferral 类。

当写代码时,永远记住代码执行的原因,什么线程可以执行这段代码,以及代码执行后会发生什么,清楚了这些后,决定如何最好的方式写代码让系统行为在掌控之中。