안드로이드 MVVM 디자인 패턴

이 튜토리얼에서는 Android 애플리케이션에서 Android MVVM 아키텍처 패턴을 논의하고 구현할 것입니다. 이전에 Android MVP 패턴에 대해 논의했습니다.

이러한 패턴이 필요한 이유는 무엇인가요? 단일 Activity나 Fragment에 모든 것을 추가하면 테스트 및 코드 리팩토링에 문제가 발생할 수 있습니다. 따라서 코드 분리와 클린 아키텍처의 사용이 권장됩니다.

Android MVVM

MVVM은 Model, View, ViewModel의 약자입니다.

  • Model: 애플리케이션의 데이터를 보유합니다. View와 직접 통신할 수 없습니다. 일반적으로 데이터를 ViewModel에게 Observables를 통해 노출하는 것이 권장됩니다.
  • View: 응용 프로그램 로직이 없는 애플리케이션의 UI를 나타냅니다. ViewModel을 관찰합니다.
  • ViewModel: Model과 View 간의 연결 역할을 합니다. Model에서 데이터를 변환하는 것이 책임입니다. View에 데이터 스트림을 제공합니다. 또한 View를 업데이트하기 위해 후크 또는 콜백을 사용합니다. Model에서 데이터를 요청합니다.

MVVM 패턴의 핵심을 설명합니다.
MVP와 어떻게 다른가요?

  • Middle Layer에서 Presenter를 ViewModel로 교체합니다.
  • Presenter는 View에 대한 참조를 유지합니다. ViewModel은 그렇지 않습니다.
  • Presenter는 전통적인 방식으로 View를 업데이트합니다(메서드 트리거).
  • ViewModel은 데이터 스트림을 전송합니다.
  • Presenter와 View는 1:1 관계에 있습니다.
  • View와 ViewModel은 1:N 관계에 있습니다.
  • ViewModel은 View가 수신 중인지 알지 못합니다.

Android에서 MVVM을 구현하는 두 가지 방법이 있습니다.

  • Data Binding
  • RXJava

이 자습서에서는 Data Binding만 사용합니다. Data Binding Library는 Google에서 소개한 것으로, xml 레이아웃에서 데이터를 직접 바인딩하는 데 사용됩니다. Data Binding에 대한 자세한 내용은 여기 자습서를 참조하세요.
우리는 간단한 로그인 페이지 응용 프로그램을 생성해 사용자 입력을 요청할 것입니다. 우리는 ViewModel이 View에 알리는 방법을 볼 것입니다 Toast 메시지를 표시해야 할 때 뷰에 대한 참조를 유지하지 않는 방식.

어떻게 ViewModel이 참조하지 않는 클래스를 알리는 것입니까? 다음 세 가지 방법으로 수행할 수 있습니다.

  • 두 가지 데이터 바인딩 사용
  • Live Data 사용
  • RxJava 사용

    (번역 by Google)

양방향 데이터 바인딩

양방향 데이터 바인딩은 객체를 XML 레이아웃에 바인딩하는 기술로, 객체와 레이아웃이 서로 데이터를 전송할 수 있습니다. 우리의 경우에는 ViewModel이 레이아웃에 데이터를 보낼 수도 있고 변경 사항을 관찰할 수도 있습니다. 이를 위해 XML에 정의된 BindingAdapter와 사용자 정의 속성이 필요합니다. 바인딩 어댑터는 속성의 변경 사항을 감시합니다. 아래 예제를 통해 양방향 데이터 바인딩에 대해 자세히 알아보겠습니다.

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}입니다

레이아웃

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이 데이터를 뷰에 바인딩합니다. ()-> viewModel.onLoginClicked()는 ViewModel에 정의된 버튼 클릭 리스너 람다를 호출합니다. EditText는 모델(뷰 모델을 통해)의 값을 업데이트합니다. bind:toastMessage="@{viewModel.toastMessage}"는 우리가 두 방향 데이터 바인딩을 위해 생성한 사용자 정의 속성입니다. ViewModel의 toastMessage가 변경되면 BindingAdapter가 뷰에서 트리거됩니다.

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();
    }
}

DataBinding 덕분에 ActivityMainBinding 클래스는 레이아웃에서 자동으로 생성됩니다. @BindingAdapter 메서드는 Button에 정의된 toastMessage 속성이 변경될 때마다 트리거됩니다. XML과 ViewModel에서 정의된 것과 동일한 속성을 사용해야 합니다. 따라서 위의 애플리케이션에서 ViewModel은 View의 변경 사항을 듣고 모델을 업데이트합니다. 또한 모델은 notifyPropertyChanged를 사용하여 ViewModel을 통해 뷰를 업데이트할 수 있습니다. 위의 애플리케이션의 작동 결과는 아래에 제공됩니다: 안드로이드 MVVM 사용 데이터 바인딩에 대한 이 튜토리얼은 여기까지입니다. 아래 링크에서 프로젝트를 다운로드할 수 있습니다.

AndroidMVVMBasics

깃허브 프로젝트 링크

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