WP8 - Launch Apps與Fast Resume

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.

Search extensibility for Windows Phone

Wallet

Incorporate wallet functionality in your app.

Wallet for Windows Phone 8

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.

Tiles for Windows Phone

Picture viewer

Launch from the picture viewer to share, edit, or open the photo.

Photo extensibility for Windows Phone

Photos Hub

Appear in the Photos Hub, a convenient place for photo apps.

Extending the Photos Hub for Windows Phone

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.

Lenses for Windows Phone 8

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 (必讀)

 

Dotblogs Tags: