前回の記事 で Application.Properties を利用すればある程度セッションデータを永続化できることはわかりました
ただし、Windows Phone の場合、起動時に復元はできなかったので別の方法を試してみます
用意するのはこれ
/// <summary> /// XML リポジトリ /// </summary> public class XmlRepositories : ISessionRepository { /// <summary> /// 保存先フォルダ /// </summary> private static readonly StorageFolder Folder = ApplicationData.Current.LocalFolder; /// <summary> /// データストア /// </summary> private Dictionary<Type, object> storage = new Dictionary<Type, object>(); /// <summary> /// コンストラクタ /// </summary> static XmlRepositories() { } /// <summary> /// データを設定します /// </summary> /// <typeparam name="T">データの型</typeparam> /// <param name="value">データ</param> public void SetValue<T>(T value) where T : class { this.storage[typeof(T)] = value; } /// <summary> /// データを取得します /// </summary> /// <typeparam name="T">データの型</typeparam> /// <returns>データ</returns> public T GetValue<T>() where T : class { if (!this.storage.ContainsKey(typeof(T))) { return null; } return this.storage[typeof(T)] as T; } /// <summary> /// 初期化します /// </summary> /// <returns>成功した場合<code>true</code>、それ以外は<code>false</code></returns> public bool Initilize() { try { this.storage.Clear(); return true; } catch (Exception) { return false; } } /// <summary> /// セッションデータを読み込みます /// </summary> /// <returns>成功した場合<code>true</code>、それ以外は<code>false</code></returns> public async Task<bool> LoadAsync() { var result = true; try { var types = new List<string>(); var indexFile = await Folder.GetItemAsync("session-keys.xml"); using (var stream = new StreamReader(indexFile.Path)) { var serializer = new XmlSerializer(types.GetType()); types = serializer.Deserialize(stream) as List<string>; } foreach (var typename in types) { this.storage[Type.GetType(typename)] = null; } } catch (Exception) { } foreach (var data in this.storage.ToList()) { try { var file = await Folder.GetItemAsync(string.Format("{0}.xml", data.Key.Name)); using (var stream = new StreamReader(file.Path)) { var serializer = new XmlSerializer(data.Key); this.storage[data.Key] = serializer.Deserialize(stream); } } catch (FileNotFoundException) { this.storage[data.Key] = null; continue; } catch (InvalidOperationException) { this.storage[data.Key] = null; continue; } catch (Exception) { result = false; } } return result; } /// <summary> /// セッションデータを保存します /// </summary> /// <returns>成功した場合<code>true</code>、それ以外は<code>false</code></returns> public async Task<bool> SaveAsync() { var result = true; try { var types = (from t in this.storage select t.Key.AssemblyQualifiedName).ToList(); var indexFile = await Folder.CreateFileAsync("session-keys.xml", CreationCollisionOption.ReplaceExisting); using (var stream = new StreamWriter(indexFile.Path)) { var serializer = new XmlSerializer(types.GetType()); serializer.Serialize(stream, types); } } catch (Exception) { } foreach (var data in this.storage) { try { var file = await Folder.CreateFileAsync(string.Format("{0}.xml", data.Key.Name), CreationCollisionOption.ReplaceExisting); using (var stream = new StreamWriter(file.Path)) { var serializer = new XmlSerializer(data.Key); serializer.Serialize(stream, data.Value); } } catch (FileNotFoundException) { continue; } catch (InvalidOperationException) { continue; } catch (Exception) { result = false; } } return result; } }
Xamarin でも XmlSerializer が使えるみたいなので、キーとなる値のリストと、それぞれのキーに格納されたインスタンスごとに XML ファイルにシリアライズして保存するようにしてみました
復元する場合は反対の処理を書けばいいというわけです
あとは App.cs に記述していた
/// <summary> /// コンストラクタ /// </summary> public App() { this.MainPage = new TopPage(); Container.RegisterType<ISessionRepository, ApplicationProperties>(new ContainerControlledLifetimeManager()); this.sessionRepository = Container.Resolve<ISessionRepository>(); this.sessionRepository.Initilize(); }
ApplicationProperties の登録コードを削除して Windows Phone の App.xaml.cs の方に
/// <summary> /// Constructor for the Application object. /// </summary> public App() { // Global handler for uncaught exceptions. UnhandledException += Application_UnhandledException; XamarinSessionRestore.App.Container.RegisterType<ISessionRepository, XmlRepositories>(new ContainerControlledLifetimeManager()); // Standard XAML initialization InitializeComponent(); // Phone-specific initialization InitializePhoneApplication(); // Language display initialization InitializeLanguage(); // Show graphics profiling information while debugging. if (Debugger.IsAttached) { // Display the current frame rate counters. Application.Current.Host.Settings.EnableFrameRateCounter = true; // Show the areas of the app that are being redrawn in each frame. //Application.Current.Host.Settings.EnableRedrawRegions = true; // Enable non-production analysis visualization mode, // which shows areas of a page that are handed off to GPU with a colored overlay. //Application.Current.Host.Settings.EnableCacheVisualization = true; // Prevent the screen from turning off while under the debugger by disabling // the application's idle detection. // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run // and consume battery power when the user is not using the phone. PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; } }
XmlRepositories の登録コードを追記するだけで移行完了
前回と同じように実行してみます・・・
ログの出力はこう変わりました
App.OnStart: activated. App.OnStart: Restore session data. TopPage.xaml.OnAppearing: TopPage Appearing. App.OnSleep: sleeped. App.OnResume: Restore session data. App.OnResume: resumed. App.OnSleep: sleeped. App.OnStart: activated. App.OnStart: Restore session data. TopPage.xaml.OnAppearing: TopPage Appearing.
ちゃんと保存できてますね
Dictionary 型など一部のデータは別の型に移し替えて保存・復元する必要がありますが、普通に永続化できそうです