Hello, Freakin world!

[Android] ViewModel, LiveData 를 이용한 예제 본문

Android

[Android] ViewModel, LiveData 를 이용한 예제

johnna_endure 2020. 2. 18. 22:28

ViewModel 왜 쓰는거지?

ViewModel을 쓰는 가장 큰 이유는 코드를 액티비티, 프래그먼트 생명주기에 종속되지 않게 할 수 있다는 점 때문이다.

위 그림처럼 ViewModel의 생명주기는 액티비티 생명주기와 독립적이다. 보통 onCreate에서 viewModel 객체를 생성한다.
이럴 경우 액티비티가 finish될 때까지 viewModel 객체는 유지된다. 따라서 앱의 설정 변화와 무관하게 데이터를 유지하고 가져올 수 있다.

뷰모델의 위와 같은 특징 때문에 데이터 로직을 UI와 분리시켜 작성할 수 있다.
그리고 LiveData 와 함께 사용하면 좀 더 리액티브한 프로그래밍을 할 수 있다.

LiveData는 또 뭔데?

이 클래스는 변화가 감지 가능한 데이터 홀더 클래스다.
음... 어렵게 생각할 필요가 전혀 없다. 옵저퍼 패턴에서 데이터를 감싼 래퍼 클래스라고 생각하면 편할 수도 있겠다.

간단하게 그림으로 살펴보자.

예제

UI

요소

  • 이름을 입력할 수 있는 하나의 EditText
  • 입력된 이름을 보여줄 TextView
  • 입력된 이름을 TextView로 보내줄 입력 버튼

시나리오

이름을 입력하고 입력 버튼을 누르면 아래 텍스트뷰에 입력한 이름이 보여진다.

ViewModel 작성하기

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class NameViewModel extends ViewModel {
    private MutableLiveData<String> currentName;
    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }
}

MutableLivaData 는 그저 데이터를 감싸기 위한 wrapper 클래스다. 어렵게 생각하지 말자.
getCurrentName()를 통해 currentName 필드에 접근하도록 한다.
MutableLiveData를 반환하는데 나중에 setValue(), postValue() 같은 메서드를 통해 값을 변화시킬 수 있다.

MainActivity 작성하기

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    EditText nameInputView;
    TextView nameTextView;
    Button inputButton;

    NameViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ui 컴포넌트 바인딩
        nameInputView = findViewById(R.id.inputName);
        nameTextView = findViewById(R.id.nameView);
        inputButton = findViewById(R.id.inputButton);

        //뷰모델 객체 생성
        viewModel = new ViewModelProvider(this, new NameViewModelFactory())
                .get(NameViewModel.class);

        //옵저버 정의 - 데이터가 변하는 이벤트 발생시 처리할 핸들러(람다)
        Observer<String> nameObserver = newName -> nameTextView.setText(newName);

        //뷰모델에 옵저버 등록
        viewModel.getCurrentName().observe(this, nameObserver);

        //ui 이벤트 처리 - 뷰모델 이용
        inputButton.setOnClickListener(view -> {
            String inputName = nameInputView.getText().toString();
            viewModel.getCurrentName().setValue(inputName);
        });
    }
}

UI Contoller(액티비티, 프래그먼트)에서 onCreate 메서드에 뷰모델 객체를 생성하고
옵저버를 정의한 뒤, 뷰모델 객체에서 지켜볼 데이터(model : 비록 viewModel 객체 안에 model도 포함되어 있긴 하지만.)를 등록한다. 이 부분은 MVVM 패턴을 조금 알아본다면 쉽게 이해가 가능하다.

 

뷰모델 객체를 생성할 때, 두번째 파라미터에 Factory를 넘겨줘야 한다. 이 Factory는 viewModel 객체를 생성하는데 사용되는 객체로 따로 구현해서 넘겨주자.
그런데 공식 홈페이지도 그렇고 모든 예제에서 factory를 생략한, 파라미터가 1개인 생성자를 이용하는데 sdk 29 버전을 쓰고 있는 현재, 그 생성자는 없어진 듯하다.(내가 못찾은건가...?)

Factory 구현하기

import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

public class NameViewModelFactory implements ViewModelProvider.Factory {
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        try {
            return modelClass.newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("팩토리 런타임 에러");
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException("팩토리 런타임 에러2");
        }
    }
}

간단하게 새 인스턴스를 리턴한다. (성의없는 예외처리는 눈감아주시라...)

실행화면

Comments