しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Xamarin でセッションデータを保存・復元したい 【Windows RT 編】

Xamarin.Forms に Windows ストアアプリと Windows Phone 8.1 がついにやってきた!

Windows Phone 8.0 などなかったのだ、いいね?

ということで Windows ランタイムアプリ用のセッションデータ保存も Xamarin から試してみます

Xamarin 用の Windows ストアアプリや Windows Phone 8.1 アプリプロジェクトの追加の仕方はきっと、某イケメンさん がわかりやすくまとめてくれると思うのですっ飛ばします!

追加するのは他のプラットフォームと同じく XmlRepository.cs

/// <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.GetFileAsync("session-keys.xml");

            using (var stream = await indexFile.OpenSequentialReadAsync())
            {
                var serializer = new XmlSerializer(types.GetType());
                types = serializer.Deserialize(stream.AsStreamForRead()) 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.GetFileAsync(string.Format("{0}.xml", data.Key.Name));
                using (var stream = await file.OpenSequentialReadAsync())
                {
                    var serializer = new XmlSerializer(data.Key);
                    this.storage[data.Key] = serializer.Deserialize(stream.AsStreamForRead());
                }

            }
            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 = await indexFile.OpenStreamForWriteAsync())
            {
                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 = await file.OpenStreamForWriteAsync())
                {
                    var serializer = new XmlSerializer(data.Key);
                    serializer.Serialize(stream, data.Value);
                }
            }
            catch (FileNotFoundException)
            {
                continue;
            }
            catch (InvalidOperationException)
            {
                continue;
            }
            catch (Exception)
            {
                result = false;
            }
        }

        return result;
    }
}

Windows Phone 8.0 のときとそれほど変わらないですね

Windows ストアアプリと Windows Phone 8.1 は API がかなり統一されているので、iOSAndroid の場合と同じくファイルリンクで共通化できちゃいます

とりあえず下記の手順で動作確認

  • アプリ起動
  • ホームボタンでバックグラウンド遷移
  • 戻るボタンで再開
  • Visual Studio から中断とシャットダウン
  • Visual Studio から再度起動

あ・・・あれ?!復元できてませんね

ログを見てみると・・・

TopPage.xaml.OnAppearing: TopPage Appearing.
App.OnSleep: sleeped.

あちゃ~ Xamarin.Forms App Lifecycle はまだ Windows ランタイムアプリに完全対応してないみたいですね

中断とシャットダウンのときだけ OnSleep が呼ばれる謎仕様・・・むむむぅ