Android Architecture Patterns Part 2:Model-View-Presenter
It’s about time we developers start thinking about how we can apply good architecture patterns in our Android apps. ここでは、Erik Hellmanと私はMVP&RXJAVAサンプルで一緒に作業しました。 私たちがそれをどのように適用したか、そしてこのアプローチの長所と短所を見てみましょう。
ここでは、すべてのコンポーネントの役割があります。
- モデル—データレイヤー。 ビジネスロジックとネットワーク層およびデータベース層との通信を処理します。
- ビュー—UIレイヤー。 データを表示し、プレゼンターにユーザーの操作について通知します。
- Presenter—モデルからデータを取得し、UIロジックを適用してビューの状態を管理し、表示する内容を決定し、ビューからのユーザー入力通知に反応します。
ビューとプレゼンターは密接に連携するため、互いに参照する必要があります。 PresenterユニットをJUnitでテスト可能にするために、ビューは抽象化され、そのためのインターフェイスが使用されます。 プレゼンターとそれに対応するビューとの関係は、Contract
インターフェイスクラスで定義されているため、コードが読みやすくなり、両者の接続がわかdiv>
モデルビュープレゼンターパターン&rxjava in android architecture blueprints
ブループリントのサンプルは”to do”アプリケーションです。 これにより、ユーザーは「To Do」タスクを作成、読み取り、更新、削除したり、表示されたタスクリストにフィルタを適用したりできます。 Rxjavaは、メインスレッドから移動し、非同期操作を処理できるようにするために使用されます。
モデル
モデルは、データを取得して保存するために、リモートおよびローカルのデータソースで動作します。 ここでは、ビジネスロジックが処理されます。 たとえば、Task
のリストを要求すると、モデルはローカルデータソースからそれらを取得しようとします。 空の場合は、ネットワークにクエリを実行し、応答をローカルデータソースに保存してから、リストを返します。
タスクの取得はRxJavaの助けを借りて行われます:
public Observable<List<Task>> getTasks(){
...
}
モデルは、ローカルおよびリモートデータソースのコンストラクタインターフェイスのパラメータとして受信し、モデルをAndroidクラスから完全に独立させ、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インターフェイスを実装します。ビューは、onResume
subscribe
presenter.unsubscribe()
inonPause
を呼び出して、更新に関心がなくなったことをプレゼンターに伝えます。 ビューの実装がAndroidカスタムビューの場合、subscribe
unsubscribe
onAttachedToWindow
onDetachedFromWindow
で呼び出す必要があります。 ボタンのクリックなどのユーザーアクションは、プレゼンターで対応するメソッドをトリガーします。
ビューはEspressoでテストされます。 たとえば、統計画面には、アクティブなタスクと完了したタスクの数を表示する必要があります。 これが正しく行われていることを確認するテストは、最初にいくつかのタスクをTaskRepository
StatisticsActivity
を起動し、ビューの内容を確認します。
@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()));
}
Presenter
プレゼンターとそれに対応するビューは、アクティビティによって作成されます。 ビューとTaskRepository
-モデル-への参照は、Presenterのコンストラクターに与えられます。 コンストラクターの実装では、プレゼンターはビューのsetPresenter
メソッドを呼び出します。 これは、対応するビュー内のプレゼンターの注入を可能にし、クラスの結合を減らす依存性注入フレームワークを使用する場合に単純化できます。 Daggerを使用したToDo-MVPの実装については、別のサンプルで説明しています。
すべてのプレゼンターは同じBasePresenterインターフェイスを実装します。
public interface BasePresenter {
void subscribe();
void unsubscribe();
}
subscribe
メソッドが呼び出されると、プレゼンターはモデルからデータの要求を開始し、受信したデータにUIロジックを適用し、ビュー たとえば、StatisticsPresenter
TaskRepository
showStatistics(int numberOfActiveTasks, int numberOfCompletedTasks)
showStatistics
TaskRepository
StatisticsContract.View
StatisticsPresenter
オブジェクトのコンストラクタ テストの実装は次のとおりです。
@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
メソッドの役割は、プレゼンターのすべてのサブスクリプションをクリアするこ
subscribe
unsubscribe
AddEditTaskPresenter
createTask
のようなメソッドを追加します。 これにより、すべてのユーザーアクション、ひいてはすべてのUIロジックがPresenterを通過し、ユニットテストが可能になります。
Model-View-Presenterパターンの欠点
Model-View-Presenterパターンは、懸念の非常に良い分離をもたらします。 これは確かにプロですが、小さなアプリやプロトタイプを開発するとき、これはオーバーヘッドのように見えることが 使用されるインターフェイスの数を減らすために、一部の開発者はContract
インターフェイスクラスとプレゼンターのインターフェイスを削除します。
UIロジックをプレゼンターに移動すると、MVPの落とし穴の一つが表示されます。 これを解決するには、コードをさらに分割し、1つの責任のみを持ち、単体テスト可能なクラスを作成することを忘れないでください。
結論
Model-View-Controllerパターンには二つの主な欠点があります。; そして第二に、UIロジックの処理を単一のクラスに制限するものではなく、この責任はコントローラとビューまたはモデルとの間で共有されます。 Model-View—Presenterパターンは、ビューとモデルの接続を切断し、ビューのプレゼンテーションに関連するすべてを処理するクラスを1つだけ作成することで、これらの問
ビューが変更に反応するイベントベースのアーキテクチャが必要な場合はどうなりますか? これをどのように実装できるかを確認するために、Androidアーキテクチャのブループリントでサンプリングされた次のパター それまでは、updayアプリのModel-View-ViewModelパターン実装について読んでください。