家族ToDo(仮)開発日誌

やりたいことや行きたい場所を家族で共有するためのAndoridアプリの開発日誌(兼Android開発学習メモ)です。

開発日誌 (4) : アプリのデータをJSONで保存する

今回の進捗

  • JSONライブラリJSONICの導入
  • android.content.Contextクラスを用いた端末上でのデータファイルIO

作業時間: 2.5h

端末ローカルへのデータ保存(JSON形式)

ネットワーク接続が不安定な状況を考えると、アプリのデータは端末ローカルとサーバ側の両方に保存するようにしたい。基本としては端末側にデータ保存を行い、定期的にサーバ側と同期をとるわけだ。

その際にローカル側に作成するデータファイルの形式について少し悩んだが、最終的にWeb APIで同期を行うことを考えるとやはりテキストデータであることが望ましい。テキストデータと言えばCSVXMLあたりが一般的と思うが、データとの親和性と取り扱いの容易さを考えると最近はもうJSONでいいだろう。

というわけでAndroidで使えるJSONライブラリを探してみた。下記サイトによれば、JSONICが一番シンプルで使いやすそうである。標準でもJSONObjectなるクラスが用意されているようだが、プリミティブすぎて取り扱いが面倒そうであった。

端末上でのファイルIO

android.content.ContextクラスのopenFileInput()、openFileOutput()を使用すれば、Android端末上のアプリローカルな領域に対するIO Streamが取得できる模様。

Androidでデータを簡単に保存する方法(ファイル入出力編) | Tech Booster

保存時と読み込み時のコードは下記のような感じ。はじめJSONからArrayListに復元できるかと思ったがうまくいかなかったのでToDoの配列を経由している。

public class LocalToDoProvider implements ToDoProvider {
    Context context;

    public LocalToDoProvider(Context context) {
        this.context = context;
    }

    @Override
    public ArrayList<ToDo> provideToDosOfUser(User user) {
        InputStream stream = null;
        ArrayList<ToDo> todoList = new ArrayList<ToDo>();

        try {

            String jsondata = FileUtils.readFileToString(this.context.getFileStreamPath("net.jibunstyle.kazokutodo.localcache"));

            ToDo[] todos = (ToDo[])JSON.decode(jsondata, ToDo[].class);
            todoList = new ArrayList<ToDo>(Arrays.asList(todos));
        } catch (IOException ex) {
            // TODO: handle
        } finally {
            try { stream.close(); } catch (Exception e) {}
        }
        return todoList;
    }

    @Override
    public void registerToDos(ArrayList<ToDo> todoList) {
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(context.openFileOutput("net.jibunstyle.kazokutodo.localcache", Context.MODE_PRIVATE));
            writer.write(JSON.encode(todoList));
            writer.close();
        } catch (IOException ex) {
            try { writer.close(); } catch (Exception e) {}
        }

    }
}

これで端末側でデータを保存・復元できるようになった。後に実装する予定のWeb APIに渡す際は、このJSON文字列をそのまま投げつければ良い。

daggerでInjectするオブジェクトの生成時に引数を渡す

上記クラスはファイルIOのためにContextクラスが必要なため、コンストラクタで受け取るようにしている。ただ、このクラスはdaggerによってインスタンス化を行っているため、下記サイトを参考に、まずModuleクラスのコンストラクタでContextをわたし、provideメソッドにて目的クラスのインスタンスを生成する際にそのContextを渡すようにしている。

Daggerを触ってみた - ほげほげ(仮)

文章で書くと全くわからんな。要は下記のようになっている。

@Module(injects = KazokuToDoController.class)
public class KazokuToDoModule {
    private String type = "local";
    private Context context = null;

    public KazokuToDoModule(String type, Context context) {
        this.type = type;
        this.context = context;
    }

(中略)

    @Provides
    public ToDoProvider provideWishListProvider() {
        ToDoProvider provider = null;
        if ("net.jibunstyle.kazokutodo".equals(this.type)) {
            // TODO: 本物を返す
        } else {
            provider = new LocalToDoProvider(context);
        }
        return provider;
    }