namespace Wintellect.WinRT.AppAids { public enum ViewType { None, Main, Hosted, Auxiliary } public enum LaunchReason { PrimaryTile, SecondaryTile, Toast, Proximity } public static class AppAid { private static ApplicationInitializationCallback m_appInitCallback; private static Func<Frame, IActivatedEventArgs, Task<Boolean>> s_deserializeFramePageStateAsync; /// <summary>从Main方法中而非从Application.Start方法中调用该方法</summary> /// <param name="callback">构造应用单例对象的回调函数</param> /// <param name="deserializeFramePageStateAsync">恢复用户会话状态的回调函数。 /// 若应用之前被终止,则在首次发生主视图激活时调用该方法。</param> public static void Start(ApplicationInitializationCallback callback, Func<Frame, IActivatedEventArgs, Task<Boolean>> deserializeFramePageStateAsync = null) { // Invoked via process' primary thread each time the process initializes s_deserializeFramePageStateAsync = deserializeFramePageStateAsync; m_appInitCallback = callback; Application.Start(AppInitialization); } private static void AppInitialization(ApplicationInitializationCallbackParams p) { // 通过主视图线程调用 // 但主视图的CoreWindow & CoreDispatcher对象尚不存在; // 这些对象将在该方法返回后由 Application.Start创建 m_appInitCallback(p); // 创建一个永不会参与垃圾回收的单例应用对象 // 由于基类(Applicaiton)保存着指向它的引用 m_appInitCallback = null; // 允许委托参与垃圾回收 } /// <summary>从App的OnWindowCreated方法中调用该方法,以确定创建了 /// 何种类型的窗口</summary> /// <returns> 用于这种激活类型的视图类型(主视图或宿主视图)</returns> public static ViewType OnWindowCreated(this WindowCreatedEventArgs args) { // 由主视图线程调用一次,并由每个宿主视图线程或辅助线程调用一次 // 注意:你无法辨别发生了哪种类型的激活 (Share, Protocol等) return ViewType; } /// <summary>该方法返回用于某个给定激活类型的视图种类</summary> /// <param name="args">表明发生了何种激活</param> /// <returns>用于这种激活类型的视图类型(主视图或宿主视图)</returns> public static ViewType GetViewType(this IActivatedEventArgs args) { switch (args.Kind) { case ActivationKind.AppointmentsProvider: String verb = ((IAppointmentsProviderActivatedEventArgs)args).Verb; if (verb == AppointmentsProviderLaunchActionVerbs.AddAppointment) return ViewType.Hosted; if (verb == AppointmentsProviderLaunchActionVerbs.ReplaceAppointment) return ViewType.Hosted; if (verb == AppointmentsProviderLaunchActionVerbs.RemoveAppointment) return ViewType.Hosted; if (verb == AppointmentsProviderLaunchActionVerbs.ShowTimeFrame) return ViewType.Main; break; case ActivationKind.Contact: verb = ((IContactsProviderActivatedEventArgs)args).Verb; if (verb == ContactLaunchActionVerbs.Call) return ViewType.Main; if (verb == ContactLaunchActionVerbs.Map) return ViewType.Main; if (verb == ContactLaunchActionVerbs.Message) return ViewType.Main; if (verb == ContactLaunchActionVerbs.Post) return ViewType.Main; if (verb == ContactLaunchActionVerbs.VideoCall) return ViewType.Main; break; case ActivationKind.Launch: case ActivationKind.Search: case ActivationKind.File: case ActivationKind.Protocol: case ActivationKind.Device: case ActivationKind.LockScreenCall: return ViewType.Main; case ActivationKind.ShareTarget: case ActivationKind.FileOpenPicker: case ActivationKind.FileSavePicker: case ActivationKind.CachedFileUpdater: case ActivationKind.ContactPicker: case ActivationKind.PrintTaskSettings: case ActivationKind.CameraSettings: return ViewType.Hosted; } throw new ArgumentException("Unrecognized activation kind"); } public static ViewType ViewType { get { try { CoreApplicationView cav = CoreApplication.GetCurrentView(); return cav.IsMain ? ViewType.Main : (cav.IsHosted ? ViewType.Hosted : ViewType.Auxiliary); } catch { return ViewType.None; } } } /// <summary>当你重载App的虚激活方法(如OnLaunched, OnFileActivated, /// OnShareTargetActivated)之一时,调用该方法 /// 如果在主视图被首次激活时调用,对窗口的Frame对象进行设置 /// 恢复用户会话状态(如果该应用之前被终止)并激活窗口 /// 如果为宿主视图激活而调用,对窗口的Frame对象进行设置,并激活窗口 /// </summary> /// <param name="args">应用激活的原因</param> /// <returns>若之前的状态被恢复,返回true;否则返回false</returns> public static async Task<Boolean> ActivateViewAsync(this IActivatedEventArgs args) { Window currentWindow = Window.Current; Boolean previousStateRestored = false; // 假设之前的状态未被恢复 if (args.GetViewType() == ViewType.Main) { if (currentWindow.Content == null) { currentWindow.Content = new Frame(); } // UI被设置; 主视图首次或第二次被激活 // 如果不是被第一次激活 // PreviousExecutionState == ApplicationExecutionState.Running if (args.PreviousExecutionState == ApplicationExecutionState.Terminated && s_deserializeFramePageStateAsync != null) { // 恢复用户会话状态,因为应用在OS将其终止后又重新启动 previousStateRestored = await s_deserializeFramePageStateAsync(CurrentFrame, args); s_deserializeFramePageStateAsync = null; //允许委托参与垃圾回收 } } else { currentWindow.Content = new Frame(); } currentWindow.Activate(); // 激活主视图窗口 return previousStateRestored; } /// <summary>在调用线程的窗口中返回Frame对象</summary> public static Frame CurrentFrame { get { return (Frame)Window.Current. Content; } } private const String ProximityLaunchArg = "Windows.Networking. Proximity:StreamSocket"; public static LaunchReason GetLaunchReason(this LaunchActivatedEventArgs args) { if (args.Arguments == ProximityLaunchArg) return LaunchReason.Proximity; if (args.TileId == Windows.ApplicationModel.Core.CoreApplication.Id) { return (args.Arguments == String.Empty) ? LaunchReason.PrimaryTile : LaunchReason.Toast; } return LaunchReason.SecondaryTile; } } }
本书的配套代码中有一个AppAid类的改进版本,它支持扩展启动画面及线程日志记录,此外,该版本还拥有一些导航辅助方法。下面是使用该AppAid类的一个应用例程。该段代码调用了一些笔者在本书配套代码中提供的附加方法以简化当应用结束时用户会话状态的保存和恢复。下列代码中最重要的部分是注释,请务必留意:
// 单例应用类;将应用中所有全局数据保存在该类的对象中 public sealed partial class App : Application { // 因DISABLE_XAML_GENERATED_MAIN被定义而调用 public static void Main(String[] args) { // 每次当进程初始化时,通过进程的主线程调用 AppAid.Start(AppInitialization, (f, a) => f.DeserializePageStateAsync(c_FramePageStateFileName, a)); } private static void AppInitialization(ApplicationInitializationCallbackParams p) { // 通过主视图线程调用 // 但主视图的CoreWindow & CoreDispatcher对象尚不存在; // 这些对象将在该方法返回后由 Application.Start创建 // 创建一个单例应用对象;由于基类(Application)保存了指向该对象的引用 //(可通过Application.Current获取),因此将永远不参与垃圾回收。 var app = new App(); } private App() { // 通过主视图线程调用 // 但主视图的CoreWindow & CoreDispatcher对象尚不存在this.InitializeComponent(); this.Resuming += OnResuming; // 当主视图线程从挂起恢复时引发该事件 this.Suspending += OnSuspending; // 当主视图线程被挂起时引发该事件 // TODO: 任何额外的应用初始化 } private void OnResuming(Object sender, Object e) { // 当从挂起中恢复时,通过主视图线程调用 // TODO: 更新UI中的过时状态(新闻、天气、比赛得分等) } private void OnSuspending(Object sender, SuspendingEventArgs e) { // 当应用被挂起或被用户关闭时,由主视图线程调用 // Windows限制应用必须在5秒内完成挂起,否则操作系统会将其终止 // Windows应用商店认证要求挂起在2秒内完成 // TODO: 保存会话状态,以防应用被终止 // (参考 ApplicationData.Current.LocalFolder) // 注意:笔者同步执行该操作,而非使用延迟 this.GetCurrentFrame().SerializePageStateAsync(c_FramePageStateFileName) .GetAwaiter().GetResult(); } protected override void OnWindowCreated(WindowCreatedEventArgs args) { // 由主视图线程调用一次,并由每个宿主视图线程调用一次 // 注意:此时你无法了解激活种类(如Launch, Share, Protocol等) switch (args.OnWindowCreated()) { case ViewType.Main: // TODO: 将你希望为主视图线程/窗口执行的代码放在这里 break; case ViewType.Hosted: // TODO: 将你希望为宿主视图线程/窗口执行的代码放在这里 break; case ViewType.Auxiliary: // TODO: 将你希望为辅助视图线程/窗口执行的代码放在这里 break; } // 备选:为这些事件注册处理方法 Window w = args.Window; // Refers to the view's window (drawing surface) w.Activated += Window_Activated; w.VisibilityChanged += Window_VisibilityChanged; } private void Window_Activated(Object sender, WindowActivatedEventArgs e) { // 每次当其窗口改变激活状态时,由视图线程调用 CoreWindowActivationState activateState = e.WindowActivationState; } private void Window_VisibilityChanged(Object sender, VisibilityChangedEventArgs e) { // 每次当其窗口改变可见性时,由视图线程调用 // 当应用被挂起或关闭时,窗口变为不可见 if (e.Visible) return; } protected override async void OnLaunched(LaunchActivatedEventArgs args) { Boolean previousStateRestored = await args.ActivateViewAsync(); switch (args.GetLaunchReason()) { case LaunchReason.PrimaryTile: if (previousStateRestored) { // 仅恢复应用被终止之前的状态 } else { // 不恢复之前的状态;导航到该应用的首页 // TODO: 跳转到期望的页面 } break; case LaunchReason.SecondaryTile: // TODO: 跳转到期望的页面 break; case LaunchReason.Toast: // TODO: 跳转到期望的页面 break; case LaunchReason.Proximity: // TODO: 跳转到期望的页面 break; } } }