Android MVVMデザインパターン

このチュートリアルでは、AndroidアプリケーションでAndroid MVVMアーキテクチャパターンを検討して実装します。以前に、Android MVPパターンについて説明しました。

なぜこれらのパターンが必要なのですか?すべてを1つのアクティビティやフラグメントに追加すると、テストやコードのリファクタリングで問題が発生します。したがって、コードの分離とクリーンなアーキテクチャの使用が推奨されます。

Android MVVM

MVVMは、モデルビュービューモデルの頭字語です。

  • モデル:アプリケーションのデータを保持します。ビューと直接通信できません。一般的には、データをViewModelにObservables経由で公開することが推奨されます。
  • ビュー:アプリケーションのUIを表しますが、アプリケーションロジックはありません。ViewModelを監視します。
  • ViewModel:モデルとビューの間のリンクとして機能します。モデルからデータを変換する責任があります。ビューにデータストリームを提供します。また、ビューを更新するためのフックやコールバックを使用します。モデルからデータを要求します。

以下のフローは、コアMVVMパターンを示しています。 これはMVPとどう違いますか?

  • ViewModelは、中間層でのPresenterを置き換えます。
  • PresenterはViewへの参照を保持しますが、ViewModelはしません。
  • Presenterは、クラシックな方法(メソッドのトリガー)を使用してViewを更新します。
  • ViewModelはデータストリームを送信します。
  • PresenterとViewは1対1の関係にあります。
  • ViewとViewModelは1対多の関係にあります。
  • ViewModelは、Viewが自分を聞いていることを知りません。

AndroidでMVVMを実装する方法は2つあります:

  • Data Binding
  • RXJava

このチュートリアルでは、Data Bindingのみを使用します。 Data Bindingライブラリは、xmlレイアウトでデータを直接バインドするためにGoogleによって導入されました。 Data Bindingに関する詳細については、このチュートリアルを参照してください。ユーザーの入力を求めるシンプルなログインページの例を作成します。 ViewModelがViewの参照を保持せずにToastメッセージを表示するタイミングをViewに通知する方法を見てみましょう。

参照を持たないクラスに通知する方法は可能ですか? これは3つの異なる方法で行うことができます:

  • 双方向データバインディングを使用する
  • LiveDataを使用する
  • RxJavaを使用する

Two Way Data Binding

Two-way Data Bindingは、オブジェクトをXMLレイアウトにバインドする技術であり、オブジェクトとレイアウトの間で双方向にデータを送信できます。私たちの場合、ViewModelはレイアウトにデータを送信し、変更を観察することもできます。そのためには、XMLで定義されたBindingAdapterとカスタム属性が必要です。Binding Adapterは属性の変更を監視します。以下の例を通じてTwo-way Data Bindingについて詳しく学びましょう。

Android MVVMの例プロジェクト構造

データバインディングライブラリの追加

以下のコードをアプリのbuild.gradleファイルに追加します:

android {

    dataBinding {
        enabled = true
    }
}

これにより、アプリケーションでデータバインディングが有効になります。

依存関係の追加

次の依存関係をbuild.gradleファイルに追加してください:

implementation 'android.arch.lifecycle:extensions:1.1.0'

Model

モデルはユーザーのメールアドレスとパスワードを保持します。次のUser.javaクラスがそれを行います:

package com.journaldev.androidmvvmbasics.model;


public class User {
    private String email;
    private String password;

    public User(String email, String password) {
        this.email = email;
        this.password = password;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    public String getEmail() {
        return email;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    public String getPassword() {
        return password;
    }


}

双方向データバインディングを使用すると、オブジェクトをXMLレイアウトにバインドできるため、オブジェクトはレイアウトにデータを送信し、その逆も可能です。双方向データバインディングの構文は@={variable}です。

Layout

activity_main.xmlのコードは以下の通りです:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:bind="https://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel" />
    </data>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <EditText
                android:id="@+id/inEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:padding="8dp"
                android:text="@={viewModel.userEmail}" />


            <EditText
                android:id="@+id/inPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="8dp"
                android:text="@={viewModel.userPassword}" />


            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:onClick="@{()-> viewModel.onLoginClicked()}"
                android:text="LOGIN"
                bind:toastMessage="@{viewModel.toastMessage}" />


        </LinearLayout>

    </ScrollView>

</layout>

データバインディングを使用するには、レイアウトタグを先頭に設定する必要があります。ここで、ViewModelがデータをViewにバインドします。()-> viewModel.onLoginClicked()は、ViewModelで定義されたボタンクリックリスナーラムダを呼び出します。EditTextはモデル(via ViewModel)の値を更新します。bind:toastMessage="@{viewModel.toastMessage}"は、双方向データバインディングのために作成したカスタム属性です。ViewModelのtoastMessageの変更に基づいて、BindingAdapterがViewでトリガーされます。

ViewModel

LoginViewModel.javaのコードは以下の通りです:

package com.journaldev.androidmvvmbasics.viewmodels;

import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.text.TextUtils;
import android.util.Patterns;

import com.android.databinding.library.baseAdapters.BR;
import com.journaldev.androidmvvmbasics.model.User;

public class LoginViewModel extends BaseObservable {
    private User user;


    private String successMessage = "Login was successful";
    private String errorMessage = "Email or Password not valid";

    @Bindable
    private String toastMessage = null;


    public String getToastMessage() {
        return toastMessage;
    }


    private void setToastMessage(String toastMessage) {

        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
    }


    public void setUserEmail(String email) {
        user.setEmail(email);
        notifyPropertyChanged(BR.userEmail);
    }

    @Bindable
    public String getUserEmail() {
        return user.getEmail();
    }

    @Bindable
    public String getUserPassword() {
        return user.getPassword();
    }

    public void setUserPassword(String password) {
        user.setPassword(password);
        notifyPropertyChanged(BR.userPassword);
    }

    public LoginViewModel() {
        user = new User("","");
    }

    public void onLoginClicked() {
        if (isInputDataValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    }

    public boolean isInputDataValid() {
        return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches() && getUserPassword().length() > 5;
    }
}

レイアウトで呼び出されたメソッドは、上記のコードで同じシグネチャで実装されています。メソッドの XML 対応部分が存在しない場合は、属性をapp:に変更する必要があります。上記のクラスは、ViewModelを拡張することもできます。しかし、toastMessageプロパティが変更されたときにデータをストリームに変換して通知するために、BaseObservableが必要です。XML で定義された toastMessage カスタム属性の getter と setter を定義する必要があります。setter 内では、データが変更されたことを観測者(アプリケーション内の View になります)に通知します。View(私たちのアクティビティ)は適切なアクションを定義できます。

BR クラスは、プロジェクトを再構築するとデータバインディングから自動生成されます

「MainActivity.java」クラスのコードは以下の通りです:

package com.journaldev.androidmvvmbasics.views;


import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;


import com.journaldev.androidmvvmbasics.R;
import com.journaldev.androidmvvmbasics.databinding.ActivityMainBinding;
import com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.executePendingBindings();

    }

    @BindingAdapter({"toastMessage"})
    public static void runMe(View view, String message) {
        if (message != null)
            Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
    }
}

データバインディングのおかげで、ActivityMainBindingクラスがレイアウトから自動生成されます。toastMessage 属性がボタンで定義された場合に、@BindingAdapter メソッドがトリガーされます。XML および ViewModel で定義された同じ属性を使用する必要があります。したがって、上記のアプリケーションでは、ViewModel が View の変更を監視してモデルを更新します。また、モデルは ViewModel を介してビューを更新できます。notifyPropertyChangedを使用します。上記のアプリケーションの動作の出力は以下の通りです:これで、Android MVVM Using DataBinding のチュートリアルは終了です。以下のリンクからプロジェクトをダウンロードできます。

AndroidMVVMBasics

Githubプロジェクトリンク

Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern