上一节完成后,程序已经能够正常运行了。基本功能都完成了。是不是有种很不出的感觉呢。大家都应该听说过会这个墓碑机制了吧。因为window phone 是单任务系统,为后台程序留了5个坑。程序一旦进入后台,就休眠了。被埋在坑里了,就不再运行了。只有你按返回键才能从进入休眠的后台程序。不过,我觉得还有两个问题,第一个就是如果程序在后台,你从启动器启动程序,就是不是按返回键进入程序,那么程序就重新运行了。原来后台的数据就没了,这个是一个用户体验很不好的地方,比如我,就是不喜欢按返回键进入程序的,喜欢从启动器进入后台程序。第二个,就是只有5个坑,一旦你的程序进入后台之后,又有5个程序进入后台,那么你程序埋在坑里的尸体就被抛尸荒野了。再也不能从后台进入了,只能从启动器重新打开。。
那么,怎么解决呢,大家都是喜欢多任务运行的。我们就来模拟一把吧。
模拟之前,先要了解程序的生命周期,这些大家还是百度吧。现在只是简单说下涉及到声明周期要使用到的函数。在App.xaml.cs里面定义了那么4个函数,Launching,Activate,Deactivate,Closing。Launching在程序启动的时候执行。Deactivate在用户点击开始键或者其他导致程序进入后台的操作时候执行,Activate在用户按返回键进入程序的时候执行。closing不用多说,看名字就知道。在程序关闭的时候执行。
在我们这个程序,也就在设置页面的时候如果中断运行进入后台的话才需要恢复,所以相对简单些。我的方案是:定义了两个变量,一个是保存当前所在页面的CurrentPage(保存到PhoneApplicationService),一个是保存是否中断到后台的Recovery。.在Deactivate函数里面保存需要的相关值和上面两个变量到Isolatedstorage。并且在Launching的时候把原来Deactivate保存的值取出来。然后再PageLoad 的时候判断是否是中断恢复。是的话就导航到相关页面,然后恢复界面值。恢复完成,Recovery设置为false,并且保存到Isolatedstorage。
在写代码之前,还要了解的是每个Page里面都有两个事件,OnNavigatedFrom和OnNavigatedTo,前者就是离开页面的时候执行,后者是进入页面的时候执行。重载这两个事件就能用了。
下面先修改App.xaml.cs的代码:(代码中可能没添加命名空间,自己加吧,很简单)
IsolatedStorageSettings iss = IsolatedStorageSettings.ApplicationSettings; private void Application_Launching(object sender, LaunchingEventArgs e) { //如果数据库不存在则创建一个数据库,并且存入相关数据 CreatDB(); #region 把Deactivate保存的相关数据取出来 string temp; if (iss.TryGetValue("LastPage", out temp)) { PhoneApplicationService.Current.State["CurrentPage"] = temp; } int index; if (iss.TryGetValue("cityIndex", out index)) { PhoneApplicationService.Current.State["cityIndex"] = index; } if (iss.TryGetValue("provIndex", out index)) { PhoneApplicationService.Current.State["provIndex"] = index; } #endregion } private void Application_Deactivated(object sender, DeactivatedEventArgs e) { #region 程序即将进入后台,保存中断情况的相关数据 iss["Recovery"] = true; if (PhoneApplicationService.Current.State.ContainsKey("cityIndex")) { iss["cityIndex"] = PhoneApplicationService.Current.State["cityIndex"]; } if (PhoneApplicationService.Current.State.ContainsKey("provIndex")) { iss["provIndex"] = PhoneApplicationService.Current.State["provIndex"]; } if (PhoneApplicationService.Current.State.ContainsKey("CurrentPage")) { iss["LastPage"] = PhoneApplicationService.Current.State["CurrentPage"]; } #endregion } private void Application_Closing(object sender, ClosingEventArgs e) { //退出, iss["Recovery"] = false; }
下面修改MainPage的代码:(PageLoad修改如下)
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { #region 判断是否是中断进入 IsolatedStorageSettings iss = IsolatedStorageSettings.ApplicationSettings; bool recov; if (iss.TryGetValue("Recovery", out recov)) { if (recov) { if (PhoneApplicationService.Current.State.ContainsKey("CurrentPage")) { string temp = Convert.ToString(PhoneApplicationService.Current.State["CurrentPage"]); if (!temp.Equals("MainPage.xaml") && temp.Length > 0) { NavigationService.Navigate(new Uri("/WeatherForecast;component/" + temp, UriKind.Relative)); } else { iss["Recovery"] = false; } } } else PhoneApplicationService.Current.State["CurrentPage"] = "MainPage.xaml"; } else PhoneApplicationService.Current.State["CurrentPage"] = "MainPage.xaml"; #endregion UpdateWeather(); }
我们主要做的是SetPage的中断进入后台的处理,所以,要重载OnNavigatedFrom事件来保存两个Listpicker的SelectedIndex。而且,要在PageLoad的时候判断是否是中断返回,如果是那么就恢复。不是就常规绑定。下面是修改或者增加的代码:
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { #region 判定是否是中断退出后进入,是就恢复数据 IsolatedStorageSettings iss = IsolatedStorageSettings.ApplicationSettings; bool recov; if (iss.TryGetValue("Recovery", out recov)) { if (recov) { if (PhoneApplicationService.Current.State.ContainsKey("CurrentPage")) { string temp = Convert.ToString(PhoneApplicationService.Current.State["CurrentPage"]); if (temp.Equals("SetPage.xaml")) { int provIndex = 0; int cityIndex = 0; if (PhoneApplicationService.Current.State.ContainsKey("provIndex")) { provIndex = Convert.ToInt32(PhoneApplicationService.Current.State["provIndex"]); } if (PhoneApplicationService.Current.State.ContainsKey("cityIndex")) { cityIndex = Convert.ToInt32(PhoneApplicationService.Current.State["cityIndex"]); } RecovListPickerItems(provIndex, cityIndex);//恢复数据 iss["Recovery"] = false;//设置为已经恢复 } else ProvLpDataBind(); } else ProvLpDataBind(); } else ProvLpDataBind(); } else ProvLpDataBind(); #endregion PhoneApplicationService.Current.State["CurrentPage"] = "SetPage.xaml"; } /// <summary> /// 恢复数据的时候绑定city的ItemSource,并且绑定两个ListPicker的当前选项 /// </summary> /// <param name="provIndex"></param> /// <param name="cityIndex"></param> void RecovListPickerItems(int provIndex, int cityIndex) { provincelp.ItemsSource = prov; if (provIndex < 0 || provIndex >= prov.Length) { provIndex = 0; } provincelp.SelectedIndex = provIndex; List<String> l = cityDataBind(prov[provIndex]); if (cityIndex < 0 || cityIndex >= l.Count()) { cityIndex = 0; } citylp.SelectedIndex = cityIndex; } /// <summary> /// 离开页面保存当前两个ListPicker的selectedIndex /// </summary> /// <param name="e"></param> protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedFrom(e); PhoneApplicationService.Current.State["provIndex"] = provincelp.SelectedIndex; PhoneApplicationService.Current.State["cityIndex"] = citylp.SelectedIndex; }
这样,多任务模拟就完成了。无论是从返回键返回程序还是从启动器启动返回程序。都行。不过,如果要做的页面太多,控件太多还真是很麻烦的。
PS:这里的断点调试技巧:由于要测试从启动器恢复程序,但是编译器调试的时候,一旦点击开始按钮进入后台,然后从启动器进入程序,编译器调试就退出了。因为原来的程序已经终结了。其实我们可以这样。点击开始按钮进入后台后,回到编译器点击终止调试,然后再次启动启动。那么这样的话就可以到需要调试的断点了。毕竟这里的判断还是很多,容易出错。其实这个是利用的是Isolatedstorage的特点,模拟器不关闭这个Isolatedstorage的数据是不会丢失的。
这节工程下载:chapter7