정상에서 IT를 외치다

[Android, MVP] MVP 기본 예제 본문

안드로이드

[Android, MVP] MVP 기본 예제

Black-Jin 2019. 2. 7. 14:26
반응형


안녕하세요. 블랙진입니다.


안드로이드 MVP 를 활용한 토이 프로젝트 리뷰 포스팅입니다. 총 3단계에 걸쳐서 앱을 리뷰하겠습니다.


1. MVP 기본 예제

2. Room 사용하기

3. Dagger 사용하기


<작업>


1. http://api.randomuser.me/?results=10 API를 통해 랜덤한 깃허브 유저 데이터를 받아옵니다.

2. 받아온 데이터를 Recycler View 에 보여줍니다.

3. API 데이터 외 Like 값을 넣을 수 있는 로컬 변수를 추가해 주었습니다.

4. 유저의 상세화면에서 Like 버튼을 누른 갯수 만큼 유저 리스트에서 유저의 Like 값이 증가됩니다.


위 작업물은 링크에서 다운받으실 수 있습니다.


그럼 <작업> 에서 보이는 앱을 MVP 패턴으로 바꾼 코드에 대해 리뷰를 시작하겠습니다.

MVP 변환 작업이 끝난 결과물을 보고 싶은신 분인 MyAndroidArchitecture를 확인해 주세요.


1. MVP?

MVP는 model, view, presenter 의 아키텍처로 이루어진 구조입니다.


View 에서는 오로지 사용자가 보고 있는 화면의 상태변화만을 보여줍니다. 사용자가 화면을 클릭 했을 때의 이벤트는 Presenter 로 전달됩니다. Presenter 에서는 뷰에서 받아온 액션을 처리하는 비즈니스 로직이 들어갑니다.



2. BaseContract

View 와 Presenter 는 1:1로 연결되기 때문에 뷰의 연결과 해제를 위한 함수를 정해줍니다.

public class BaseContract {

//View 와 Presenter 는 1:1로 연결되기 때문에 뷰의 연결과 해제를 위한 함수를 정해줍니다.
public interface Presenter<T> {

void setView(T view);

void releaseView();
}

//View 의 공용 함수가 필요한 경우 정해줍니다.
public interface View {

}
}


3. Main

Main은 랜덤유저 API를 통신해 화면에 유저 리스트로 보여줍니다.


MainContract

public interface MainContract {

interface View extends BaseContract.View {

void showProgress();

void hideProgress();

void showToast(String message);

// 아이템을 어댑터에 연결해 줍니다.
void setItems(ArrayList<User> items);

// 단일 아이템에 변경되었음을 알려줍니다.
void updateView(User user);

}

interface Presenter extends BaseContract.Presenter<View> {

@Override
void setView(View view);

@Override
void releaseView();

// API 통신을 통해 데이터를 받아옵니다.
void loadData();

// RxEventBus 를 연결하여 Like 값을 동기화 해줍니다.
void setRxEvent();
}
}

MainActivity 의 Contract 입니다. View 와 Presenter 에 관한 내용을 한눈에 볼수 있습니다.


MainAcitvity

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("RANDOM USER");

// recycler view 초기화
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
adapter.setClickListener(this);

// presenter 와 연결
presenter.setView(this);

// 랜덤 유저 데이터를 받아옵니다.
presenter.loadData();

// RxEventBus 를 연결합니다.
presenter.setRxEvent();
}

@Override
protected void onDestroy() {
super.onDestroy();

// presenter 와의 연결을 해제합니다.
presenter.releaseView();
}

@Override
public void onClick(User user) {
// 어탭터 리스터로 아이템 클릭 시 상세 유저 화면으로 넘어갑니다.
// intent 통해 user 데이터를 넘겨줍니다.
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra(DetailActivity.KEY_USER, user);
startActivity(intent);
}

@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}

@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}

@Override
// 아이템을 어댑터에 연결해 줍니다.
public void setItems(ArrayList<User> items) {
adapter.setItems(items);
}

@Override
// 단일 아이템에 변경되었음을 알려줍니다.
public void updateView(User user) {
adapter.updateView(user);
}

데이터를 받아오는 부분과 Like 클릭 시 이뤄지는 비즈니스 로직은 모두 Presenter 이뤄지며 Activity 에서는 오로지 뷰의 변화만을 코드로 보여주고 있습니다.


MainPresenter

public MainPresenter() {
this.api = GithubApiProvider.provideGithubApi();
this.disposable = new CompositeDisposable();
}

생성자를 통해 통신을 위한 변수를 초기화 해줍니다.

@Override
public void loadData() {

disposable.add(api.getUserList(Constant.RANDOM_USER_URL)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(__ -> {
view.showProgress();
})
.doOnTerminate(() -> {
view.hideProgress();
})
.subscribe(userResponse -> {
view.setItems((ArrayList<User>)userResponse.userList);
}, error -> {
view.showToast(error.getMessage());
})
);

}

RxJava 를 사용해 Api 통신을 합니다.

@Override
public void setRxEvent() {

disposable.add(
RxEvent.getInstance()
.getObservable()
.subscribe(
object -> {
if(object instanceof User) {
view.updateView((User) object);
}
},
// 아래 코드는 생략 가능 합니다.
error -> {
Log.d("MyTag","onError");
},
() -> {
Log.d("MyTag","onCompleted");
}
)
);
}

RxJava 를 사용해 유저의 단일 아이템을 업데이트 해줍니다.

@Override
public void releaseView() {
disposable.clear();
}

마지막으로 disposable 변수를 사용했기 때문에 presenter 가 해제가 되면 disposable 변수를 clear 해줍니다.



4. Detail

Detail른 유저 상세정보를 보여주고 Like 버튼을 클릭하여 갯수를 늘릴 수 있습니다.



DetailContract

public interface DetailContract {

interface View extends BaseContract.View {

void setText(String text);
}

interface Presenter extends BaseContract.Presenter<View> {

@Override
void setView(DetailContract.View view);

@Override
void releaseView();

void clickEvent(User user);
}
}

View 에서는 like 갯수의 변화를 보여줄 setText 함수와

Presenter 에서는 버튼을 클릭했을 때의 비즈니스 로직을 처리합니다.


DetailActivity

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

// presenter 와 연결
presenter = new DetailPresenter();
presenter.setView(this);

// MainActivity 로부터 User 객체를 받아옵니다.
getUserFromIntent();
}

private void getUserFromIntent(){
user = (User) getIntent().getSerializableExtra(KEY_USER);
setTitle(user.getFullName());

// 받아온 User 객체로 부터 View 를 초기화 해줍니다.
initView(user);
}

MainActivity 와 달리 preenter.relaseView() 함수를 설정해 주지 않았습니다. 여기서는 따로 Presnter 을 해제해줄 필요가 없습니다.


DetailPresenter

@Override
public void clickEvent(User user) {
// Like 클릭시 발생하는 비즈니스 로직입니다.
// User 객체의 LikeCnt 값을 늘려 줍니다.
user.likeCnt++;

// view 상태에 변화를 줍니다.
view.setText(user.getLikeCnt());

// RxEventBus 를 호출하여 MainActivity 에 변화가 생겼음을 알려줍니다.
RxEvent.getInstance().sendEvent(user);
}

이렇게 MVP 패턴을 활용한 간단한 깃허브 예제를 작성해 보았습니다. 여기에 Room 을 활용하여 클릭한 유저들을 저장해보고 Dagger 를 사용하여 외부에서 객체를 주입할 수 있는 방법에 대해 차례로 포스팅을 진행해보겠습니다.


위 예제 코드는 링크 에서 확인하실 수 있습니다.


1. MVP 기본 예제

2. Room 사용하기

3. Dagger 사용하기



반응형
Comments