본 포스팅은 DroidKaigi 2017 ~ DataBindingで実現するMVVM Architecture 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
DataBinding 로 구현하는 MVVM Architecture
주로 Model-View-ViewModel이 각각 무엇을 할 것인가 이야기
MVVM
Android에서 MVVM
View | ViewModel | Model | ||
---|---|---|---|---|
Activity | <–DataBinding–> | State | –Call Method–> | Besiness Logic |
Fragment | Presentation Logic | Domain | ||
Layout XML | <–Notification– | <–Notification– | Repository | |
API |
View | ViewModel | Model | ||
---|---|---|---|---|
Activity | State | Besiness Logic | ||
Fragment | –Dependency–> | Presentation Logic | –Dependency–> | Domain |
Layout XML | Repository | |||
API |
View
ViewModel의 상태를 반영
View | ViewModel | |
---|---|---|
Activity | State | |
Fragment | <–Reflect– | Property |
Layout XML |
// ViewModel
public class ViewModel {
public final ObservableField<String> title = new ObservableField<>();
}
<!-- 레이아웃 XML —>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.title}" />
// 사용자 정의 Setter 정의
public class ImageViewBinding {
@BindingAdapter("imageFromURL")
public static void loadImage(ImageView view, String url) {
Glide.with(view.getContext()).load(url).into(view);
}
}
<!-- 레이아웃 XML —>
<ImageView
android:layout_width="match_parent"
android:layout_height=“match_parent"
app:imageFromURL="@{viewModel.imageURL}" />
View의 입력을 ViewModel에 전달
View | ViewModel | |
---|---|---|
Activity | State | |
Fragment | –Input–> | Property |
Layout XML |
<!-- 레이아웃 XML —>
<EditText
android:id="@+id/edit_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={viewModel.title}" />
// ViewModel
public class ViewModel {
public final ObservableField<String> title = new ObservableField<>();
}
ViewModel에 이벤트 처리를 위임
View | ViewModel | |
---|---|---|
Event | –Dispatch–> |
<!-- 레이아웃 XML —>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:onClick="@{viewModel::onClickDone}"
android:src="@drawable/ic_done" />
// ViewModel
public class ViewModel {
public void onClickDone(View view) {
// ...
}
}
<!-- 레이아웃 XML —>
<!--suppress AndroidUnknownAttribute -->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{viewModel::onTextChanged}" />
// ViewModel
public class ViewModel {
public void onTextChanged(CharSequence s,
int start,
int before,
int count) {
// ...
}
}
// ViewModel
public class ViewModel {
public void onRefresh() {
// ...
}
}
<!-- 레이아웃 XML —>
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:onRefreshListener="@{viewModel::onRefresh}">
// Activity or Fragment
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_action:
viewModel.someAction();
return true;
}
return super.onOptionsItemSelected(item);
}
// ViewModel
public class ViewModel {
void someAction() {
// ...
}
}
ViewModel
View를 위한 상태유지 및 공개
View | ViewModel | |
---|---|---|
Activity | State | |
Fragment | <–Reflect– | Property |
Layout XML |
// ViewModel
public class ViewModel {
public final ObservableField<String> title = new ObservableField<>();
}
// ViewModel
public class ViewModel extends BaseObservable {
private String title;
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
notifyPropertyChanged(BR.title);
}
}
<!-- 레이아웃 XML —>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height=“wrap_content"
android:text="@{viewModel.title}" />
Model 처리 호출
ViewModel | Model | |
---|---|---|
Besiness Logic | ||
–Call Method–> | Domain | |
Repository | ||
API |
반환 값이 없는 메서드
호출밖에 없는 것ViewModel에 대한 Model 인터페이스
그것을 바탕으로 생각하면 ViewModel이 공개할 Model의 인터페이스는 다음의 두 가지밖에 없습니다.
- Model의 상태의 공개와 그 변경 알림
- Model의 작업에 대한 반환 값이 없는 메서드
// ViewModel
public class ViewModel {
public void onClickAction(View view) {
model.someOperation();
}
}
View에 변경 알림 이벤트
View | ViewModel | |
---|---|---|
Activity | <–Change Notification– | Property |
Fragment | ||
Layout XML | <–Change Notification– | Other |
// ViewModel
public class ViewModel {
public final ObservableField<String> title = new ObservableField<>();
public void something(String result) {
// 변경 알림
title.set(result);
}
}
// ViewModel
public class ViewModel extends BaseObservable {
private String title;
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
// 변경 알림
notifyPropertyChanged(BR.title);
}
}
View | EventBus | ViewModel | ||
---|---|---|---|---|
Activity | ||||
Fragment | ||||
–Subscribe–> | ||||
Something | <–Recieve– | <–Post– | Change Event |
EventBus
https://github.com/greenrobot/EventBus
// EventClass
public class ShowDialogEvent {
private final String message;
public ShowDialogEvent(String message) {
this.message = message;
}
}
// ViewModel
public class ViewModel {
public void something() {
EventBus.getDefault().post(
new ShowDialogEvent("message")
);
}
}
// View
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void showDialog(ShowDialogEvent event) {
// 다이얼로그 표시
}
View | ViewModel | |
---|---|---|
Activity | Subject | |
Fragment | ↓ OnNext | |
Something | –Subscribe–> | Observable |
Something | <–Recieve– | Observable |
RxJava
https://github.com/ReactiveX/RxJava
// ViewModel
public class ViewModel {
private final PublishSubject<String> showDialogSubject = PublishSubject.create();
final Observable<String> showDialog = showDialogSubject.asObservable();
public void something() {
// 알림 이벤트를 발행
showDialogSubject.onNext("message");
}
}
// View
private void subscribe() {
viewModel.showDialog.subscribe(message -> {
// 다이얼로그를 표시
});
}
private final PublishSubject<String> subject
= PublishSubject.create();
final Observable<String> observable
= subject.asObservable();
Model
View와 ViewModel 이외의 처리
ViewModel에 변경 알림 이벤트
ViewModel | Model | |
---|---|---|
<–Change Notification– | Entity | |
<–Change Notification– | Success | |
<–Change Notification– | Error |
// Model
// 성공
private final PublishSubject<Entity> entitySubject
= PublishSubject.create();
public final Observable<Entity> entity
= entitySubject.asObservable();
// 실패
private final PublishSubject<Void> errorSubject
= PublishSubject.create();
public final Observable<Void> error
= errorSubject.asObservable();
repository.get()
.subscribeOn(Schedulers.newThread())
.unsubscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Entity>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
// 실패 알림 이벤트
errorSubject.onNext(null);
}
@Override
public void onNext(Entity entity) {
// 성공 알림 이벤트
entitySubject.onNext(entity);
}
});
// ViewModel
model.entity.subscribe(entity -> {
// 성공
});
model.error.subscribe(aVoid -> {
// 실패
});
// ViewModel
public class ViewModel {
public final ObservableField<Entity> entity = new ObservableField<>();
}
<!-- 레이아웃 XML —>
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.entity.name}" />
public class Entity extends BaseObservable {
private String name;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
// 값이 설정되면 알림
notifyPropertyChanged(BR.name);
}
// 무언가 조작해서 Model 상태를 변경
public void someOperation() {
// …
setName(value);
}
}
정리
https://github.com/STAR-ZERO/AndroidMVVM
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024