しっぽを追いかけて

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

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

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

前回の記事Windows Phone でのセッションデータの保存と復元を XML ファイルで行ったので、ついでに iOSAndroid でもやってみます!

おもむろにリポジトリクラスを追加

#if __IOS__
namespace XamarinSessionRestore.iOS.Repositories
#else
namespace XamarinSessionRestore.Droid.Repositories
#endif
{
    /// <summary>
    /// XML リポジトリ
    /// </summary>
    public class XmlRepositories : ISessionRepository
    {
        /// <summary>
        /// 保存先フォルダ
        /// </summary>
        private static readonly string Folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);

        /// <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 = Path.Combine(Folder, "session-keys.xml");

                using (var stream = new StreamReader(indexFile))
                {
                    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 = Path.Combine(Folder, string.Format("{0}.xml", data.Key.Name));
                    using (var stream = new StreamReader(file))
                    {
                        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 = Path.Combine(Folder, "session-keys.xml");
                using (var stream = new StreamWriter(indexFile))
                {
                    var serializer = new XmlSerializer(types.GetType());
                    serializer.Serialize(stream, types);
                }
            }
            catch (Exception)
            {
            }

            foreach (var data in this.storage)
            {
                try
                {
                    var file = Path.Combine(Folder, string.Format("{0}.xml", data.Key.Name));
                    using (var stream = new StreamWriter(file))
                    {
                        var serializer = new XmlSerializer(data.Key);
                        serializer.Serialize(stream, data.Value);
                    }
                }
                catch (FileNotFoundException)
                {
                    continue;
                }
                catch (InvalidOperationException)
                {
                    continue;
                }
                catch (Exception)
                {
                    result = false;
                }
            }

            return result;
        }
    }
}

今回は iOSAndroid がワンソースです

名前空間のために #if を使ってますが別になくても問題ないです

f:id:matatabi_ux:20150317212010p:plain

こんな感じでクラスファイルをリンクとして追加すれば ioSAndroid でワンソースが簡単に実現できます

あとはいつものように DI コンテナへの登録コードを追加

public class Application
{
    // This is the main entry point of the application.
    static void Main(string[] args)
    {
        App.Container.RegisterType<ISessionRepository, XmlRepositories>(new ContainerControlledLifetimeManager());

        // if you want to use a different Application Delegate class from "AppDelegate"
        // you can specify it here.
        UIApplication.Main(args, null, "AppDelegate");
    }
}

これが iOS

[Activity(Label = "XamarinSessionRestore", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        App.Container.RegisterType<ISessionRepository, XmlRepositories>(new ContainerControlledLifetimeManager());

        global::Xamarin.Forms.Forms.Init(this, bundle);
        LoadApplication(new App());
    }
}

こっちが Android です

さっそく実行!

操作の様子はこんな感じ

問題なく保存・復元できましたね

LINQ も使えるし、SQLite よりおすすめです