Windows Phone 8–Launch Apps與Fast Resume
過去在WP7.0與WP7.1的時候對於應用程式的 execution model(運行模式)與切換應用程式時對於App發生什麼事情,
我們知道存在著墓碑模式(Tombstone)與7.1的休眠模式(Dormant),如<Windows Phone 7 - 簡介新運作模式 - Dormant>該篇所述。
到了Windows Phone 8又有那些新的Model與機制,支援應用程式如何執行、在背景下傳送資料或完成Background agents的任務。
以下便透過概要的方式去說明相關Windows Phone Application運作的模式。
〉Launching your App:
在WP下要執行自己的App,來自幾個情境:(1) 用戶從App List或Screen home中點擊;(2) 點擊Toast訊息或Reminder/Alert;
(3)由Search quick card(說明)串聯;(5)其他相關註冊的Extensions,…等,方式非常多,透過下圖馬上可以理解:
這些方法有些是WP8開始才支援的,例如:Wallet(電子錢包)、File/URI Association、Lens Picker、Picture Viewer,詳細的說明如下:
Extensibility point |
Description |
For more info |
Search quick cards |
Launch with context from product cards, place cards, movie cards, and event cards. |
|
Wallet |
Incorporate wallet functionality in your app. |
|
Music + Videos Hub |
Launch to play your app's media from the Music + Videos Now playing, History, and New lists. |
How to integrate with the Music and Videos Hub for Windows Phone |
Live Tiles |
Launch from a secondary Tile to an experience in your app that the Tile represents. |
|
Picture viewer |
Launch from the picture viewer to share, edit, or open the photo. |
|
Photos Hub |
Appear in the Photos Hub, a convenient place for photo apps. |
|
File association |
Launch to handle a file when the user wants to open a file from another app. |
Auto-launching apps using file and URI associations for Windows Phone 8 |
Lens picker |
Launch to display the viewfinder of your lens app. |
|
URI association |
Launch from a URI that another app sends to invoke your app. |
Auto-launching apps using file and URI associations for Windows Phone 8 |
以上這些主題其實我也有寫過大家如果看了英文覺得還是不懂,可以參考我其他篇文章希望能對各位有所幫助。
另外,根據<Launching, resuming, and multitasking for Windows Phone>也提供針對一些啟動Apps的方式,做了一些說明如下:
(1) 透過已知的特定Publisher ID來啟動已安裝相同ID的Apps:
可透過 Windows.Phone.Management.Deployment 命名空間下的APIs得到電話中已安裝具有相同Publisher ID的Apps。
取得的這些Apps一樣可透過該APIs來執行他們。透過下方的程式範例來說明:
1: // 先至每個應用程式的WMAppManifest.xml
2: // 修改<App />下的 Publisher屬性設定成固定的值
3:
4: IEnumerable<Package> apps =
5: Windows.Phone.Management.Deployment.InstallationManager.
6: FindPackagesForCurrentPublisher();
7: apps.First().Launch(string.Empty);
不過我試了一下,我發現即使相同的Publisher也還是只有捉到自己目前執行的那個,所以有待確認其用法。
(2) 透過URI的方式啟動Apps:
透過URI啟動Apps採用的是熟悉的Launcher.LaunchUriAsync(Uri),透過指定"特別的URI"來啟動已安裝的App。
在<Windows Phone 8 - App2App Communication - Protocol (URI Schema)>與
<Windows Phone 8 - App2App Communication - File associations>
有比較詳細的說明。另外,可透過下圖了解可透過URI來啟動的:
從本身的App串聯至其他的App,在用戶按back鍵返回時仍然可以回到本身的App之中。
(3) Background agents and file transfers:
在WP環境同時只有一個App在前景,但可以為App建立支援背景模式下繼續Works或傳遞檔案的方法。
詳細可參考<Windows Phone 7 – Background Schedule Tasks>與<Windows Phone 7 – Background File Transfer>,以及
<Windows Phone 7 – Background Audio Playlist Application>。前二篇分別介紹Background Agent支援背景下定期執行指定的任務,
以及背景下上傳/下載檔案,最後一篇是說明如何開發一個支援背景播放音樂的說明。這些特性支援讓App可以有更豐富的功能,
也達到一些背景模式運行的需求。
〉App activation and deactivation:
這是本篇的重點,在Windows Phone環境同一時間只有一個App可以在前景(Foreground)。
當用戶點擊Start或由App串聯其他App時,該App會被暫存於記憶體中,轉換成熟悉的墓碑模式(Tombstone)與7.1的休眠模式(Dormant),
如<Windows Phone 7 - 簡介新運作模式 - Dormant>該篇所述有關墓碑模式與休眠模式的切換與還原時的機制。
但到了Windows Phone 8增加了一個新的特性:Fast App Resume。
其用途在Windows Phone 8的環境下,當用戶從目前執行的App離開時,該App會被暫停並保存在記憶體中。
此時,如果用戶按下Back鍵或透過Task Switcher啟動時,該App將會自動Resume而不是重啟。
換句話說,也就是允許當應用程式的Instance已被建立,但被保存於記憶體中時,當用戶切換回原來的App時,不會像過去重新啟動App,
而是延用既有的Instance加快用戶得到該App的時間。
主要的原由就是記憶體保存了Instance,對於此機制被稱作為:Fast App Switching (FAS)。
因為過去WP7.1開發或未設定該特性的Apps,即時剛開過此App只是按Start鍵離開,當用戶又重新點擊App List中相同的App或Tile,
old instance會被刪除而重新啟動一個新的instance,這是很慢的,所以有些App就會自己做在activation/deactivation的處理以保存狀態。
然而,在WP8開發的App時加上這個特性,用戶將會有不同的使用經驗。
但要注意,該特性使用的情境在於instance還在記憶體中,所以當instance被刪掉了過去處理activation/deactivation的程式還是要寫,
詳細可參考<App activation and deactivation for Windows Phone>。
那麼要達到這樣的效果要怎麼實作呢?以下分成幾個步驟來說明:
(A) 在WMAppManifest.xml啟動Fast Resume的功能:
開啟WMAppManifest.xml找到<DefaultTask />標籤,增加一個新的屬性:ActivationPolicy,給值為:Resume。
這個特性支援 XAML apps、Direct3D apps與Direct3D / XAML apps三種,其範例如下:
1: <!-- for XAML apps -->
2: <DefaultTask Name="_default" NavigationPage="MainPage.xaml"
3: ActivationPolicy="Resume"/>
4:
5:
6: <!-- for Direct3D apps -->
7: <DefaultTask Name="_default" ImagePath="PhoneDirect3DApp1.exe" ImageParams=""
8: ActivationPolicy="Resume"/>
(B) 針對Fast Resume加上最佳化的Experience:
如果App要支援Fast Resume的特性,需要針對App在不同執行進入點的處理情境,如下:
‧Primary launch point:
=>代表用戶會直接進入App中的main page。包括:在Start Screen中App的Primary Tile、App List或Games Hub的App Name;
‧Deep link navigates:
=>用戶會進入App中的other page。 包括:Secondary Tiles、Toast notification、Search的延伸App或People Hub的串聯;
在預設Windows Phone環境下,針對每一個App執行時會有一個維護Pages執行的History,記錄目前用戶瀏覽的Page與過去經過的Page,
讓用戶可在Back鍵觸發時回到上一個瀏覽的Page。
Fast Resume下,需注意當App被Resume時,系統會再建立一個新的Page Instance以因應當前進入點所要的Page,
該Page被放置於舊History(backstack)的最上層。
當這個情境發生時:
‧在程式裡可選擇是否清掉舊History中的Page,只保留最新的Page:
=>如果是這情境,用戶resume後會覺得該App是全新的,因為當按下Back鍵時,畫面會回到Start Screen或是上一個App。
‧在程式選擇cancel navigation到新建立的page:
=>如果是這情境,用戶將回到離開程式前的瀏覽的Page,則是舊History(backstack)中最上面一個Page,
離開前的Session或狀態都會被還原。
如果上述的文字比較難理解的話,透過<Fast app resume for Windows Phone 8>準備的圖來說明:
‧假設一個App裡有三個Page:
-> Main Page
-> Page 1 (由Main Page導向產生)
-> Page2 (由Secondary tile指定的URI產生);
‧情境1:啟動App,用戶可看到Main Page,接著進入Page1,按下Start Button回到Start Screen,如下圖:
‧情境2:用戶點擊Primary Tile進入該App時,系統建立一個新的Main Page,並且新Main Page放至backstack的最頂端。如下圖:
由於用戶拿到新的Main Page,此時用戶按下back鍵要離開程式時,將會經過Page 1,再看到舊的Main Page,最後離開。
這可能不是用戶預期的,因為用戶點擊App看到Main Page,他們會認為應該按一次back鍵就要離開程式了。
在這個情境下App本身需要識別如何提供用戶預期的結果,例如:如果用戶離開很長一段時間回到App,則可以從Main Page
開始,相反的如果離開的時間很短,則可以回到離開前的畫面。
‧情境3:提供“fresh instance” experience,當App Resume時移除所有存在backstack中的Pages,如下圖:
=>前提是用戶先啟動App進入Main Page,接著Page 1,再按Start Button回到Start Screen,同情境1。
可以看到其他Page已被清除,用戶只會看到新的Main Page,按下Back鍵則立即離開App。
‧情境4:“resume” experience,取消建立新Main Page,回到離開前的Page 1,如下圖:
此情境讓用戶回到離開前的Page 1,這是與 fresh instance最大不同的地方,也比較接近用戶習慣的期待。
‧情境5:”deep link”,先執行情境1,接著按下Secondary Tile進入Page 2,如下圖:
如果用戶執行完情境1,接著按下Secondary Tile啟動App時,系統會導向Tile指定的Page 2,並將Page 2放置於
backstack的頂端,此時用戶如果按下Back鍵,他將會先回到Page 1,接著Main Page,最後才離開App。
此情境下也許不是用戶所期待的情境,因此,建議在導向Page 2之前,應先做clear backstack的動作。
(C) 如何清除backstack:
上述提及清除backstack的動作,往下便來看看程式如何撰寫。需注意以下的觀念:
當App被Resume時,系統會涉及RootFrame的Naviagted的事件,並夾帶Reset的值:NavigationMode。
該Reset的值代表App處於Relaunch.At狀態,可用於呼叫RemoveBackEntry()清除backstack中的資料。程式碼如下:
1: // 透過while loop遂一清掉backstack中的page
2: while (RootFrame.RemoveBackEntry() != null);
做完上述的remove後,新的Page將被建立並且導向一個新的NavigationMode。
[補充]
‧NavigationMode:識別目前Navigation被發生的狀態。
Member name | Member name | |
New | 導向新的內容。 此值被使用於當Navigate方法被呼叫、Source屬性被設定。 它也可被用於任何Navigation請求,包括從WebBrowser而來。 |
|
Back | 導向至back navigation history中最近使用的內容。此值被使用於當GoBack方法被呼叫。 | |
Forward | 導向至forward navigation history中最近使用的內容。此值被使用於當GoForward 方法被呼叫。 | |
Refresh | 重載現在的內容。 | |
Reset | 相似於Refresh,但App應重新評估的目標URI導航,可能清除backstack的導航到新的目標URI。 |
(D) 如何取消導向新的Page:
同上述的機制,當系統呼叫RootFrame導向新的Page時,如果App不想清除backstack,並且不導向新的Page時,可以在
新的Page中的OnNavigatedTo接收到新的NavigationMode時,執行 e.cancel = true,
使用NavigatingCancelEventArgs 離開新的Page,由backstack取得目前在頂端的Page。
(E) Resume and refresh the page on the top of the backstack:
需注意的是如果今天透過URI(deep link)導向的Page是目前放置於backstack頂端的Page,此時,系統不會建立新的Page,
但該Page被標誌為最頂端的Page建議應該是要做Refresh的動作。這種情境下是沒必要做clear backstack與cancel navigation。
〉實作範例:
了解了Fast Resume的概念後,往下便透過實際的Code來看看,要達到上面的概念要做那些事情:
A. 建立一個新的專案,並在WMAppManifest.xaml中<DefaultTask />增加 ActivationPolicy屬性;
1: <Tasks>
2: <DefaultTask Name="_default" NavigationPage="MainPage.xaml"
3: ActivationPolicy="Resume" />
4: </Tasks>
B. 建立三個Page,並且加入以下的事件:
B-1. MainPage.xaml:加入一個HyperlinkButton連結至Page1.xaml,一個Button用於建立Secondary Tile連結Page2.xaml的功能;
1: <!-- 加入二個畫面控件與邏輯事件 -->
2: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
3: <StackPanel>
4: <HyperlinkButton NavigateUri="/Page1.xaml"
5: Content="Go to page 1"/>
6: <Button Name="Page2TileButton"
7: Click="Page2TileButton_Click_1"
8: Content="Create Page 2 Tile"
9: Visibility="Collapsed"/>
10: </StackPanel>
11: </Grid>
1: private void Page2TileButton_Click_1(object sender, RoutedEventArgs e)
2: {
3: // 如果未存在指定的Tile,則建立一個新的。
4: if (!TileExists("Page2"))
5: {
6: // 建立一個新的Secondary Tile物件。
7: StandardTileData NewTileData = new StandardTileData
8: {
9: BackgroundImage = new Uri("purple.jpg", UriKind.Relative),
10: Title = "Page2",
11: Count = 12,
12: BackTitle = "Back of Tile",
13: BackContent = "Welcome to the back of the Tile",
14: };
15:
16: // 指定該Tile的URI夾帶DeepLink參數,讓Page2.xaml可以識別是從Start或Deactiviation的來源。
17: ShellTile.Create(new Uri("/page2.xaml?DeepLink=true", UriKind.Relative), NewTileData);
18: }
19: }
20:
21: private bool TileExists(string match)
22: {
23: // Look to see if the tile already exists and if so, don't try to create again.
24: ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(
25: x => x.NavigationUri.ToString().Contains(match));
26:
27: // Create the tile if we didn't find it already exists.
28: if (TileToFind == null) return false;
29:
30: return true;
31: }
32:
33: protected override void OnNavigatedTo(NavigationEventArgs e)
34: {
35: Page2TileButton.Visibility =
36: TileExists("Page2") ? Visibility.Collapsed : Visibility.Visible;
37: }
B-2. Page1.xaml:加入一個HyperlinkButton連結至Page2.xaml;
1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
2: <HyperlinkButton NavigateUri="/Page2.xaml" Content="Go to page 2"/>
3: </Grid>
B-3. Page2.xaml:override OnNavigatedTo的方法,判斷是否由Secondary Tile所Deep link進入App;
1: protected override void OnNavigatedTo(NavigationEventArgs e)
2: {
3: base.OnNavigatedTo(e);
4: // 識別是否由Start或Deactivation的來源。
5: if (NavigationContext.QueryString.ContainsKey("DeepLink") == true)
6: MessageBox.Show("From DeepLink");
7: else
8: MessageBox.Show("From Deactivation");
9: }
完成以上步驟,也許您會跟我一樣馬上去試看看,是否跟上述說明的會有相同效果,我必需告訴您,是不會的。
=>情境2:執行完情境1後,重新按下Primary Tile或App List中的App Name,確實重新出現了MainPage.xaml,但按下Back鍵就離開程式;
=>情境3:不用設定或移除Page,似乎預設就是了。
=>情境4:需要增加在RootFrame的處理才有辦法達到。
=>情境5:情境是正常的。
從上述幾個情境的簡單測試下來,為了達到情境2、情境4的效果,需要對RootFrame在Naviation URI與NavigationMode進行管理。
原因在於:程式需要去了解當App做Fast resume時,它有一或多個導向需要去處理,才能管理出符合用戶期待的結果。
C. 開啟App.xaml.cs增加操作RootFrame與識別目前狀態的屬性:
C-1. 建立必要的屬性:
1: #region Fast Resume 專用參數與屬性
2: enum SessionType
3: {
4: None,
5: // 當app被啟動來自Primary tile則為此值。
6: Home,
7: // 當app被啟動來自Deep Link則為此值。
8: DeepLink
9: }
10:
11: private SessionType sessionType = SessionType.None;
12:
13: /// <summary>
14: /// 設定為true,代表Page被重新導向。
15: /// </summary>
16: bool wasRelaunched = false;
17:
18: /// <summary>
19: /// 設定為true,代表超過30秒再重新啟動App。
20: /// </summary>
21: bool mustClearPagestack = false;
22:
23: IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
24: #endregion
當App resume時,Root Frame將會被導向很多次。如果Root Frame被導向指定為NavigationMode.Reset時,
wasRelaunched將會為true,這個資訊會被保存起來用於下一個導向識別。
mustClearPagestack變數被使用於代表是否超過預期的回來時間長度。
C-2. 為 App的Launching、Activated、Deactivated、Closing增加維護狀態的邏輯:
1: #region Fast Resumem專用方法
2: /// <summary>
3: /// 新增或更新指定ApplicationSettings中Key的值。
4: /// </summary>
5: public bool AddOrUpdateValue(string Key, Object value)
6: {
7: bool valueChanged = false;
8: // 判斷key是否存在
9: if (settings.Contains(Key))
10: {
11: // 判斷值是否有不一致,有則加入
12: if (settings[Key] != value)
13: {
14: settings[Key] = value;
15: valueChanged = true;
16: }
17: }
18: else
19: {
20: // 不存在則建立一個新的key
21: settings.Add(Key, value);
22: valueChanged = true;
23: }
24: return valueChanged;
25: }
26:
27: /// <summary>
28: /// 移除指定ApplicationSettings中Key。
29: /// </summary>
30: /// <param name="Key"></param>
31: public void RemoveValue(string Key)
32: {
33: // 判斷key是否存在
34: if (settings.Contains(Key)) settings.Remove(Key);
35: }
36:
37: /// <summary>
38: /// 儲存現在的時間。
39: /// </summary>
40: public void SaveCurrentTime()
41: {
42: // 用於識別是否超過5分鐘才回到應用程式時。
43: if (AddOrUpdateValue("DeactivateTime", DateTimeOffset.Now))
44: {
45: settings.Save();
46: }
47: }
48:
49: /// <summary>
50: /// 移除暫存的時間值。
51: /// </summary>
52: public void RemoveCurrentTimeSetting()
53: {
54: RemoveValue("DeactivateTime");
55: settings.Save();
56: }
57:
58: /// <summary>
59: /// 比對DeactivationTimeStamp。識別離開的時間與現在的時間是否符合差距。
60: /// </summary>
61: /// <returns></returns>
62: bool CheckDeactivationTimeStamp()
63: {
64: DateTimeOffset lastDeactivated;
65:
66: if (settings.Contains("DeactivateTime"))
67: {
68: lastDeactivated = (DateTimeOffset)settings["DeactivateTime"];
69: }
70:
71: var currentDuration = DateTimeOffset.Now.Subtract(lastDeactivated);
72:
73: return TimeSpan.FromSeconds(currentDuration.TotalSeconds) > TimeSpan.FromSeconds(30);
74: }
75: #endregion
以上為操作IsolatedStroage.ApplicationSetting與判斷是否超過預期回來時間長度的邏輯方法。
主要應用於Lauching、Activated、Deactivated、Closing。
1: // Code to execute when the application is launching (eg, from Start)
2: // This code will not execute when the application is reactivated
3: private void Application_Launching(object sender, LaunchingEventArgs e)
4: {
5: // Appy執行時,清掉上一個記錄離開的時間。
6: RemoveCurrentTimeSetting();
7: }
8:
9: // Code to execute when the application is activated (brought to foreground)
10: // This code will not execute when the application is first launched
11: private void Application_Activated(object sender, ActivatedEventArgs e)
12: {
13: // App重新啟動時,識別是否超過預期回來的時間長度。
14: mustClearPagestack = CheckDeactivationTimeStamp();
15: }
16:
17: // Code to execute when the application is deactivated (sent to background)
18: // This code will not execute when the application is closing
19: private void Application_Deactivated(object sender, DeactivatedEventArgs e)
20: {
21: // App離開時,記下目前離開的時間。
22: SaveCurrentTime();
23: }
24:
25: // Code to execute when the application is closing (eg, user hit Back)
26: // This code will not execute when the application is deactivated
27: private void Application_Closing(object sender, ClosingEventArgs e)
28: {
29: // App整個關閉時,清掉上一個記錄離開的時間。
30: RemoveCurrentTimeSetting();
31: }
C-3. 在InitializePhoneApplication註冊RootFrame.Navigating()的處理事件:
1: private void InitializePhoneApplication()
2: {
3: if (phoneApplicationInitialized)
4: return;
5:
6: // Create the frame but don't set it as RootVisual yet; this allows the splash
7: // screen to remain active until the application is ready to render.
8: RootFrame = new PhoneApplicationFrame();
9: RootFrame.Navigated += CompleteInitializePhoneApplication;
10:
11: // Handle navigation failures
12: RootFrame.NavigationFailed += RootFrame_NavigationFailed;
13:
14: // Handle reset requests for clearing the backstack
15: RootFrame.Navigated += CheckForResetNavigation;
16:
17: // 註冊監控Deep Link Launching
18: RootFrame.Navigating += RootFrame_Navigating;
19:
20: // Ensure we don't initialize again
21: phoneApplicationInitialized = true;
22: }
註冊該事件以處理Root Fame導向的來源。其主要處理重點有二:
(1) Navigating()該方法在App Resume時會被呼叫多次,代表有多個Navigations需要處理;
(2) 有二個可能的結果:
一個是確定要清除backstack:此情境不需要做什麼,系統會自動(Default)去執行ClearBackStackAfterReset方法;
一個是確定不清除backstack:它將從RootFrame.Navigated()移除ClearBackStackAfterReset這個方法避免清掉backstack;
以下為實作Navigating()的內容:
1: /// <summary>
2: /// RootFrame的Navigating事件,它會被執行很多次。
3: /// </summary>
4: /// <param name="sender"></param>
5: /// <param name="e"></param>
6: void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
7: {
8: // 確認目前狀態是否為None,而且NavigationMode是否為New,如果是代表new instance。
9: if (sessionType == SessionType.None && e.NavigationMode == NavigationMode.New)
10: {
11: // 以下內容只有在app的初次啟動才會執行。
12: // 判斷Uri中是否存在DeepLink=true,如果是代表為Deep Link。如果是MainPage.xaml則由Primary Tile來的。
13: if (e.Uri.ToString().Contains("DeepLink=true"))
14: {
15: sessionType = SessionType.DeepLink;
16: }
17: else if (e.Uri.ToString().Contains("/MainPage.xaml"))
18: {
19: sessionType = SessionType.Home;
20: }
21: }
22:
23: // 識別目前NavigationMode是否為Reset,如果是該值需要儲存做為下一個Navigation使用。
24: if (e.NavigationMode == NavigationMode.Reset)
25: {
26: wasRelaunched = true;
27: }
28: // 識別目前NavigationMode是否為New,而且上一個Navigation是reset。
29: else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
30: {
31: // 以下內容只在上一個Navigatoin是relaunch才執行,先清掉wasRelaunched。
32: wasRelaunched = false;
33:
34: if (e.Uri.ToString().Contains("DeepLink=true"))
35: {
36: // 此部分的判斷存在Uri具有DeepLink=true,也代表已具有一個old instance,
37: // 用戶再點擊了Secondary tile進來這個App,需要系統(template code)會自動清掉已存在的backstack.
38: sessionType = SessionType.DeepLink;
39: }
40: else if (e.Uri.ToString().Contains("/MainPage.xaml"))
41: {
42: // 此部分的判斷存在Uri具有MainPage.xaml,而且上一個執行是Deep Linke,
43: if (sessionType == SessionType.DeepLink)
44: {
45: // 代表用戶重新點了Primary tile進來app,需要系統(template code)會自動清掉已存在的backstack.
46: sessionType = SessionType.Home;
47: }
48: else
49: {
50: // 此部分的判斷存在Uri具有MainPage.xaml,而且上一個執行是Home(Primary Tile)。
51: // 透過mustClearPagestack識別是否有超過30秒。
52: if (!mustClearPagestack)
53: {
54: // 此部分代表未超過30秒,不需要清掉backstack。所以要-= ClearBackStackAfterReset方法
55: e.Cancel = true;
56: RootFrame.Navigated -= ClearBackStackAfterReset;
57: }
58: }
59: }
60:
61: mustClearPagestack = false;
62: }
63: }
上述程式碼非常的複雜,分成幾個識別點來說明:
a. 先識別SessionType是否為None,並且NavigationMode = new,如果成立,即App是第一次啟動;
=>只需注意啟動的URI為何更新SessionType。
b. 識別NavigationMode = Reset,則先標識於wasRelaunched做為下一個Navigation時判斷用;
=>由於RootFrame在Resume時會執行很多次RootFrame.Navigating()。
c. 識別NavigationMode = New,並且wasRelaunched = true,代表是Resume,有已存在的old instance;
=>識別此次Uri是否存在DeepLink,如果存在代表是由Secondary Tile進來,需要清掉backstack。
=>識別此次Uri是否存在MainPage,如果存在代表是由Primary Tile進來:
進一步識別SessionType在上一個狀態是否為:DeepLink,如果是代表此次要回到Home,需清掉backstack。
=>為何要清掉?因為上一個進來的狀態是DeepLink,此時又點擊Primary Tile代表要進入新的狀態,故清掉backstack;
如果SessionType在上一個狀態不是DeepLink,代表是重新回到該App,需改識別mustClearPagestack:
=>如果用戶超過30秒預期回到App的時間,需清掉backstack。
如果未超過,則移掉ClearBackStackAfterReset方法不清掉backstack。
詳細的說明可參考<Fast app resume backstack sample>。
D. 執行結果:
‧情境1:從Primary Tile進入MainPage.xaml,進入Page1.xaml,按Start Button回到Start Screen,再按Primary Tile。
=>看到Page1.xaml,按下Back鍵可回到MainPage.xaml。這樣才能符合用戶的期待。
‧情境2:從Primary Tile進入MainPage.xaml,進入Page1.xaml,按Start Button回到Start Screen,再按Secondary Tile。
=>看到Page2.xaml,按下Back鍵離開App回到Start Screen。應用程式被Resume,但清除了所有的Page backstack。
‧情境3:從Primary Tile進入MainPage.xaml,進入Page1.xaml,按Start Button回到Start Screen,等待超過30秒,再按Primary Tile。
=>看到MainPage.xaml,按下Back鍵backstack被清除離開App回到Start Screen。主要原因在於用戶離開App已有一段時間,
按照用戶的期望應該是回到一個全新的Instance會比回到上一次狀態好一些。
因此,建議在App可自訂取決一個時間長度來做為fresh instance的機制。
[範例程式]
======
以上是分享在Windows Phone 8啟動Apps與新Fast Resume特性,有些內容還需透過實作時才會比較有感覺,
尤其在處理RootFrame.Navigating()的部分,真的需要花時間好好了解其特性,才能做到適合用戶期望的感覺,
讓Resume時是有意義的,希望有助於大家了解Windows Phone 8的開發與新特性的掌握。
References:
‧Launching, resuming, and multitasking for Windows Phone (重要)
‧App activation and deactivation for Windows Phone.
‧Fast app resume for Windows Phone 8. (重要)
‧URI schemes for launching built-in apps for Windows Phone 8
‧Fast Resume Backstack Sample (重要)
‧Background file transfers for Windows Phone.
‧Background agents for Windows Phone.
‧Continuous Location tracking on Windows Phone 8
‧Idle detection for Windows Phone (必讀)
‧How to preserve and restore page state for Windows Phone (必讀)
‧How to preserve and restore app state for Windows Phone (必讀)