Android UIアーキテクチャの進化:MVCからMVVMへの実践的移行

EffectiveAndroidUIは、AndroidアプリにおけるUI層の設計を改善するためのベストプラクティスを示すサンプルプロジェクトです。このプロジェクトでは、MVPおよびMVVM(データバインディングエンジンなし)の両方を実装しており、アーキテクチャパターンの違いを実コードで比較・理解できるようになっています。プロジェクトリポジトリ: https://gitcode.com/gh_mirrors/ef/EffectiveAndroidUI

アーキテクチャの変遷:MVCの限界とその先

従来のMVCでは、ActivityやFragmentがビューとロジックの両方を担うため、肥大化しやすくテストも困難でした。EffectiveAndroidUIでは、MVPとMVVMを用いて関心事を明確に分離し、保守性と拡張性を高めています。

MVPでは、PresentationLogicHandlerクラスがビジネスロジックを担当し、ビューとのやり取りはインターフェース経由で行われます:

public class ShowListPresenter implements PresenterContract {
    private final ShowListView view;
    private final DataSource dataSource;

    public ShowListPresenter(ShowListView view, DataSource source) {
        this.view = view;
        this.dataSource = source;
    }

    public void loadShows() {
        List<TvShow> shows = dataSource.fetchAll();
        view.displayShows(shows);
    }
}

一方、MVVMではShowDetailViewModelがデータの取得・加工を担当し、UIコンポーネントはその状態を監視して描画を更新します:

public class ShowDetailViewModel {
    private final MutableLiveData<List<EpisodeItem>> episodes = new MutableLiveData<>();

    public LiveData<List<EpisodeItem>> getEpisodes() {
        return episodes;
    }

    public void fetchEpisodes(String showId) {
        List<Episode> rawEpisodes = repository.getEpisodes(showId);
        List<EpisodeItem> items = transform(rawEpisodes);
        episodes.setValue(items);
    }

    private List<EpisodeItem> transform(List<Episode> episodes) {
        return episodes.stream()
            .map(EpisodeItem::new)
            .collect(Collectors.toList());
    }
}

MVPの実装ポイント

MVPでは、Fragmentが特定のViewインターフェースを実装することで、Presenterとの結合を弱めています。たとえばShowListFragmentは次のように定義されます:

public class ShowListFragment extends Fragment implements ShowListView {
    private ShowListPresenter presenter;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        presenter = new ShowListPresenter(this, new RemoteDataSource());
        presenter.loadShows();
    }

    @Override
    public void displayShows(List<TvShow> shows) {
        adapter.updateData(shows);
    }
}

これにより、プレゼンテーションロジックが完全にFragmentから切り離され、ユニットテストが容易になります。

MVVMの実装戦略(バインディングなし)

このプロジェクトのMVVM実装は、AndroidのDataBindingやViewBindingに依存せず、ViewModelとLiveDataのみで構成されています。ShowDetailFragmentでは、ViewModelのLiveDataをオブザーブしてUIを更新します:

public class ShowDetailFragment extends Fragment {
    private ShowDetailViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        viewModel = new ViewModelProvider(this).get(ShowDetailViewModel.class);
        viewModel.getEpisodes().observe(getViewLifecycleOwner(), this::renderEpisodes);
    }

    private void renderEpisodes(List<EpisodeItem> items) {
        episodeAdapter.submitList(items);
    }
}

このアプローチは、バインディングライブラリを使わずにMVVMの本質——「状態駆動のUI」——を体現しています。

プロジェクト構造の特徴

  • ui/:Activity、Fragment、Presenter、ViewModelを含むUI層
  • domain/:TvShow、Episodeなどのドメインモデルとユースケース
  • data/:リポジトリとデータソース(ローカル/リモート)
  • di/:手動DIまたはシンプルなコンテナによる依存性管理
  • executor/:非同期処理のスレッド制御

なぜこのプロジェクトが学習に適しているのか

  1. 実装の対比が可能:同一機能をMVPとMVVMで実装し、設計の違いを直接比較できる
  2. 最小限の外部依存:アーキテクチャの本質に集中できるよう、ライブラリを極力使用していない
  3. テスト可能性:各コンポーネントがインターフェースや状態ベースで構成され、ユニットテストが容易
  4. 現実的なスケール:単純なTODOアプリではなく、複数画面・データフローを持つ実用的な構造

EffectiveAndroidUIを通じて、Android開発者はUIアーキテクチャの進化を実践的に理解し、自らのプロジェクトに応じた適切なパターンを選択・適用する力を養うことができます。

タグ: Android MVP MVVM Architecture LiveData

8月14日 12:46 投稿