일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 백트래킹
- 트리
- dp
- spring boot
- Zuul
- spring cloud
- 스프링 시큐리티
- 완전 탐색
- 스택
- docker-compose
- 유레카
- 다익스트라
- ZuulFilter
- Gradle
- 서비스 디스커버리
- 메모이제이션
- 비트마스킹
- 이분 탐색
- 달팽이
- Spring Cloud Config
- 이분 매칭
- 플로이드 와샬
- Logback
- 구현
- 도커
- 구간 트리
- 게이트웨이
- BFS
- 주울
- Java
- Today
- Total
Hello, Freakin world!
[Android] ViewModel, LiveData 를 이용한 예제 본문
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");
}
}
}
간단하게 새 인스턴스를 리턴한다. (성의없는 예외처리는 눈감아주시라...)
실행화면