Xamarin.Forms.AndroidでAssetsファイルをコピー

xamarin.forms-Androidでアプリ内に含めたsqliteを使用する場合、Assetsとしてbuildしたsqliteファイルをxamarinでアクセス可能なディレクトリにコピーする必要がある。インストール初期DBを持たせたい場合だ。

xamarin-AndroidでのAssets

Assetsとして設定されたリソースはビルドされるアプリの中に含められる。しかし、xamarinのApp内からこのAssetsにアクセスすることができず、assetsのパスを取得することも出来なかった。(いくら探しても有用な情報が出てこず) ちなみにiOSのNSBundleはアクセスできる。

しかし、AndroidのActivityクラス内ではAssetsにアクセスできる。であるからして、new App()の前にActivity内でassetsを何処かにコピーする必要がある。

assetsファイルをコピー

Activityクラス内で以下の様にして"Environment.SpecialFolder"のどこかにファイルをコピー。

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            CopyFile();

            LoadApplication(new App());
        }
        /// <summary>
        /// AssetsからLocalApplicationData内にファイルをコピー
        /// </summary>
        /// <returns></returns>
        private void CopyFile()
        {
            string fileName = "hoge.db";
            string toDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
            string filePath = Path.Combine(toDir, fileName);
            if (!File.Exists(filePath))
            {
                using (BinaryReader br = new BinaryReader(Application.Context.Assets.Open(fileName)))
                {
                    using (BinaryWriter bw = new BinaryWriter(new FileStream(filePath, FileMode.Create)))
                    {
                        byte[] buffer = new byte[2048];
                        int len = 0;
                        while ((len = br.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            bw.Write(buffer, 0, len);
                        }
                    }
                }
            }
        }
    }

あとはコピー先のパスに対して読み込みをすれば良い。

iOSではNSBundleをコピー

iOSでも同様の処理を行う場合以下のようになる。

public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            CopyFile();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

        /// <summary>
        /// BundleからLibrary内にファイルをコピー
        /// </summary>
        /// <returns></returns>
        private void CopyFile()
        {
            string fileName = "hoge.db";
            string toDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "Library");
            string filePath = Path.Combine(NSBundle.MainBundle.BundlePath, fileName);
            if (!File.Exists(Path.Combine(toDir, fileName)))
            {
                File.Copy(filePath, toDir, true);
            }
        }
    }

まとめ

上記の様にすれば、アプリ内に初期DBを含ませてリリースし、起動時に端末内にコピーして使用することができる。
しかし、本当はAssetsまたはNSBundleのファイルのコピーもxamarin内で行いたかったが、おそらく不可能なのだろう。