shuhelohelo’s blog

Xamarin.Forms多めです.

MonkeyCacheを使ったデータのキャッシュ Xamarin.Formsで

xamgirl.com

公式↓ montemagno.com

MonkeyCacheはウェブから取得したデータをキャッシュして,必要に応じて利用することを助けるライブラリです.

データのキャッシュとは,オンライン時にWebAPIなどから取得したデータをローカルに保持しておき,オフライン時にはキャッシュからデータを取り出して表示したり,または不要なデータの取得による通信の発生を低減したりできます.

MonkeyCacheを使うことで,このようなキャッシュの仕組みを簡単に利用できます.

キャッシュしたデータの有効期限を指定することで,有効期限が切れていた場合はWebからデータを取得するといった処理もできます.

MonkeyCacheのインストール

MonkeyCacheには3種類あって,キャッシュするデータの保存先によって異なります.

  • ファイルに保存 : MonkeyCache.FileStore
  • SQLiteに保存 : MonkeyCache.SQLite
  • LiteDBに保存 : MonkeyCache.LiteDB

一番シンプルなのはファイルへの保存なので,今回はMonkeyCache.FileStoreをインストールします.

f:id:shuhelohelo:20200615164720p:plain

WebAPIからデータを取得する機能を用意する

WebAPIを利用してデータを取得する際に便利なので,先にSystem.Net.Http.Jsonをインストールしておきます.

f:id:shuhelohelo:20200615173958p:plain

WebAPIからデータを取得する機能はHttpClientを使って,WebAPIからデータを取得するか,もしくは有効なキャッシュデータがあればそれを取得して呼び出しもとに返すシンプルなものです.

ソースコードは以下のとおりです.

using MonkeyCache;
using MonkeyCache.FileStore;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using XFCacheDataPractice.Models.Covid19;
using XFCacheDataPractice.Services;

[assembly: Dependency(typeof(Covid19JapanApiManager))]
namespace XFCacheDataPractice.Services
{
    public class Covid19JapanApiManager
    {
        private IBarrel _barrel;

        private readonly HttpClient _httpClient;

        public Covid19JapanApiManager()
        {
            //HttpClientの生成
            //Covid19JapanApiManagerクラスはDependencyService経由でインスタンスを取得するので
            //1回しか生成されないので大丈夫.
            this._httpClient = new HttpClient
            {
                BaseAddress = new Uri("https://covid19-japan-web-api.now.sh/api/v1/")
            };

            Barrel.ApplicationId = AppInfo.PackageName;
            this._barrel = Barrel.Create(FileSystem.AppDataDirectory);
        }

        //県別のデータをMonkeyCacheに格納,取得する際のキー
        public const string CachePrefecturesKey = "get_prefectures";

        /// <summary>
        /// WebAPIから県別のデータを取得する.
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<Prefecture>> GetPrefectures()
        {
            try
            {
                //キャッシュが存在して,有効期間内の場合はキャッシュから
                if (!_barrel.IsExpired(key: CachePrefecturesKey))
                {
                    await Task.Yield();
                    return _barrel.Get<IEnumerable<Prefecture>>(key: CachePrefecturesKey);
                }

                //インターネットに接続してれば取得
                if (Connectivity.NetworkAccess == NetworkAccess.Internet)
                {
                    //WebAPIからデータ取得
                    var prefecturesData = await _httpClient.GetFromJsonAsync<IEnumerable<Prefecture>>("prefectures");

                    //キャッシュを更新.キャッシュの有効期限を設定
                    _barrel.Add(key: CachePrefecturesKey, data: prefecturesData, expireIn: TimeSpan.FromMinutes(10));

                    return prefecturesData;
                }

                //インターネットにも接続してないし,キャッシュも有効期限切れだった場合
                //それでも有効期限切れのキャッシュデータを返すか.
                if (_barrel.Exists(key: CachePrefecturesKey))
                {
                    //とりあえず返すことにした.
                    return _barrel.Get<IEnumerable<Prefecture>>(key: CachePrefecturesKey);
                }

                return new List<Prefecture>();
            }
            catch
            {
                throw;
            }
        }
    }
}

MonkeyCacheの使い方

MonkeyCacheではデータをキーとともにBarrel(樽)というオブジェクトに入れたり出したりするシンプルなものです.

データを格納する際に有効期限を設定でき,有効期限内か有効期限切れかをチェックすることができます.利用者は,有効期限切れの際にはWebAPIからデータを再取得する,などを判断して実装することができます.

利用前の初期化

Barre.ApplicationIdに任意のユニークな文字列を設定します.Xamarin.EssentialsのAppInfo.PackageNameでアプリケーションのパッケージ名を指定すれば良いでしょう.

            Barrel.ApplicationId = AppInfo.PackageName;

次に,Barrelインスタンスをキャッシュ保存先のディレクトリのパスとともに生成します.

            this._barrel = Barrel.Create(FileSystem.AppDataDirectory);

保存先のディレクトリはXamarin.EssentialsのFileSystem.AppDataDirectoryを使うと良いでしょう.

Barrelのインスタンスですが,Createメソッドを使わずともBarrel.CurrentプロパティがSingletonでインスタンスを返してくれるようになっているので,保存先をおまかせするのであればCreateメソッドを使う必要はなく,以後のデータ格納,取得はBarrel.Current経由で行っても大丈夫です.

その場合は,MonkeyCacheのソースコードによると,以下のように各プラットフォーム毎に用意されているキャッシュ専用のディレクトリが使用されるようになっています.

  #if __IOS__ || __MACOS__
                basePath = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User)[0];
   #elif __ANDROID__
                basePath = Application.Context.CacheDir.AbsolutePath;
   #elif __UWP__
                basePath = Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path;
   #else
                basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
   #endif

これで利用の準備が整いました.

データの格納

Barrelインスタンスに対してAddメソッドでデータを格納します.

                    //キャッシュを更新.キャッシュの有効期限を設定
                    _barrel.Add(key: CachePrefecturesKey, data: prefecturesData, expireIn: TimeSpan.FromMinutes(10));
//もしくはBarrel.Current.Add(....

KeyとDataと有効期限をメソッドに渡すだけです.

Keyは任意の文字列,Dataはオブジェクトをそのまま,有効期限はTimeSpanで.ここでは有効期限を10分間にしています.

これでデータのキャッシュが完了です.

データの取得

格納したキャッシュデータを取り出す場合はGetメソッドを使います.

_barrel.Get<IEnumerable<Prefecture>>(key: CachePrefecturesKey);
//もしくはBarrel.Current.Get<.....

型引数に取り出したいデータの型を指定し,データ格納時に使用したKeyを指定することでデータを取得できます.

データが有効期限内か否かを確認する

データが指定した有効期限内か否かを確認するためにはIsExpiredメソッドを使います.

_barrel.IsExpired(key: CachePrefecturesKey)
//もしくはBarrel.Current.IsExpired(...

パラメータにデータ格納時に指定したKeyを指定することで,そのデータが有効期限内か否かをbool値で返してくれます.

使い方は以上のように至ってシンプルです.

ソースコード

github.com