UWP開發之Mvvmlight實踐五:SuspensionManager中斷掛起以及複原處理

来源:http://www.cnblogs.com/lixiaobin/archive/2016/04/24/SuspensionManager.html
-Advertisement-
Play Games

最近比較忙有一段時間沒有更新了,再接再厲繼續分享。 先我們看看App在生命周期中會出現那些狀態: 詳細介紹參考官網:App lifecycle https://msdn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle 一般情況: ...


最近比較忙有一段時間沒有更新了,再接再厲繼續分享。

 

先我們看看App在生命周期中會出現那些狀態:

詳細介紹參考官網:App lifecycle  https://msdn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle

 

state diagram showing transitions between app execution states

一般情況:

比如用新聞APP看新聞的時候突然收到郵件,然後跳轉到郵件APP查看郵件,查看完了再回到APP繼續看新聞。

這個時候如果不做中斷掛起處理的話,是很難保證APP會恢復到跳轉之前的狀態。之所以說很難保證是因為如果手機記憶體夠大夠用系統就不會關閉沒用的app讓它駐留在記憶體中,下次直接從記憶體恢復,這樣可以恢復到跳轉之前的狀態。如果記憶體不夠用系統會關閉app回收資源,這個時候沒有中斷保存進度處理再次啟動的時候就會從新開始,無法恢復到跳轉前的狀態。

正是為了這樣的人性化處理才有中斷恢復處理的必要性。

 

中斷複原的原理:

數據保存時機:從Running--->Suspended的時候觸發Suspending事件做畫面進度保存處理,

數據恢復時機:如果記憶體沒回收直接觸發Resuming事件不需要做任何事情,如果回收的情況下在調用app的OnLaunched中通過判斷Terminated狀態做數據恢復處理。

 

中斷複原實現:(MVVM實現方式以後待續

在Win8.1開發StoreApp的時候預設會在項目中添加SuspensionManager.cs文件並且配置好,然而在UWP開發中模板沒有自帶這個文件,為什麼?難道還有其他處理方法?

在網上找了下沒找到原因,如果有知道歡迎介紹。雖然沒找到結果無意發現了一個很好的開源框架:Template10,UWP很多常用的開發技巧都封裝好了,減少不少工作量。Template10詳細介紹參考:https://github.com/Windows-XAML/Template10/wiki

那我們的中斷處理就有兩種方法:

  • SuspensionManager.cs形式
  • Template10 插件

第一種SuspensionManager.cs形式實現:

找到SuspensionManager.cs文件,在微軟的UWP例子裡頭有:https://github.com/Microsoft/Windows-universal-samples/blob/master/Samples/ApplicationData/cs/SuspensionManager.cs。把這個文件趴下來或者新建一個win8.1的app從它裡面拷貝過來,別忘記改命名空間。

image

在App.xaml.cs文件確認OnSuspending事件是否註冊了。

public App()
        {
            Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
                Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
                Microsoft.ApplicationInsights.WindowsCollectors.Session);
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

在App.xaml.cs中找到OnSuspending事件添加中斷保存處理SuspensionManager.SaveAsync()。由於是非同期操作事件名前記得加上async。

/// <summary>
        /// 在將要掛起應用程式執行時調用。  在不知道應用程式
        /// 無需知道應用程式會被終止還是會恢復,
        /// 並讓記憶體內容保持不變。
        /// </summary>
        /// <param name="sender">掛起的請求的源。</param>
        /// <param name="e">有關掛起請求的詳細信息。</param>
        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            // 保存應用狀態
            await SuspensionManager.SaveAsync();
            deferral.Complete();
        }

在App.xaml.cs中添加恢復處理。預設OnLaunched方法中是已經留好位置的,找到【//TODO: 從之前掛起的應用程式載入狀態】替換為SuspensionManager.RestoreAsync()。以及關聯設置SuspensionManager.RegisterFrame(rootFrame, "AppFrame")。由於是非同期操作事件名前記得加上async。

/// <summary>
        /// 在應用程式由最終用戶正常啟動時進行調用。
        /// 將在啟動應用程式以打開特定文件等情況下使用。
        /// </summary>
        /// <param name="e">有關啟動請求和過程的詳細信息。</param>
        protected async override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = false;
            }
#endif
            Frame rootFrame = Window.Current.Content as Frame;

            // 不要在視窗已包含內容時重覆應用程式初始化,
            // 只需確保視窗處於活動狀態
            if (rootFrame == null)
            {
                // 創建要充當導航上下文的框架,並導航到第一頁
                rootFrame = new Frame();

                //將框架與 SuspensionManager 鍵關聯                                
                SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // 數據恢復
                    await SuspensionManager.RestoreAsync();
                }

                // 將框架放在當前視窗中
                Window.Current.Content = rootFrame;
            }

            if (e.PrelaunchActivated == false)
            {
                if (rootFrame.Content == null)
                {
                    // 當導航堆棧尚未還原時,導航到第一頁,
                    // 並通過將所需信息作為導航參數傳入來配置
                    // 參數
                    rootFrame.Navigate(typeof(MainPage), e.Arguments);
                }
                // 確保當前視窗處於活動狀態
                Window.Current.Activate();
            }
        }

這樣簡單的中斷掛機以及恢復處理就配置完成了。

簡單使用,在MainPage.xaml.cs裡面重載OnNavigatedFrom和OnNavigatedTo方法。

/// <summary>
        /// 保存數據
        /// </summary>
        /// <param name="e"></param>
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
            var _pageKey = "Page-" + this.GetType().ToString();
            var pageState = new Dictionary<String, Object>();

            pageState[nameof(txtInput)] = txtInput.Text;

            frameState[_pageKey] = pageState;
        }
        /// <summary>
        /// 恢複數據
        /// </summary>
        /// <param name="e"></param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (e.NavigationMode == NavigationMode.New)
            {

            }
            else
            {
                var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
                var _pageKey = "Page-" + this.GetType().ToString();

                var data = (Dictionary<String, Object>)frameState[_pageKey];
                if (data != null && data.ContainsKey(nameof(txtInput)))
                {
                    txtInput.Text = data[nameof(txtInput)].ToString();
                }
            }
        }

 

SuspensionManager.SaveAsync()代碼如下:

主要原理—>通過遍歷每個Frame調用frame.GetNavigationState()方法收集畫面數據到_sessionState,然後將_sessionState字典數據序列化保存到LocalState下的_sessionState.xml文件中。

/// <summary>
        /// 保存當前 <see cref="SessionState"/>。  任何 <see cref="Frame"/> 實例
        /// (已向 <see cref="RegisterFrame"/> 註冊)都還將保留其當前的
        /// 導航堆棧,從而使其活動 <see cref="Page"/> 可以
        /// 保存其狀態。
        /// </summary>
        /// <returns>反映會話狀態保存時間的非同步任務。</returns>
        public static async Task SaveAsync()
        {
            try
            {
                // 保存所有已註冊框架的導航狀態
                foreach (var weakFrameReference in _registeredFrames)
                {
                    Frame frame;
                    if (weakFrameReference.TryGetTarget(out frame))
                    {
                        SaveFrameNavigationState(frame);
                    }
                }

                // 以同步方式序列化會話狀態以避免對共用
                // 狀態
                MemoryStream sessionData = new MemoryStream();
                DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
                serializer.WriteObject(sessionData, _sessionState);

                // 獲取 SessionState 文件的輸出流並以非同步方式寫入狀態
                StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(sessionStateFilename, CreationCollisionOption.ReplaceExisting);
                using (Stream fileStream = await file.OpenStreamForWriteAsync())
                {
                    sessionData.Seek(0, SeekOrigin.Begin);
                    await sessionData.CopyToAsync(fileStream);
                }
            }
            catch (Exception e)
            {
                throw new SuspensionManagerException(e);
            }
        }

SuspensionManager.RestoreAsync()代碼如下:

主要原理—>讀取LocalState下_sessionState.xml文件的內容反序列化保存到_sessionState字典中。然後調用frame.SetNavigationState((String)frameState["Navigation"])重新載入數據。

/// <summary>
        /// 還原之前保存的 <see cref="SessionState"/>。  任何 <see cref="Frame"/> 實例
        /// (已向 <see cref="RegisterFrame"/> 註冊)都還將還原其先前的導航
        /// 狀態,從而使其活動 <see cref="Page"/> 可以還原其
        /// 狀態。
        /// </summary>
        /// <param name="sessionBaseKey">標識會話類型的可選密鑰。
        /// 這可用於區分多個應用程式啟動方案。</param>
        /// <returns>反映何時讀取會話狀態的非同步任務。
        /// 在此任務完成之前,不應依賴 <see cref="SessionState"/>
        /// 完成。</returns>
        public static async Task RestoreAsync(String sessionBaseKey = null)
        {
            _sessionState = new Dictionary<String, Object>();

            try
            {
                // 獲取 SessionState 文件的輸入流
                StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(sessionStateFilename);
                using (IInputStream inStream = await file.OpenSequentialReadAsync())
                {
                    // 反序列化會話狀態
                    DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
                    _sessionState = (Dictionary<string, object>)serializer.ReadObject(inStream.AsStreamForRead());
                }

                // 將任何已註冊框架還原為其已保存狀態
                foreach (var weakFrameReference in _registeredFrames)
                {
                    Frame frame;
                    if (weakFrameReference.TryGetTarget(out frame) && (string)frame.GetValue(FrameSessionBaseKeyProperty) == sessionBaseKey)
                    {
                        frame.ClearValue(FrameSessionStateProperty);
                        RestoreFrameNavigationState(frame);
                    }
                }
            }
            catch (Exception e)
            {
                throw new SuspensionManagerException(e);
            }
        }

★做序列化和反序列化的時候用到了_knownTypes,這個是重點。如果要對自定義類型,列表數據做中斷保存處理時需要添加自定義類型的Type到_knownTypes,否則序列化會失敗。

_knownTypes詳細使用後續章節待續…


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、彈框出現在屏幕中間位置 此方法可以添加文本框,輸入內容 2、彈框出現在屏幕底部(兩種方式的不同點在於代碼第一行最後的,底部是UIAlertControllerStyleActionSheet) ...
  • 最近解析些複雜的節點數據解析,用安卓自帶的json解析比較麻煩所以只能用Gson解析,所以從網上下了點demo來看看 http://blog.csdn.net/tkwxty/article/details/34474501這個看看還不錯 例子代碼如下 ...
  • 給EditText追加ChangedListener可以監聽EditText內容變化的監聽 如圖是效果圖 類似於過濾的一種實現 1 佈局也就是一個EditText,當EditText內容發生變化時(當輸入數字時)就會出現一個PopupWindow通過ChangedListener來顯示監聽 2 數據 ...
  • Android開發中,常常會用到color.xml顏色配置,好的顏色配置可以讓尼的應用讓人看起來賞心悅目! 不羅嗦,上圖先 該工程已經羅列了常用的顏色配置 附上工程鏈接:http://download.csdn.net/detail/geniuseoe2012/5816967 什麼?還沒找到尼想要的 ...
  • TNW中文站 4月8日報道 安卓操作系統的軟體開發語言是Java,而在過去幾年中,有關Java的版權,谷歌(微博)和甲骨文之間發生了長期的訴訟。最新外媒消息稱,谷歌正在考慮將蘋果開發的Swift作為未來安卓軟體開發的“一級”語言,此外Facebook、Uber等公司也開始越來越重視Swift的使用。 ...
  • 字典轉模型 1> 什麼是字典轉模型? 字典數據/數組(可以是 plist 文件中的數據也可以是網路後臺的數據等)轉化為模型對象/數組. 2> 註意 模型要提供可以傳入字典參數的構造方法.(一個對象方法和一個類方法) - (instancetype)initWithDict:(NSDictionary ...
  • ➠更多技術乾貨請戳:聽雲博客 做為一個測試人員,工作中經常會用到數據抓包工具來進行數據分析和驗證,下麵就簡單介紹一下工作中常用的抓包工具。 TcpDump抓包 Tcpdump是一個用於截取網路分組,並輸出分組內容的工具。它憑藉強大的功能和靈活的截取策略,使其成為類UNIX系統下用於網路分析和問題排查 ...
  • 函數是一組用於執行特定任務的獨立的代碼段,你用一個名字來標識函數,這個名字是用來“調用”函數來執行它的任務。 swift統一函數的語法具有足夠的靈活性來表達任何一個簡單的不帶參數的名稱與本地和外部的每個參數的參數名稱的複雜objective-c-style C風格的函數方法。參數可以提供預設值,以簡 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...