Articles

Android Architecture Patterns Part 2:Model-View-Presenter

Florina Muntenescu
Florina Muntenescu

Follow

Nov 2, 2016 · 6 min read

It’s about time we developers start thinking about how we can apply good architecture patterns in our Android apps. 이를 돕기 위해 Google 은 Erik Hellman 과 mvp&RxJava 샘플에서 함께 작업 한 Android 아키텍처 청사진을 제공합니다. 우리가 그것을 어떻게 적용했는지와이 접근법의 장단점을 살펴 보겠습니다.

다음은 모든 구성 요소의 역할입니다.

  • 모델—데이터 계층. 비즈니스 로직을 처리하고 네트워크 및 데이터베이스 계층과의 통신을 담당합니다.
  • 보기-UI 레이어. 데이터를 표시하고 발표자에게 사용자 작업에 대해 알립니다.
  • 발표자—데이터를 검색합니다 모델에서 적용한 UI 로직을 관리하고 국가의 보기를,무엇을 결정을 표시하고 반응하는 사용자 입력에서 알림을 보기입니다.

뷰와 발표자가 서로 긴밀히 협력하기 때문에 서로에 대한 참조가 필요합니다. 발표자 단위를 JUnit 으로 테스트 할 수있게하려면보기가 추상화되고 그에 대한 인터페이스가 사용됩니다. 사이 관계는 발표자와 해당보기 정의에서는Contract인터페이스 클래스 코드 읽기 쉽고 둘 사이의 연결하여 쉽게 이해할 수 있게 해줍니다.

모 발표자 클래스 구조

모델뷰-Presenter 패턴&RxJava 안드로이드식 건물 청사진

청사진을 샘플은”to”응용 프로그램입니다. 사용자가”할 일”작업을 작성,읽기,업데이트 및 삭제할 수있을뿐만 아니라 표시된 작업 목록에 필터를 적용 할 수 있습니다. RxJava 는 주 스레드를 이동하고 비동기 작업을 처리 할 수 있도록하는 데 사용됩니다.

모델

모델와 함께 작동 원격 및 로컬 데이터 원본을 얻을하고 데이터를 저장합니다. 이것은 비즈니스 로직이 처리되는 곳입니다. 예를 들어Tasks 목록을 요청할 때 모델은 로컬 데이터 소스에서 검색하려고합니다. 비어 있으면 네트워크를 쿼리하고 응답을 로컬 데이터 소스에 저장 한 다음 목록을 반환합니다.

작업의 검색은 RxJava 의 도움으로 이루어집니다:

public Observable<List<Task>> getTasks(){ 
...
}

모델을 받으로 매개변수에서 생성자 인터페이스의 로컬 및 원격 데이터 소스를 만들고,모델을 완전히 독립적인에서 모든 안드로이드 클래스고,따라서 쉽게 단위 테스트 JUnit. 예를 들어,을 테스트하는getTasks에서 데이터를 요청 지원,우리가 구현한 다음과 같은 테스트:

@Mock 
private TasksDataSource mTasksRemoteDataSource;
@Mock
private TasksDataSource mTasksLocalDataSource;
...
@Test
public void getTasks_requestsAllTasksFromLocalDataSource() {
// Given that the local data source has data available
setTasksAvailable(mTasksLocalDataSource, TASKS);
// And the remote data source does not have any data available
setTasksNotAvailable(mTasksRemoteDataSource);
// When tasks are requested from the tasks repository
TestSubscriber<List<Task>> testSubscriber =
new TestSubscriber<>();
mTasksRepository.getTasks().subscribe(testSubscriber); // Then tasks are loaded from the local data source
verify(mTasksLocalDataSource).getTasks();
testSubscriber.assertValue(TASKS);
}

보기

View 작동으로 발표자는 데이터를 표시하고 그것을 알리는 발표자에 대해 사용자의 작업입니다. MVP 활동에서 조각 및 사용자 정의 Android 뷰는 뷰가 될 수 있습니다. 우리의 선택은 조각을 사용하는 것이 었습니다.

모든 뷰는 발표자를 설정할 수있는 동일한 BaseView 인터페이스를 구현합니다.

public interface BaseView<T> {
void setPresenter(T presenter);
}

View 알 발표자는 그것은 준비가 된 것입 업데이트를 호출하여subscribeonResume. View 전화presenter.unsubscribe()onPause말을 발표자는 그것에 관심이 더 이상 업데이트되고 있습니다. 면의 구현 View 안드로이드 사용자 지정 보기,다음subscribeunsubscribeonAttachedToWindowonDetachedFromWindow. 같은 사용자 동작 버튼을 클릭 트리거 방법을 해당 발표자,이는 결정 하는 것을 어떻게 해야 합니다 다음.

뷰는 에스프레소로 테스트됩니다. 예를 들어 통계 화면에는 활성 및 완료된 작업의 수를 표시해야합니다. 테스트를 확인하는 것이 먼저 올바르게를 둔 몇 가지 작업에는TaskRepositoryStatisticsActivity을 검사합의 콘텐츠 보기

@Before 
public void setup() {
// Given some tasks
TasksRepository.destroyInstance();
TasksRepository repository = Injection.provideTasksRepository( InstrumentationRegistry.getContext()); repository.saveTask(new Task("Title1", "", false));
repository.saveTask(new Task("Title2", "", true)); // Lazily start the Activity from the ActivityTestRule
Intent startIntent = new Intent();
mStatisticsActivityTestRule.launchActivity(startIntent);
}@Test
public void Tasks_ShowsNonEmptyMessage() throws Exception {
// Check that the active and completed tasks text is displayed
Context context = InstrumentationRegistry.getTargetContext();
String expectedActiveTaskText = context
.getString(R.string.statistics_active_tasks);
String expectedCompletedTaskText = context
.getString(R.string.statistics_completed_tasks); onView(withText(containsString(expectedActiveTaskText)))
.check(matches(isDisplayed()));
onView(withText(containsString(expectedCompletedTaskText)))
.check(matches(isDisplayed()));
}

발표자

발표자와 해당 보기에 의해 만들어집니다. 뷰 및TaskRepositorysetPresenter메소드를 호출합니다. 이 단순화 할 수있다 사용할 경우 종속성을 주입할 수 있는 프레임워크를 주입의 발표자는 해당 전망을 감소 커플링의 클래스입니다. 단검을 사용한 ToDo-MVP 의 구현은 다른 샘플에서 다루어집니다.

모든 발표자는 동일한 BasePresenter 인터페이스를 구현합니다.

public interface BasePresenter {
void subscribe();
void unsubscribe();
}

경우subscribe방법이라고 발표자 시작한 데이터를 요청하는 모델에서 다음에 적용되 UI 논리를 수신한 데이터로 설정합니다. 예를 들어,StatisticsPresenterTaskRepositoryshowStatistics(int numberOfActiveTasks, int numberOfCompletedTasks)메소드의 매개 변수로 사용됩니다.

단위 테스트를 확인하는 실제로showStatisticsTaskRepositoryStatisticsContract.ViewStatisticsPresenter개체입니다. 시험 구현이다.

@Test 
public void loadNonEmptyTasksFromRepository_CallViewToDisplay() {
// Given an initialized StatisticsPresenter with
// 1 active and 2 completed tasks
setTasksAvailable(TASKS); // When loading of Tasks is requested
mStatisticsPresenter.subscribe(); // Then the correct data is passed on to the view
verify(mStatisticsView).showStatistics(1, 2);
}

의 역할을unsubscribe방법은 명확한 모든 구독의 발표자,따라서 피하는 메모리 누수를 방지합니다.

Apart fromsubscribeunsubscribeAddEditTaskPresentercreateTask것,호출할 때 사용자가 버튼을 누르면 새로 만드는 작업입니다. 이렇게하면 모든 사용자 작업(결과적으로 모든 UI 논리)이 발표자를 통과하여 단위 테스트를 수행 할 수 있습니다.

Model-View-Presenter Pattern 의 단점

Model-View-Presenter pattern 은 매우 좋은 우려 분리를 가져옵니다. 이것은 확실히 프로이지만,작은 앱이나 프로토 타입을 개발할 때 이것은 오버 헤드처럼 보일 수 있습니다. 의 수를 줄이기 위한 인터페이스에 사용되는,일부 개발자를 제거하는Contract인터페이스 클래스 및 인터페이스에 대한 발표자입니다.

함정의 한 MVP 나타나는 이동할 때 UI 논리를 발표자:이것이 이제는 모든 것을 알고있는 클래스로 수천 개의 라인의 코드입니다. 이를 해결하기 위해,분리 코드를 더욱 기억하는 클래스를 만들 하나만 있 책임은 단위 테스트 가능한.

결론

모델-뷰-컨트롤러 패턴에는 두 가지 주요 단점이 있습니다.; 둘째,UI 논리의 처리를 단일 클래스로 제한하지 않습니다.이 책임은 컨트롤러와 뷰 또는 모델간에 공유됩니다. 모델자인 패턴을 발표자 모두 해결하여 이러한 문제를 깨는 연결을 보기에는 모델을 만들기 클래스를 하나만을 처리하는 모든 것에 관련된 프레젠테이션의 전망—발표자:단일 클래스는 단위 테스트입니다.

뷰가 변경 사항에 반응하는 이벤트 기반 아키텍처를 원한다면 어떻게해야합니까? 이것이 어떻게 구현 될 수 있는지 확인하기 위해 안드로이드 아키텍처 청사진에서 샘플링 된 다음 패턴을 계속 지켜봐주십시오. 그때까지 upday 앱에서 모델-View-ViewModel 패턴 구현에 대해 읽어보십시오.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다