在看这篇文章之前,首先应该对下面的图足够了解,并且动手写过WP7程序。

如果有时间,这篇文章值得一看:http://blog.csdn.net/cc_net/article/details/6665737

 

image

 

上图中要注意,在Launch和Close的时候,并不会触发Activated和Deactived事件。

 

下文中的State泛指Application的State字典和Page的State字典。

本文主要针对的是,程序在进入到生命周期中的某一环节时,如何判断是否需要保存数据。

Mango中引入了Dormant状态,它与tombstoned的区别在于:

Dormant的程序,内存中的所有数据是完好的,activated后无须重新载入,而tombstoned除了在State里保留的数据,其余内存空间全部释放。

所以最容易出问题的地方就是:当内存被释放后,程序从tombstoned状态恢复到运行状态,很多之前创建的对象都是null了,这时候要重新创建这些对象。

至于程序获得的其他系统资源,无论是从dormant还是从tombstoned恢复运行,都需要重新获得这些资源。

 

下图详解了running和dormant状态间切换时发生的事。

image

 

image

 

下面根据MSDN上的建议以及我自己的理解,说一说每个事件处理方法中分别应该做些什么。

注意,需要保存的数据,应该在程序运行时恰当的时机立即保存,而不要等到离开页面或程序的时候才想到保存。如果在离开页面时没有需要保存的东西,那样最好。每个事件处理方法的执行时间都被限制在10秒,如果超过10秒程序会被强制关掉,所以应该尽量减少这些事件处理函数中的工作。

 

Application_Launching

MSDN上对Launching事件的解释:
The Launching event is raised when a new application instance is launched by the user from the installed applications list or from a Tile on Start in addition to other means, such as tapping on a toast notification associated with an application or selecting an application from the Photos Extras menu. When an application is launched this way, it should appear to the user to be a new instance, not a continuation of a previous instance. To help ensure that your application loads quickly, you should execute as little code as possible in the handler for this event. In particular, avoid resource-intensive tasks like file and network operations. You should perform these tasks on a background thread after your application has loaded for the best user experience.

MSDN给的建议:Execute very little code. Do not do resource-intensive operations like accessing isolated storage.

所以还是不要在这个函数中有动作了。

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}

 

Application_Activated

MSDN对Activated事件的解释:

The Activated event is called when the user returns to a dormant or tombstoned application. Applications should check the IsApplicationInstancePreserved property of the event args to determine whether the application is returning from being dormant or tombstoned. If IsApplicationInstancePreserved is true, then the application was dormant and state was automatically preserved by the operating system. If it is false, then the application was tombstoned and the application should use the state dictionary to restore application state. Applications should not perform resource-intensive tasks such as loading from isolated storage or a network resource during the Activated event handler because it increase the time it takes for the application to resume. Instead, these operations should be performed on a background thread after the application has loaded.

MSND的建议是:Check IsApplicationInstancePreserved. If true, do nothing. If false, use data in State to restore application state.

虽然MSDN上不让这不让那,但我个人认为Application_Activated方法调用过后,要保证程序运行所需基本数据加载到位,非常大的数据可以等到页面加载后再异步获取。

需要注意:判断一下是从dormant还是从tombstoned状态恢复的,如果是从tombstoned恢复的,要根据需要从Application的state恢复数据。

还需要注意:由于从tombstoned状态恢复,是全新的一个实例,一切的对象甚至App这个对象都是要重新new的,内存重新分配,所以保存在State中的之前对象的引用(也就是指针)别指望仍然好用。也就是说,之前你有两个引用指向同一个对象,一个存放在state中,一个没有存放在state中,从tombstoned恢复过来以后,它们指向的就是两个不同的对象了。

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    // 处理保存在State中的数据
    if (e.IsApplicationInstancePreserved == false)
    {
        if (PhoneApplicationService.Current.State.ContainsKey("ViewModel"))
        {
            viewModel = (MainViewModel)PhoneApplicationService.Current.State["ViewModel"];
        }
    }

    // 处理保存在IsolatedStorage中的数据
    // ...
}

 

Application_Deactivated

MSDN对Deactivated事件的解释:

The Deactivated event is raised when the user navigates forward, away from your application, by pressing the Start button or by launching another application. The Deactivated event is also raised if your application launches a Chooser. For more information about Choosers, see Launchers and Choosers Overview for Windows Phone. This event is also raised if the device’s lock screen is engaged, unless application idle detection is disabled.

In the handler for the Deactivated event, your application should save any application state so that it could be restored at a later time. Windows Phone applications are provided with the State object, which is a dictionary you can use to store application state. If the application is tombstoned and then reactivated, this state dictionary will be populated with the data you saved in Deactivated. Because this data is present in memory, you can use it to restore state without resource-intensive file operations.

It is possible for an application to be completely terminated after Deactivated is called. When an application is terminated, its state dictionary is not preserved. For this reason, you should also store any unsaved state that should be persisted across application instances to isolated storage during the Deactivated event.

对应Deactivated事件handler中所做的事情,就是把该保存的都保存好,以便下次恢复的时候使用,当然也有可能永远不会恢复而是被close掉,所以在deactivated时保存的数据一定要涵盖所有close时应保存的数据。

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    // 处理保存在State中的数据
    PhoneApplicationService.Current.State["ViewModel"] = viewModel;

    // 处理保存在IsolatedStorage中的数据
    // ...
}

 

Application_Closing

MSDN对Closing事件的解释:

The Closing event is raised when the user navigates backwards past the first page of an application. In this case, the application is terminated and no state is saved. In the Closing event handler, your application can save data that should persist across instances. There is a limit of 10 seconds for applications to complete all application and page navigation events. If this limit is exceeded, the application is terminated. For this reason, it is a good idea to save persistent state throughout the lifetime of the application and avoid having to do large amounts of file I/O in the Closing event handler.

只有用户在应用程序首页按back时触发Closing事件,其他情况下则都不会触发这个事件。有些时候应用程序被终止也不会触发close事件,例如当超过了5个程序被打开(dormant和tombstoned都算是被打开)系统为节省资源会强制终止掉最早打开的程序,不触发Closing事件,再例如重新启动了该应用程序则会终止掉之前的程序而不触发closing,也就是说,Closing事件不见得一定会被触发。如果要保存数据的话,应当写一个方法,让closing和deactived的handler都调用这个方法。

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
    // 处理保存在IsolatedStorage中的数据
    // ...
}

 

OnNavigatedFrom

MSDN上解释得非常好:

Whenever this method is called, your application should store the page state so that it can be restored if the user returns to the page. The exception to this is backward navigation. The NavigationMode property can be used to determine if the navigation is a backward navigation, in which case there is no need to save state because the page will be re-created the next time it is visited.

In some cases, you may want to save state in the OnNavigatingFrom(NavigatingCancelEventArgs) method as well. In particular, you will need to do this to store the state of a MediaElement control.

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);

    if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
    {
        // 保存UI相关数据到本页面的State中
        this.State["someText"] = textBox1.Text;
    }
}
可以在OnNavigatingFrom方法中取消这次导航,例如通过事件参数的属性,我们让可以取消的、并且是打开一个新的页面而不是Back到上个页面的、并且Uri中包含某个字符串的,并且阻止这次导航。
protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
    if (e.IsCancelable 
        && e.NavigationMode == System.Windows.Navigation.NavigationMode.New
        && e.Uri.ToString().Contains("SomePage.xaml"))
    {
        e.Cancel = true;
    }

    base.OnNavigatingFrom(e);
}
如果要取消按Back键的行为,则一般不在OnNavigatingFrom方法中取消,而是在OnBackKeyPress方法中令e.Cancel = true。
 

OnNavigatedTo

MSDN上的解释:

The OnNavigatedTo(NavigationEventArgs) method is called when the user navigates to a page. This includes when the application is first launched, when the user navigates between the pages of the application, and when the application is relaunched after being made dormant or tombstoned. In this method, applications should check to see whether the page is a new instance. If it is not, state does not need to be restored. If the page is a new instance, and there is data in the state dictionary for the page, then the data should be used to restore the state of the page’s UI.

在OnNavigatedTo方法中,MSDN的建议是:

Check whether the page is a new instance. If not, the state is automatically intact. Otherwise, if there is data in State, use it to restore UI.

但我觉得MSDN上说得有问题!如果是我理解错了,多谢指正。

按照我的理解,OnNavigatedTo方法应该是以下逻辑:

if 是程序内的GoBack操作或者是从dormant状态恢复,都无需读取State,因为这两种情况内存都是完好的。

else if 是从tombstoned状态恢复,则需要读取State,因为这个page是新建的实例了,对象需要初始化。

但这里有个问题,通过什么来判断是从dormant状态恢复还是从dormant状态恢复的,两者的e.NavigationMode都是Back。解决方法是:给页面类加一个isNewPageInstance成员变量,默认置为true表明是新建的实例,在OnNavigateTo里置为false表示已经不是新实例了。

代码如下:

bool isNewPageInstance = true;

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    if (isNewPageInstance && e.NavigationMode != System.Windows.Navigation.NavigationMode.New)
    {
        // 既不是新实例,又不是打开一个新页面的navigation操作,只能是从tombstoned状态恢复的了
         if (this.State.ContainsKey("text"))
        {
            textBox1.Text = (string)this.State["text"];
        }
    }
    
    isNewPageInstance = false;
}

 

关于Task

Launcher, Chooser这些Task也会使得当前应用被deactivated,之后如果从tombstoned状态恢复,要特别注意,因为如果这些Task是局部变量,那么恢复之后就不复存在了。因此要让Task是成员变量,并且在构造函数中或声明的时候初始化它,这样才能在正确的时机调用Completed事件handler。例如像下面这样:

PhoneNumberChooserTask phoneNumberChooserTask = new PhoneNumberChooserTask();

public MainPage()
{
    InitializeComponent();
    phoneNumberChooserTask.Completed += new EventHandler<PhoneNumberResult>(phoneNumberChooserTask_Completed);
}

 

 

一些先后顺序的总结

顺序

来到页面

离开页面

1 NavigatedTo事件触发

NavigatedFrom事件触发

2

控件Loaded事件触发
(先子元素后父元素)

控件UnLoaded事件触发
(先父元素后子元素)

比较有意思的是,多层嵌套控件的load以及unload顺序和我原来想的不一样,是子控件先load,然后是父控件load。父控件先unload,然后是子控件load。

posted on 2011-11-29 00:16  MainTao  阅读(694)  评论(0编辑  收藏  举报