최대한 작은 기능으로 나눈다.
테스트가 쉬워지고 관리하기가 용이하다.
SOLID 구조를 지향
약한 결합 컴포넌트를 가지게된다.
가장 큰 목적은 유지 보수가 쉽고 테스트가 용이한 코드를 작성
하는것이다.
위와 같은 앱 아키텍처를 권장하면서 앱 개발자가 수월하게 아키텍처를 구현할 수 있게 Android Architecture Components를 통해서 지원을 해주는것이다.
단방향 레이어를 가지는 것을 볼 수 있다.
View는 옵저버 패턴을 이용해 ViewModel을 참조하고 ViewModel의 데이터가 갱신될 때 마다 등록된 옵저버를 통해 이를 파악하고 처리할 수 있게 된다.생명 주기
를 고려해서 UI와 관련된 데이터를 저장하고 관리하도록 설계되었다. MVC 패턴을 떠올려 보면 View와 Controller를 명확하게 나누지 못해 View와 Controller가 상당히 비대해진다는 문제가 있다. 액티비티나 프래그먼트와 같은 UI 컨트롤러는 사용자와 interaction하고, view를 표시하는 역할을 수행해야 하는데 Controller와의 경계가 명확하지 않아 UI Controller 클래스가 가지는 책임이 커져버리면 테스트 코드를 작성하기가 상당히 까다로워진다.
ViewModel은 뷰나, 액티비티 컨텍스트를 포함하는 클래스를 참조하면 안된다
화면이 rotated 되어 onDestory가 호출 되더라도 ViewModel 객체의 데이터는 사라지지 않는다는 것이다.
ViewModel Scope를 그림을 통해 자세히 살펴보자.finish() 메서드가 호출되서 완전히 액티비티가 종료되고 난 이후에 onCleared()가 호출되고 viewModel객체가 메모리에서 사라지게 된다.
ViewModel은 액티비티가 Destory되고 다시 Create 되는 동안에도 계속 살아있기 때문에 ui가 가지고 있어야할 데이터를 안전하게 가지고 있을 수 있다.
어떻게 가능한 것일까
ViewModel을 상속 해야한다.
사용자가 생성하는 ViewModel은 ViewModelStore 객체를 통해서 관리된다. ViewModelStore 객체는 내부에 HashMap<String,ViewModel>타입의 map 객체를 두어 key,value 형태로 ViewModel을 관리하게 된다. 별도로 ViewModel 객체를 관리하기 때문에 ViewModel은 activity의 수명주기를 파악해서 activity와 맞는 Scope를 가질 수 있게된다.
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
ViewModelStore 객체를 관리하는 것은 ViewModelStoreOwner를 통해서 하게 되는데 ViewModelStoreOwner는 다음과 같다.
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
그렇다면 ViewModelStoreOwner는 어디서 구현하고 있을까 ?
지금까지 내용을 살펴보면 ViewModel이 어디선가 관리 된다는 것을 알 수 있는데 단순히 MyViewModel() 처럼 객체를 생성한다고 자동으로 관리되는것 같지는 않다. 그럼 어떻게 해야 ViewModel을 제대로 생성할 수 있을까 ?
ViewModelProvider 객체를 생성하고 get메서드를 호출해야한다.
가장 간단한 생성자를 살펴보자.
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
ViewModelProvider의 생성자로 액티비티 또는 프래그먼트를 전달 해줘야 하는것이다.
생성자로 액티비티 또는 프래그먼트를 전달해 ViewModelProvider 객체를 생성하고 아래 get 메서드를 호출해 어떤 viewModel 클래스를 얻고싶은지 알려줘야한다.
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
ViewModel을 생성하기 위해 ViewModelProvider를 이용해야하는것을 알게 되었으니 ViewModelProvider().get을 호출하면 어떻게 ViewModel이 생성되고 관리되는지 프로세스를 하나씩 살펴보자.
다음과 같이 Provider객체를 생성하고 get메서드를 호출할 경우 내부 동작을 살펴보겠다.
viewModel = ViewModelProvider(requireActivity()).get(AddTaskViewModel::class.java)
ViewModelProvider 객체 생성
ViewModelProvider는 팩토리 패턴을 사용한다고 언급했다. 파라미터로 팩토리 클래스를 넘겨주지 않으면 ViewModelProvider 내부에 자체적으로 구현해 둔 팩토리 클래스를 사용할 수 있게 저장해둔다.
위의 생성자가 호출되어 넘겨준 activity가 mViewModelStore에 저장되고 자동으로 생성된 (혹은 파라미터로 넘겨주는) 팩토리 클래스가 mFactory에 저장된다.
ViewModelProvider객체의 get메서드를 호출한다.
get(AddTaskViewModel::class.java) 와 같이 메서드를 호출하게 될 경우 내부에서` AddTaskViewModel 클래스 이름을 이용해 unique한 key를 생성하고 또다른 get 메서드를 호출한다.
밑줄 친 부분을 보면 넘겨받은 key를 이용해 mViewModelStore에서 ViewModel 객체를 꺼내오는 과정을 수행하는것을 볼 수 있는데 mViewModelStore 는 아까 위에서 보았던 ViewModelStoreOwner의 getViewModelStore()를 통해 가져올 수 있으며 이 모든 구현들이 Activity와 Fragment 단에 추가 되어 있는것이다.
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
mViewModelStore에서 가져온 ViewModel객체가 파라미터로 넘겨받은 Class와 동일하면 해당 객체를 반환하고 그렇지 않다면 (기존에 생성된 ViewModel이 없다면 생성하고 mViewModelStore에 저장 후 반환한다.
이어서 작성 예정 ..