SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門

メンテナンスしやすいアプリを作成しよう! RoomをViewModelと組み合わせて利用する方法

一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門 第3回

  • X ポスト
  • このエントリーをはてなブックマークに追加

データ処理を一括に担うリポジトリ

 前節で、ViewModelからRoomが利用できるようになり、データベースのデータをViewModelから扱えるようになりました。ただし、この状態でも問題があります。次にその話をしていきます。

リポジトリの役割

 Androidアプリでは、ViewModelから直接Roomの処理を行うコードパターンは、推奨されていません。つまり、ここで紹介した単一テーブルの簡単なアプリの場合はよいのですが、テーブル数が複数になる、すなわち、DAOオブジェクトが複数になると、途端にViewModel内のデータ処理コードが煩瑣になります。さらに、データベースだけでなく、Web APIとの連携も含めたアプリとなると、さらに複雑なコードになります(図2)。

図2: ViewModelからデータ処理オブジェクトを直接操作すると問題
図2: ViewModelからデータ処理オブジェクトを直接操作すると問題

 そもそも、ViewModelはアクティビティに必要なデータを保持するクラスであって、データ処理クラスではありません。そこで、このようなViewModelに必要なデータを、データベースやWeb APIなどのデータソースから取得したり、格納したりする処理をまとめておくクラスを別に用意すべきです。Androidアプリ開発では、このクラスのことをリポジトリと呼んでいます(図3)。そして、今回のアプリのように、たとえデータソースがひとつのテーブルのみでも、リポジトリを導入することが推奨されています。

図3: データ処理をまとめておけるリポジトリ
図3: データ処理をまとめておけるリポジトリ

 もちろん、このリポジトリは複数存在することができます。その際、取得するデータの種類に応じてひとつずつ作成することが推奨されています。たとえば、会員に関するデータ処理をまとめておける会員リポジトリと注文に関するデータ処理をまとめておける注文リポジトリなどです。もちろん、それぞれのリポジトリが、さらに、複数のデータソースを利用する形になります。また、アクティビティとViewModelのペアも複数存在できるため、アプリ全体としては、図4のような構造になります。

図4: リポジトリは複数作成できる
図4: リポジトリは複数作成できる

リポジトリクラスは普通のクラス

 では、早速そのリポジトリクラスを導入していきます。実は、ViewModelクラスやRoomの各クラスと違い、リポジトリクラスは普通のクラスとして作成すればよいのです。例えば、カクテルメモ情報に関するデータ処理を行うリポジトリクラスとしてCocktailmemoRepositoryを作成すると、Javaコードではリスト8のコードとなります。

リスト8:CocktailmemoRepository(Java版)
public class CocktailmemoRepository {  // (1)
  private AppDatabase _db;  // (2)
  public CocktailmemoRepository(Application application) {  // (3)
    _db = AppDatabase.getDatabase(application);  // (4)
  }
  public Cocktailmemo getCocktailmemo(int cocktailId) {  // (5)
    CocktailmemoDAO cocktailmemoDAO = _db.createCocktailmemoDAO();
    ListenableFuture<Cocktailmemo> future = cocktailmemoDAO.findByPK(cocktailId);
    Cocktailmemo cocktailmemo = null;
    try {
       cocktailmemo = future.get();
    }
    catch(ExecutionException ex) {
      :
    }
    catch(InterruptedException ex) {
      :
    }
    return cocktailmemo;
  }
}

 リスト8の(1)をみてもわかるように、何かのクラスを継承したり、何かのインターフェースを実装したりといった特別なことが何もない、普通のクラスとして宣言します。そして、このリポジトリクラス内でRoomの利用コードを記述することになるので、(2)のように、フィールドでAppDatabaseを宣言し、(4)のようにコンストラクタ内でAppDatabaseオブジェクトを取得します。

 ただし、その際、やはりApplicationオブジェクトが必要であり、当然ですが、このクラス内では用意できません。そこで、(3)のようにコンストラクタの引数として定義し、このリポジトリクラスを利用するViewModelクラスから受け取るようにします。

 この処理コードさえ記述しておけば、あとは、(5)のように、Roomを利用して、カクテル情報を取得するメソッドなどを定義しておけばよいです。

 Kotlinコードでも同じであり、リスト9のようになります。リスト7のJavaコードを、Kotlinコードに置き換えただけのようなコードになっています。ただし、(1)のように、DAOメソッドを実行するメソッドでは、コルーチンスコープは不要であり、単にメソッド全体をsuspendメソッドとするだけでよいです。コルーチンスコープによる処理は、ViewModelに任せます。

リスト9:CocktailmemoRepository(Kotlin版)
class CocktailmemoRepository(application: Application) {
  private val _db: AppDatabase
  init {
    _db = AppDatabase.getDatabase(application)
  }
  suspend fun getCocktailmemo(cocktailId: Int): Cocktailmemo? {  // (1)
    val cocktailmemoDAO = _db.createCocktailmemoDAO()
    return cocktailmemoDAO.findByPK(cocktailId)
  }
}
ViewModelでのリポジトリの利用

 リポジトリが用意できたところで、ViewModelを、リポジトリを利用したものへと改造すると、Javaではリスト10のコードとなります。

リスト10:リポジトリを利用したMainViewModel(Java版)
public class MainViewModel extends AndroidViewModel {
  private CocktailmemoRepository _cocktailmemoRepository;  // (1)
  private int _cocktailId = -1;
    :
  public MainViewModel(Application application) {
    super(application);
    _cocktailmemoRepository = new CocktailmemoRepository(application);  // (2)
  }
  public void prepareCocktailNote() {
    _cocktailNote = "";
    Cocktailmemo cocktailmemo = _cocktailmemoRepository.getCocktailmemo(_cocktailId);  // (3)
    if(cocktailmemo != null) {  // (4)
      _cocktailNote = cocktailmemo.note;
    }
  }
  //以下アクセサメソッド
    :
}

 リスト10では、(1)のように、これまでフィールドとして保持していたAppDatabaseの代わりに、CocktailmemoRepositoryをフィールドとしています。そして、そのインスタンスの生成を、(2)のようにコンストラクタ内で行っています。これは、リスト8の(3)のように、リポジトリクラスのコンストラクタの引数としてApplicationが定義されており、これを渡すタイミングが(2)のようにコンストラクタ内しかないからです。

 このように、フィールドにリポジトリオブジェクトを用意しておくと、あとは、(3)のように、リポジトリのメソッドを実行することで必要なデータを用意することができます。なお、リスト8の(5)のgetCocktailmemo()メソッド内のコードからもわかるように、getCocktailmemo()メソッドの戻り値であるCocktailmemoオブジェクトはnullの可能性があります。そこを考慮して、(4)のようにnullチェックを行なっています。

 Kotlinコードも同様で、リスト11のようになります。このコードは特に問題ないでしょう。

リスト11:リポジトリを利用したMainViewModel(Kotlin版)
class MainViewModel(application: Application) : AndroidViewModel(application) {
  private val _cocktailmemoRepository: CocktailmemoRepository
  var cocktailId = -1
    :
  init {
    _cocktailmemoRepository = CocktailmemoRepository(application)
  }
  suspend fun prepareCocktailNote(): Job {
    val job =viewModelScope.launch {
      val cocktailmemo = _cocktailmemoRepository.getCocktailmemo(cocktailId)
      cocktailNote = cocktailmemo?.note ?: ""
    }
    return job
  }
}

まとめ

 Android Jetpackについて紹介していく本連載の第3回は、いかがでしたでしょうか。

 今回は、RoomをViewModelと組み合わせて利用する方法を紹介しました。さらに、よりメンテナンスしやすいアプリを作成するためのアーキテクチャであるリポジトリも、併せて紹介しました。

 次回は、非同期でのデータ更新を、そのタイミングに合わせて画面に反映させる仕組みであるLiveDataとFlowを紹介します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17569 2023/04/05 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング