SharedPreferences辛い
SharedPreferences周りのコードが大分辛い感じになってきたので、リファクタリングしようと思って色々と調べたところ、偉大な先人の方々のありがたい教えを発見しました。
http://qiita.com/operandoOS/items/8af20ac09a9d6acb075e
http://qiita.com/rejasupotaro/items/e112350e5a7db2febc74
SharedPreferencesをいい加減に使ってるとどう辛いかは、上の記事で十分説明されているので割愛します。
解決するライブラリ
で、解決策として上の記事で紹介されているライブラリ(Garum, kvs-schema)を使おうかと思ったのですが、 ちと今のプロジェクトにそのまま使いにくい事情(後述)があったので、三番煎じですがSpotというライブラリを作りました。
SharedPreferences用のObject data mapperです。
Garumやkvs-schemaも同じ用途で開発されていて人気もありそうなので、プロジェクトごとの事情に合えば勿論これらを使ってもよいと思います。
Spotはまだ開発途上なのでAPIなどまだ変わる可能性大ですが、基本機能が一通り動いたので以下に紹介します。
Spotの紹介
簡単な使い方
まず、Entityクラスにアノテーションを付けます
@Pref(name = "foo")
public class MyEntity {
// Integer field
// defaultValue can be omitted.
@PrefInt(name = "hoge", defaultValue = 1)
public int hoge;
// String field
// defaultValue can be omitted.
@PrefString(name = "fuga", defaultValue = "Hello")
public String fuga;
// Default constructor is needed.
public MyEntity() {}
}
そして、ビルドします。ビルド後、***SpotRepository
というリポジトリが生成されるので使います。
// Get
MyEntity entity = MyEntitySpotRepository.getEntity(context);
// Put
entity.hoge = 123;
entity.fuga = "Goodbye"
MyEntitySpotRepository.putEntity(context, entity);
以上。ね、簡単でしょ?
Spotの特徴
POJOサポート
これが今回Spotを作ろうと思った動機です。上のサンプルでも見たとおり、SpotではEntityがベースクラスを継承する必要はありません。
自分のプロジェクトでは、他のライブラリの都合でEntityがベースクラスを継承しないといけないライブラリは使いづらかったので、SpotではEntityクラスは継承しなくてよい設計にしました。
Annotation processing
パフォーマンスを落としたくないため、すべてannotation processingで関連クラスをビルド時に生成しており、ランタイムでリフレクションを内部で一切使っていません。とはいえ十分なパフォーマンス測定はできてません(そのうちやりたい)
任意のタイプを保存可能
SharedPreferencesでサポートされている型は、int
, long
, float
, boolean
, String
, Set<String>
だけですが、これらの型との変換クラスを用意することで任意のクラスを保存できます。
例えば、Dateクラスを使いたい場合、こんな感じでDate <=> Longの変換をするtype converterを用意すればよいです。
public class DateTypeConverter extends TypeConverter<Date, Long> {
@Override
public Date convertFromSupportedType(Long t) {
return new Date(t);
}
@Override
public Long convertToSupportedType(Date t) {
return t.getTime();
}
}
そして、Entityのアノテーションでtype converterを指定しましょう。
@Pref(name = "foo")
public class MyEntity {
@PrefLong(name = "date", converter = DateTypeConverter.class)
public Date date;
}
これだけで、SharedPreferencesに読み書きするときはLongに変換してくれます。使う側は気にせずDateが入っているEntityを操作できます。
余談:苦労した点
ライブラリ自体は対して大きくないのですんなり実装できたのですが、ライブラリの公開がとにかく苦労しました。
普段開発している他のライブラリではbintray-releaseというプラグインを使ってデプロイしてるのですが、aptを使ったライブラリによくある2 modules in 1 projectという構成だと、aptのcompiler(processor)の方のモジュールがデプロイされるがjcenterで公開されないという問題があって結局解決できず。。。WebUIから手動で公開だけすることもできるんですが、めんどくさいので諦めました。
で、手っ取り早い方法でJitPack.ioでの公開にしました。JitPackマジ最高。
※似たような問題/解決策ご存知のかたいらっしゃいましたら教えて下さいmm
まとめ
- SharedPreferencesをいい感じに扱うライブラリを作りました
- POJOサポート、リフレクションなし、任意のタイプサポート
- aptを使ったライブラリをうまくjcenterにデプロイできなかった