システムをレイヤー化し、各レイヤーに独自の責任を持たせよう
データをローカルに格納するためにSQLiteに保存する場合がありますが、データベースの作成とかアクセスとかCursorとか少し面倒です。😧
自分はそんなめんどくささから、OrmaとDagger2を使用しています。
OrmaはAndroid用のORMで、実装としてはSQLiteDatabaseのラッパーとなっております。
Dagger2はGooglの依存性注入ライブラリーです。
Repositoryレイヤーはこんな感じにしています。
MVPなどで実装する場合、ご覧のようにPresenterはRepositoryと依存関係を持つ感じで、Repositoryオブジェクトだけでデータベース関連の操作を実行します。そうすることでアプリ全体のデータベースエントリーポイントとなります。
では深掘りして動作を理解していきましょう。
サンプルアプリを作成しましたのでご覧ください。
シンプルなTodoアプリです。
まずデータベーステーブルのアクセスに関するOrmaとmodel周りから
Ormaでは、テーブルに対応するスキーマはPOJOにアノテーションを付加したものです。Ormaのannotation processorがアノテーションをみてヘルパークラスを生成します。
(引用:モデルクラスの定義
@Table
public class Todo {
@PrimaryKey(autoincrement = true)
public long id;
@Column(indexed = true)
public String title;
@Column
@Nullable // allows NULL (default: NOT NULL)
public String content;
@Column(indexed = true)
public boolean done;
@Override
public String toString() {
return "Todo{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
", done=" + done +
'}';
}
}
次にデータベースを実行する上での基本的なクエリをインターフェースに定義します。
public interface DataSource<T> {
void add(T item);
void add(List<T> items);
void update(T item);
void update(List<T> items);
void remove(T item);
boolean isEmpty();
}
SELECT系のクエリはモデルによってフィルターをかけたり、Rxで処理したかったりと別れる場合があるのでDataSourceを継承したインターフェースを定義しています。
public interface TodoDataSource extends DataSource<Todo> {
Observable<Todo> fetchAsObservable(long id);
Single<List<Todo>> fetchAllAsSingle();
}
データベースハンドルであるOrmaDatabaseを使用してCRUD操作をする必要があるため、Daggerを使用して依存関係を作ります。
public class LocalTodoDataSource implements TodoDataSource {
private OrmaDatabase ormaDatabase;
@Inject
public LocalTodoDataSource(OrmaDatabase ormaDatabase) {
this.ormaDatabase = ormaDatabase;
}
}
これでデータベースのクエリを実行できる用意ができたので、Presenterがデータソースとやり取りできるようにするエントリーポイントであるRepositoryのインターフェースを作成します。
public interface Repository {
LocalTodoDataSource localTodoData();
}
そしてアクセスするためのRepositoryImplを作成します。
public class RepositoryImpl implements Repository{
public LocalTodoDataSource localTodoDataSource;
@Inject
public RepositoryImpl(LocalTodoDataSource localTodoDataSource) {
this.localTodoDataSource = localTodoDataSource;
}
@Override
public LocalTodoDataSource localTodoData() {
return localTodoDataSource;
}
}
最後に、Presenterにすべてのデータソースに依存するリポジトリレイヤを準備しますが、これらの依存関係はDaggerによってつくられます。 Daggerはこれらのデータソースの単一のインスタンスを保持し、必要なときにいつでも提供(Provide)します。
これで、リポジトリの依存関係を提供し、Todoのローカルデータを取り出すことができるようになります。
public class TodoListPresenter implements TodoListContract.Presenter {
Repository repository;
@Inject
public TodoListPresenter(Repository repository) {
this.repository = repository;
}
}
以上です。いかがでしょうか。リポジトリレイヤーを作成し、インターフェースでやりとりすることで保存する仕組みをOrma以外にRealmやPref、リモートのデータソースと切り離すこともできるようになると思います。
ご覧いただきありがとうございました! 😃
Happy Coding!