スポンサーリンク

【Android】初心者が意識すべきMVVMでの開発ルール

Android アプリ開発時に MVVM パターンを利用した開発が多くあります。

最近はアーキテクチャコンポーネントでも採用されています。

Android の 「DataBinding」 を利用した 「MVVM」で責務を適切に分離することで可読性が高く改修やリファクタリングが容易なコードになります。

ソフトウェアアーキテクチャを導入することでの利点も多くあります。

クラスの役割、責任が明確になることです。

クラスの役割が明確になると、

「どの処理をどのクラスへ実装すべきか?」

「解読時に何処に実装されているのか?」

の迷いが無くなります。

迷いが無くなることで設計が簡素になり、可読性が良くなるのです。

そして、可読性が高くなることで、実装時、改修時の迷いが無なくなります。

と良いことだらけです。

また、MVVM での開発に慣れていない人がチームに含まれていた場合、上記の役割や分離を理解していないこともあります。

気付くと「ViewMode」に Layout の処理が存在したり、Modelへ書くべき処理がViewModelにかかれていたり、ViewModel で Layout の描画処理されていたりと負債が発生します。

ここでは、それぞれのクラスの役割の解説と、分離の方法を解説します。

今回は主に「layout」「Activity(Fragment)」「ViewModel」に関して解説します。

※ ここでは、Model の分離(Domain層、Repositoryなど)に関しての分離は解説しません。

チームメンバーでこのルールを意識し実装することで統一性のあるプロジェクトを目指しましょう。

また、これらを意識することで「MVVM」アーキテクチャーの基本を学ぶこともできるでしょう。

全体

「Layout」「Activity(Fragment)」「ViewModel」「Model」の各役割を学び、機能を分離することを意識しましょう。

迷った時に各クラスの役割とルールを確認し適切なコーディングをすることが重量です。

結果、可読性が高く、改修のしやすいプログラムになるでしょう。

Layout(View)

概要

DataBinding を利用し、Activity(Fragment) にて Layout と ViewModel の変数を紐付けます。

紐付けることで、ViewModel で更新された内容が Layout へ自動的に通知され、更新されます。

実装時の意識

UI(Layout)以外のロジックは書かない

ユーザーが参照するレイアウト(Xml)だけを記述します。

基本的には、「Bind された値(Live Data など)」「onClick イベント」以外は記述しません。

ロジックを記述してはいけません。と言うより基本記述できません。

ロジックをどうしても実装したい場合は、「BindingAdapter」を利用しましょう。

ViewModelを保持

基本的に ViewModelを保持します。

動的な表示データに関しては全て「ViewModel」にて紐付けます。

動的な変数が必要ない場合は保持する必要はないでしょう。

<!-- ViewModel を Bind し保持する -->
<data>

    <variable
        name="viewModel"
        type="com.example.ui.main.MainViewModel" />
</data>

動的変数を紐付ける

「Android:text=“@{viewModel.name}”」にて「text」へ設定する。

<!-- Bind した ViewModel の値を紐付ける -->
android:text="@{viewModel.name}"

OnClick イベント

紐付けられた ViewModel へ OnClick を作成し、Layout から呼び出します。

<!-- onClick イベント で呼び出すメソッドを指定する -->
android:onClick="@{() -> viewModel.onClick()}"

※ Action クラスを作成し Layout で OnClick 設定する方法もありますが、Databinding を利用して ViewModelへ持たせる方が良いでしょう。

※ Activity の遷移処理の場合、ViewModelから LiveData、Rx にて Activity へ通知しましょう。

BindingAdapter を利用する場合

別途、BindingAdapter を用意した場合は、下記のように呼び出します。

<!-- BindingAdapter を利用する場合 -->
app:visibleGone="@{viewModel.isVisible}"

Activity(Fragment)

以下、Activity で統一します。

概要

DataBinding を利用し、 Layout と ViewModel の動的変数を紐付けます。

実装時の意識

ViewModel の初期化

ViewModel を初期化と Layout への紐付けをし Binding にて保持します。

val viewModel = ViewModelProvider(
    this,
    viewModelFactory
).get(MainViewModel::class.java)

val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
    this,
    R.layout.activity_main
).also {
    it.viewModel = viewModel
    it.lifecycleOwner = this
}

アニメーションの処理

アニメーションは、View の処理なので、Activity へ処理を書きます。

アニメーションの開始と終了等の操作をします。

イベントの処理

ViewModel では処理できない場合は Activity にて View のイベントを操作をします。

Activity が必要な処理の場合、onClick を受け取ります。

binding.button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.v(TAG, "onClick");
    }
});

Activity の呼び出し

別の Activity を呼び出す場合、Activity から処理を呼びます。ViewModel などで呼び出していけません。

ViewModel からのコールバック

ViewModel での処理完了後の通知は、LiveData を利用します。

ViewModel.onClick.observe(this, Observer {
    Log.v(TAG, "onClick")
})

ViewModel のデータを取得

ViewModel のデータを取得する場合は、Binding にセットされた ViewModel から取得します。

Log.v(TAG, ViewModel.userName)

ViewModel

概要

Activity にて DataBinding を利用し、Layout と ViewModel の動的変数を紐付けます。

ViewModel の変数が変更されると自動的に Layout が更新されます。

Layout の状態を全て保持します。

Model を呼び出しデータの取得、変更を指示します。

OnClick 等、ユーザからのイベントを処理します。

既にViewModel にてデータを保持している場合はそのデータを利用して Model の処理を実行します。

実装時の意識

Layout の状態を全て保持

UIの表示が回転等にて変更されても破棄されず、取得したデータを保持し、Layout へ表示します。

Context は持たない

ViewModelから呼び出し用のラッパークラスを用意しましょう。

Drawable などの Resource を持ってはいけない。

Activity、Context を持ってはいけない。

Unitテストを意識する

Activity 以外は Pure Java で書くことを意識する。

View を操作しない

View を所持してはいけません。

持っていないので、View に直接アクセスできませんが、念のため、View を操作してはいけません。

layout から onClick を呼び出すのは問題ありません。

Layout からのイベントを受け取る

Layoutからの呼び出しは Layout にて「() -> viewModel.onclick()」を利用し呼び出す。

「viewMode::onClick」での呼び出しは「onClick(View view)」となり、ViewModel に View を持ってしまいます。

「() -> viewModel.onclick()」にて View を持たないようにしましょう。

<!-- Layout -->
android:onClick="@{() -> viewModel.onClick()}"

<!-- ViewModel -->
fun onClick() {
    Log.v(TAG, "onClick")
}

データの操作を直接しない

データの取得、変更は Model を通す。

例:SharedPreference を直接読んではいけない。

UserSettingRepository を通して呼ぶこと。

データを直接せつ制御してはいけない。

Model 非同期で呼び出し、コールバックにて処理

ViewModel は Model を呼び出し、LiveData を利用して処理の結果を取得する。

インスタンス破棄時の処理

「android.arch.lifecycle.ViewModel」を継承した場合は 「onCleared()」 をオーバーライドすることで破棄時の操作をしてください。

Model

※ ここでは、Model の分離(Domain層、Repositoryなど)に関しての分離は解説しません。

データの操作に関してのロジックは全て Model にて処理を行います。

  • データの保存、メモリ、データベースへからのデータ取得。
  • ネットワーク、API等を利用したデータの取得。
  • データベースの処理。
  • Entitiesの処理。
タイトルとURLをコピーしました