内容預告:
- 啟動(Launching)和關閉(Closing)
- Deactviating和Activating
- Dormant和Tombstoned
- 用模拟器模拟這些事件
- 空閑檢測
- 快速恢複
- 生命周期規劃
- 頁面導航和後退棧
Windows Phone應用程式在不同的狀态間過渡的圖示如下:

程式從點選開始螢幕的圖示上啟動,使用者可以關閉程式,系統可能挂起你的程式(在程式失去焦點的時候),挂起的程式可能會進入墓碑,程式可能從挂起狀态激活。
當使用者啟動一個新的程式的執行個體時,之前的挂起狀态會丢失。比如當運作一個程式時,點到了Home鍵,再點選開始螢幕的圖示上啟動程式,按Home鍵之前挂起狀态會丢失,正确的做法是按住Back鍵不動選擇那個程式恢複狀态。
在Windows Phone 8中,可以用快速恢複功能(Fast Application Resume)重新啟動挂起的程式。
應用程式的生命周期事件:
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
Windows Phone應用程式環境會通過一些事件通知上述狀态,在項目模闆裡的App.xaml裡訂閱了事件,并在App.xaml.cs處理了,初使化情況下處理邏輯是空的。
啟動和關閉:Launching 和 Closing
當程式啟動時,Applcation_Launching會被調用,程式結束時,Application_Closing會被調用,調試器在程式停止後還會繼續運作,是以需要手動結束。
程式的Deactivation和Reactivation:
出于省電的考慮,任何時間隻有一個程式運作在前台,使用者可以deactivate程式也可以reactivate它們,程式需要處理Activated和Deactivated事件。
程式的休眠(Dormant):
使用者可以随時休眠應用程式,然後運作其他程式,這個時候Application_Deactivated函數被調用,電話突然打來時,程式也會休眠,鎖屏時程式也會休眠一段時間。使用者也可以恢複休眠的程式。但是不保證一定能從休眠狀态中恢複。
從休眠中重新激活:
處理休眠:
當程式被休眠時必須盡可能地儲存資料因為程式可能會關閉,如果使用者不再通過長按Back回到那個程式,Application_Deactivated就相當于Application_Closing了。
你的程式有5秒鐘的機會清理現場(儲存資料),之後程式會被從記憶體中清除掉。當程式長按Back恢複時,它會自動恢複到Deactivated時的那個頁面,這是作業系統幫我們做的,但是,頁面的内容并不會自動儲存。
從休眠到墓碑:
一個程式會和其他程式一起在記憶體裡休眠,如果作業系統的記憶體不夠用了會釋放最先休眠的程式的緩存狀态,這個過程叫做“墓碑化”。
頁面導航曆史和緩存狀态都被墓碑了的程式維護着。
當一個休眠了的程式恢複時,緩存狀态會重新加載,程式會萬利到它離開之前的那個頁面。
當一個墓碑了的程式恢複時,它會重新開機離開之前的頁面,但是所有的程式狀态會丢失,你需要重新加載。
一個程式可以決定從哪個狀态激活。
從墓碑狀态重新激活:
從休眠還是墓碑恢複的?可以在恢複前做一個判斷
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
// Dormant - objects in memory intact
}
else
{
// Tombstoned - need to reload
}
}
狀态和墓碑:
當程式從休眠恢複時,程式會準确地恢複到離開時的頁面,所有的對象和它們的狀态都在記憶體裡,你可能需要寫一些邏輯來重置依賴于時間或網絡的調用代碼。
當程式從墓碑恢複時,程式隻會恢複到離開時的頁面,但所有對象和它們的狀态都丢失了,是以需要重新加載控件的資料,這就是為什麼需要儲存狀态,程式從記憶體中移除了系統也維護着狀态。
當程式的一個新的執行個體啟動時,狀态是空的。如果一個先前的程式挂起了,那麼那個程式存儲的狀态字典會丢失。
狀态字典:
PhoneApplicationService.Current.State["Url"] = "www.robmiles.com";
休眠程式的狀态資訊存在一個狀态字典裡,如上述代碼。
可以在Application_Deactivated函數裡存儲,然後在頁面激活時讀取。
是以Application_Deactivated有兩件事情要做,儲存資料以防程式不能重新激活,儲存狀态資料以保證程式恢複到正确的狀态。
調試墓碑狀态:
當休眠的時候,可以在Visual Studio裡設定強制程式墓碑化(從記憶體中移除)。
你應該把這個做為測試引擎的一部分。
你也可以用模拟器操作鎖屏,也可以讓程式進入休眠。
模拟器操作面闆:
模拟器可以模拟鎖屏和解鎖,會讓程式進入Deactivated和Activated狀态。
空閑處理:
Windows Phone作業系統會檢測到程式的空閑狀态,當手機進入空閑狀态,會讓手機鎖屏。
使用者可以配置多長時間後開始檢測,也可以關閉檢測。
當程式被檢測到是空閑的話,将會進行Deactivated狀态,當使用者解鎖後,手機會Activated。
程式可以禁止檢測空閑,那樣就可以在鎖屏下運作了。
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
這樣即使手機鎖屏了也會有什麼Activated和Deactivated了。
但如果是使用者按了Start鍵還是會deactivated。
禁止空閑檢測也不能同時運作兩個程式。
檢測遮擋事件:比如toast消息,鎖屏,來電等。
App.RootFrame.Obscured += RootFrame_Obscured;
...
void RootFrame_Obscured(object sender, ObscuredEventArgs e)
{
}
程式可以訂閱遮擋事件,也可以訂閱遮擋移除的事件。
導航和後退棧:
Windows Phone程式的導航模型使用起來很友善,可以通過連結到其他頁面,可以通過後退鍵到上一個頁面。
系統維護了一個後退的棧,當跳轉到另一個頁面時,之前的頁面位址會被壓入棧,當後退時,目前頁面會出棧。
後退棧和Activated/Deactivated:
當程式deactivated時,系統會保持後退棧,包括目前的頁面,但隻有頁面位址被儲存,内容并不儲存。
程式必須在OnNavigatedTo和OnNavigatedFrom事件裡建構頁面和儲存資料。
從不同的地方進入程式:
一般情況下程式會在運作時顯示首頁,而且這個首頁會被壓入棧,且後退時會回到首頁。
有些情況下可以不用進入首頁,比如(第二磁貼,提醒,檔案關聯,搜尋,錢包,語音),但同時帶來新的問題
後退棧的問題:
有這樣一個場景,當使用者在後退棧裡憶壓入很多頁面時,進入相冊選擇了相片,然後在相冊界面按後退鍵,會首先回到程式進入相冊前的最後一個頁面,再後退,程式會退出。
這裡需要特殊處理後退棧,比如模拟後退棧,因為在頁面從後退棧清除時會有一個事件觸發。
模拟後退棧:
private void PurgeBackStackButton_Click(object sender, RoutedEventArgs e)
{
while (NavigationService.CanGoBack)
NavigationService.RemoveBackEntry();
}
程式可以質問後退棧是否可以後退,也可以把棧内所有頁面清除光。
程式可以枚舉後退棧的所有頁面,但不能将頁面壓棧。
OnRemovedFromJournal:
protected override void OnRemovedFromJournal(JournalEntryRemovedEventArgs e)
{
base.OnRemovedFromJournal(e);
}
在程式運動時,一個頁面希望能夠從子頁面傳回,但如果它從後退棧移除後就不是這個流程了。
可以用OnRemovedFromJournal事件捕獲當本頁面從後退棧裡移除時做一些處理。
快速恢複FAR(Fast Application Resume):這個功能可以讓程式在激活時速度更快。需要手動在WMAppManifest.xml裡編輯
<Tasks>
<DefaultTask Name ="_default" NavigationPage="MainPage.xaml">
<BackgroundExecution>
<ExecutionType Name="LocationTracking" />
</BackgroundExecution>
</DefaultTask>
</Tasks>
FAR的兩種下場:
通過把程式注冊為LocationTracking可以實作FAR,但LocationTracking我們未必會用。
如果程式中用到了定位的類,那麼當程式deactivated時會繼續一直在背景運作,如果重新運作程式,程式會快速從目前休眠的狀态恢複。
如果程式中沒用定位的類,那麼那麼當程式deactivated時,程式像平常一樣,如果重新運作程式,程式會從之前休眠的狀态恢複。
标準的重新啟動程式如上圖所示:
真正的有背景定位的行為如上圖所示:
背景無定位程式在運作的FAR程式如上圖所示:
為什麼不在所有程式上用FAR?
因為用的時候要非常小心頁面導航的使用者體驗,
非FAR的程式中,如果啟動程式到page2,那麼page2是後退棧裡唯一的頁面,按後退會退出程式。
在FAR的程式中,如果啟動程式到page2,但是之前挂起的程式的後退棧裡有mainpage,page1,page2,page3,那麼當運作新程式執行個體時,按後退鍵的話,程式會以mainpgae,pgae1,page2,page3,page2的程式退出。
FAR程式中主磁貼的行為:
使用者在點選程式的磁貼後,程式會進入首頁面,非FAR的程式會這樣,之前沒有挂起執行個體的FAR的程式也會這樣。
如果是恢複到程式呢?應該是怎麼樣?恢複到首頁還是最後一個頁面?如果恢複到最後一頁那怎麼進入首頁?這裡有一個建議是在頁面上加一個回到首頁的連結。
檢測FAR程式的恢複行為:
在真正的有背景運作定位的程式中,當程式被送到背景後會繼續運作,程式會觸發PhoneApplicationService.RunningInBackground事件而不是Application_Deactivated事件。
當FAR程式重新啟動時,會以NavigationMode.Reset的方式導航到後退棧最頂部的頁面,然後會以NavigationMode.New的方式定位到新啟動的URL的頁面,可以決定是否上傳頁面的後退棧,或取消導航到新頁面。
清除FAR程式之前的執行個體運作的頁面:在App.xaml.cs裡加入
private void InitializePhoneApplication() { ... // Handle reset requests for clearing the backstack RootFrame.Navigated += CheckForResetNavigation;
...
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
}
private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again
RootFrame.Navigated -= ClearBackStackAfterReset;
// Only clear the stack for 'new' (forward) and 'refresh' navigations
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;
// For UI consistency, clear the entire page stack
while (RootFrame.RemoveBackEntry() != null) { } // do nothing
}