MVVM 패턴의 장점
여러 화면이 있더라도 비슷한 데이터를 가지고 있는 애라면 같은 ViewModel을 공유할 수 있다.
(MVP 처럼 1:1이 아니기 때문)
ViewModel이 직접적으로 요소를 그리라고 View에게 요청하지 않기 때문.
MVVM 패턴의 단점
간단한 프로젝트에 사용하기에는 과하다.
비교적 구현 구조가 복잡하고 설계가 수비지 않다.
MVVM 패턴 구현
이번 예제의 프로젝트 구조입니다.
a. build.gradle (모듈 앱)
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
implementation 'com.orhanobut:logger:2.2.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
}
b. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.project1.viewmodel.ViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="오늘의 간식 당번"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.25" />
<TextView
android:id="@+id/user_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.winner}"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.4" />
<Button
android:id="@+id/ok_btnview"
android:layout_width="114dp"
android:layout_height="68dp"
android:text="돌리기"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
c. MainActivity.java
package com.example.project1.view;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.View;
import com.example.project1.R;
import com.example.project1.databinding.ActivityMainBinding;
import com.example.project1.model.Database;
import com.example.project1.viewmodel.ViewModel;
import com.orhanobut.logger.Logger;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding; //상속 ViewDataBinding
ViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Main_onCreate() 실행");
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
viewModel = new ViewModel(Database.getInstance());
binding.setViewModel(viewModel);
binding.okBtnview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Logger.d("버튼 클릭");
viewModel.getUser();
}
});
}
}
d. ViewModel.java
package com.example.project1.viewmodel;
import androidx.databinding.BaseObservable;
import com.example.project1.model.Database;
import com.example.project1.model.Person;
import com.orhanobut.logger.Logger;
import java.util.ArrayList;
import java.util.List;
public class ViewModel extends BaseObservable {
private Database database;
private List<Person> items = new ArrayList<>();
private String winner;
public ViewModel(Database database){
Logger.d("ViewModel 생성자 실행 | DB(Model) 참조");
this.database = database;
this.database.setOnDatabaseListener(new Database.DatabaseListener() {
@Override
public void onChanged() {
Logger.d("리스너 실행");
winner = null;
winner = database.getWinner();
notifyChange();
}
});
}
public void getUser() {
Logger.d("db에게 user(winner)를 달라고 요청");
database.getUser();
}
public String getWinner() {
Logger.d("Winner 변환 (%s)", winner);
return winner;
}
}
e. Database
package com.example.project1.model;
import com.orhanobut.logger.Logger;
import java.util.ArrayList;
public class Database {
private static Database instance;
private ArrayList<Person> personList = new ArrayList<>();
private String winner;
private DatabaseListener databaseListener;
private Database(){
Logger.d("Model인 Database 생성");
personList.add(new Person(0, "최00"));
personList.add(new Person(1, "김00"));
personList.add(new Person(2, "고00"));
personList.add(new Person(3, "문00"));
personList.add(new Person(4, "윤00"));
}
public static Database getInstance() {
Logger.d("Model에 접근 할 수 있도록 DB 인스턴스 값 요청");
if(instance == null) {
instance = new Database();
}
return instance;
}
public void getUser() {
Logger.d("당첨자 획득");
winner = personList.get((int)(Math.random()*5)).getName();
notifyChange();
}
private void notifyChange() {
if(databaseListener != null) {
Logger.d("Model | Data 변경 되어 notify 하라고 알림");
databaseListener.onChanged();
}
}
public void setOnDatabaseListener(DatabaseListener databaseListener) {
Logger.d("DatabaseListener 구현 객체 참조 변수 세팅 (arg1 : %s)", databaseListener.getClass().getSimpleName());
this.databaseListener = databaseListener;
}
public String getWinner() {
return winner;
}
public interface DatabaseListener {
void onChanged();
}
}
f. Person
package com.example.project1.model;
public class Person {
private long id;
private String name;
public Person(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}