このガイドでは、品質の高い堅牢なアプリを作成するためのおすすめの方法と推奨アーキテクチャを紹介します。
モバイルアプリのユーザー エクスペリエンス
標準的な Android アプリは、さまざまなアプリ コンポーネント(アクティビティ、フラグメント、サービス、コンテンツ プロバイダ、ブロードキャスト レシーバなど)で構成されています。これらのアプリ コンポーネントのほとんどを、アプリ マニフェストで宣言しておきます。Android OS ではこのアプリ マニフェスト ファイルを基に、デバイスの全体的なユーザー エクスペリエンスにアプリをどのように組み込むかを決定します。一般的な Android アプリには複数のコンポーネントが含まれており、ユーザーが短時間に複数のアプリを頻繁に操作することを考慮すると、アプリはユーザー主導のさまざまなワークフローとタスクに適応する必要があります。
また、モバイル デバイスはリソースに限りがあるため、オペレーティング システムは新たなアプリのリソースを確保するために、アプリプロセスを任意のタイミングで強制終了する場合があります。
このような環境の条件を考慮すると、アプリ コンポーネントは個別に順不同で起動され、オペレーティング システムやユーザーによって随時破棄される可能性があります。デベロッパーはこうしたイベントを制御できないため、アプリのデータや状態をアプリ コンポーネント内に保存したり、メモリ内に保持したりしないでください。また、アプリ コンポーネントは互いに依存してはなりません。
アーキテクチャに関する共通の原則
アプリのデータや状態の保存にアプリ コンポーネントを使用できないとなると、アプリをどのように設計すればよいのでしょうか。
Android アプリのサイズが大きくなるにつれ、アプリを拡張し、堅牢性を高め、アプリをテストしやすくするアーキテクチャを定義することが重要になります。
アプリ アーキテクチャは、アプリの各部分の境界と、各部分が担う役割を定義します。前述のニーズを満たすには、具体的な原則に沿ってアプリ アーキテクチャを設計する必要があります。
関心の分離
最も重要な原則は関心の分離です。すべてのコードを 1 つの Activity
または Fragment
に記述するのはよくある間違いです。これらの UI ベースのクラスには、UI やオペレーティング システムとのやり取りを処理するロジックのみを含めます。これらのクラスをできる限りシンプルに保つことで、コンポーネントのライフサイクルに関連する多くの問題を回避し、クラスのテストのしやすさを向上させることができます。
Activity
と Fragment
の実装はデベロッパーが管理するものではないことにご注意ください。これらのクラスは、Android OS とアプリの間のコントラクトを体現する単なる結合クラスです。Android OS は、ユーザーの操作に基づいて、またはシステムの状態(メモリ不足など)を理由として、いつでもこれらのクラスを破棄できます。十分なユーザー エクスペリエンスを実現し、アプリを管理しやすくするために、クラスへの依存を最小限に抑えることをおすすめします。
UI をデータモデルで操作する
もう 1 つの重要な原則は、UI をデータモデルで操作することです(永続モデルをおすすめします)。データモデルはアプリのデータを表し、アプリの UI 要素やその他のコンポーネントから独立しています。つまり、UI とアプリ コンポーネントのライフサイクルには関連付けられませんが、OS がアプリのプロセスをメモリから削除することを決定したときは、破棄されます。
永続モデルが望ましい理由として、次の点が挙げられます。
Android OS がアプリを破棄してリソースを解放してもデータが失われない。
ネットワーク接続が不安定または利用不可の場合でもアプリが動作し続ける。
アプリ アーキテクチャをデータモデル クラスに基づいて構築すると、アプリのテストのしやすさと堅牢性を高めることができます。
信頼できる唯一の情報源
アプリ内で新しいデータ型を定義するときは、信頼できる唯一の情報源(SSOT)を割り当てる必要があります。SSOT はそのデータの「オーナー」であり、SSOT のみがそのデータを変更またはミューテーションできます。そのために、SSOT は不変の型を使用してデータを公開します。SSOT がデータを変更するには、関数を公開するか、他の型が呼び出すことができるイベントを受け取ります。
このパターンには、次のようないくつかのメリットがあります。
- 特定のデータ型に対するすべての変更を 1 か所に集約できる。
- 他の型によって改ざんされないようにデータを保護できる。
- データに対する変更が追跡しやすくなり、それによりバグを見つけやすくなる。
オフライン ファーストのアプリでは、アプリデータの信頼できる情報源は、通常はデータベースです。場合によっては、ViewModel や UI が信頼できる情報源になります。
単方向データフロー
単方向データフロー(UDF)パターンに関する Android のガイドでは、信頼できる唯一の情報源の原則がよく使用されます。UDF では、状態は一方向にのみ流れます。データを変更するイベントはその反対方向に流れます。
Android では、一般的に状態またはデータは、上位スコープの階層の型から下位スコープの階層の型に流れます。一般的にイベントは、下位スコープの型からトリガーされ、対応するデータ型の SSOT に到達するまで流れます。たとえば、一般的にアプリデータはデータソースから UI に流れます。ボタンの押下などのユーザー イベントは UI から SSOT に流れ、SSOT でアプリデータが変更されて、不変の型で公開されます。
このパターンにより、データの整合性の保証が向上し、間違いの発生が減り、デバッグが簡単になります。つまり、SSOT パターンのすべてのメリットが実現されます。
アプリの推奨アーキテクチャ
このセクションでは、推奨されるベスト プラクティスに沿ってアプリを構築する方法について説明します。
前のセクションで説明したアーキテクチャに関する一般的な原則を考慮すると、各アプリに少なくとも 2 つのレイヤが必要です。
- 画面にアプリデータを表示する UI レイヤ。
- アプリのビジネス ロジックを含み、アプリデータを公開するデータレイヤ。
ドメインレイヤというレイヤを追加することで、UI レイヤとデータレイヤの間のやり取りを簡素化でき、再利用できます。

最新のアプリ アーキテクチャ
この最新のアプリ アーキテクチャでは、特に次の手法の使用が推奨されています。
- リアクティブで階層的なアーキテクチャ。
- アプリのすべてのレイヤにおける単方向データフロー(UDF)。
- UI の複雑さを管理する状態ホルダーを含む UI レイヤ。
- コルーチンとフロー。
- 依存関係挿入のベスト プラクティス。
詳細については、以下の各セクションおよび目次にあるその他のアーキテクチャ ページと、最も重要なベスト プラクティスの概要が記載されている推奨事項ページをご覧ください。
UI レイヤ
UI レイヤ(またはプレゼンテーション レイヤ)の役割は、アプリデータを画面に表示することです。ユーザー操作(ボタンの押下など)または外部入力(ネットワーク レスポンスなど)によってデータが変更されるたびに、変更を反映するように UI を更新する必要があります。
UI レイヤは次の 2 つのもので構成されています。
- データを画面にレンダリングする UI 要素。これらの要素は、View または Jetpack Compose 関数を使用して作成します。
- データを保持して UI に公開し、ロジックを処理する状態ホルダー(ViewModel クラスなど)。

このレイヤについて詳しくは、UI レイヤのページをご覧ください。
データレイヤ
アプリのデータレイヤには、ビジネス ロジックが含まれています。ビジネス ロジックはアプリに価値をもたらすものであり、アプリがデータを作成、保存、変更する方法を決定するルールで構成されています。
データレイヤは、それぞれが 0 から多数のデータソースを含むことができるリポジトリで構成されています。アプリで処理するデータの種類ごとにリポジトリ クラスを作成する必要があります。たとえば、映画に関するデータであれば MoviesRepository
クラス、支払いに関するデータであれば PaymentsRepository
クラスを作成します。

リポジトリ クラスは、次のタスクを行います。
- アプリの他の部分にデータを公開する。
- データの変更を一元管理する。
- 複数のデータソース間の競合を解決する。
- アプリの他の部分からデータソースを抽象化する。
- ビジネス ロジックを格納する。
各データソース クラスは、ファイル、ネットワーク ソース、ローカル データベースなど、1 つのデータソースのみを処理する役割を担う必要があります。データソース クラスは、データ オペレーションのためにアプリとシステムの橋渡しをします。
このレイヤについて詳しくは、データレイヤのページをご覧ください。
ドメインレイヤ
ドメインレイヤは、UI レイヤとデータレイヤの間に位置するオプションのレイヤです。
ドメインレイヤは、複雑なビジネス ロジック、または複数の ViewModel で再利用される単純なビジネス ロジックをカプセル化します。すべてのアプリにこのような要件があるわけではないため、このレイヤはオプションです。複雑さに対処する場合や再利用性を優先する場合など、必要な場合にのみ使用してください。

通常、このレイヤのクラスを「ユースケース」または「インタラクタ」と呼びます。各ユースケースは 1 つの機能を担うべきです。たとえば、複数の ViewModel がタイムゾーンに基づいて適切なメッセージを画面に表示する場合、アプリに GetTimeZoneUseCase
クラスを持たせることができます。
このレイヤについて詳しくは、ドメインレイヤのページをご覧ください。
コンポーネント間の依存関係を管理する
アプリのクラスは、適切に機能するために他のクラスに依存しています。次のいずれかのデザイン パターンを使用して、特定のクラスの依存関係を収集できます。
- 依存関係の注入(DI): 依存関係の注入を利用すると、クラスの依存関係を構築することなく定義できます。ランタイムには、別のクラスがこの依存関係を提供します。
- サービス ロケータ: サービス ロケータ パターンでは、クラスが依存関係を作成せずに取得できるレジストリが提供されます。
こうしたパターンでは、コードが重複して煩雑になることなく依存関係を明確に管理できるため、コードの拡張が可能になります。さらに、テスト版と製品版の実装を簡単に切り替えることができます。
依存関係の注入のパターンに沿って、Android アプリで Hilt ライブラリを使用することをおすすめします。Hilt では、依存関係ツリーをたどって自動的にオブジェクトが構築され、コンパイル時の依存関係が保証され、Android フレームワーク クラスの依存関係コンテナが作成されます。
一般的なベスト プラクティス
プログラミングは創造的な活動であり、Android アプリの作成も例外ではありません。問題の解決方法は数多くあります。複数のアクティビティやフラグメント間でデータをやり取りする、リモートデータを取得してオフライン モード用にローカルで永続化するなど、重要なアプリで対処する一般的なシナリオにはさまざまなものがあります。
以下の推奨事項は必須ではありませんが、ほとんどの場合これに沿うことで、コードベースの堅牢性を高め、テストとメンテナンスを長期にわたって容易に実施できるようになります。
アプリのコンポーネントにデータを格納しないでください。
アプリのエントリ ポイント(アクティビティ、サービス、ブロードキャスト レシーバなど)をデータソースとして指定しないでください。その代わり、そのエントリ ポイントに関連するデータのサブセットを取得する他のコンポーネントとの調整のみを行う必要があります。ユーザーによるデバイスの操作や、システムの現在の全体的な稼働状態によっては、各アプリ コンポーネントの生存期間がかなり短くなります。
Android クラスへの依存を減らします。
アプリ コンポーネントは、Context
や Toast
など、Android フレームワーク SDK API に依存する唯一のクラスにする必要があります。そこからアプリの他のクラスを抽象化すると、テストがしやすくなり、アプリ内の結合を軽減できます。
アプリの各種モジュール間の役割の境界を明確に定義します。
たとえば、ネットワークからデータを読み込むコードを、コードベース内の複数のクラスやパッケージに散在させないでください。同様に、関連のない複数の処理(データ キャッシングとデータ バインディングなど)を同じクラスで定義しないでください。アプリの推奨アーキテクチャに沿うと便利です。
各モジュールからの公開はできるだけ行わないでください。
たとえば、モジュールの内部実装の詳細を公開するショートカットを作成しようとしないでください。短期的には時間を少し節約できるかもしれませんが、コードベースが発展するにつれて何倍もの技術的負債を負うことになる可能性があります。
アプリの特別な部分に焦点を当てて、他のアプリとの差別化を図ります。
同じボイラープレート コードを何度も書いてすでにあるものを作り直すのではなく、アプリを特別なものにすることに時間とエネルギーを集中させましょう。繰り返しのボイラープレート コードの記述には Jetpack ライブラリやその他の推奨ライブラリを利用してください。
アプリの各部分を個別にテストできるようにする方法を検討します。
たとえば、ネットワークからデータを取得するための明確に定義された API を用意することで、そのデータをローカル データベースに永続化するモジュールを簡単にテストできるようになります。そうしないで、2 つのモジュールのロジックを 1 か所に混在させたり、ネットワーク用のコードをコードベース全体に分散させたりすると、テストが不可能にはならないにしても、非常に困難になります。
型は同時実行ポリシーに関する責任を負います。
ある型が時間のかかるブロック処理を実行している場合、その型は適切なスレッドに計算を移動する責任を負います。その特定の型は、実行している計算のタイプと、その計算をどのスレッドで実行する必要があるかを認識します。型はメインセーフである(つまり、ブロックせずにメインスレッドから安全に呼び出せる)ことが必要です。
データの関連性と新鮮さをできる限り維持します。
こうすることで、デバイスがオフライン モードのときでも、ユーザーがアプリの機能を利用できるようになります。すべてのユーザーが常に高速な接続を利用できるわけではなく、たとえ利用できるとしても、混雑した場所では受信不良が起きる可能性があることに留意してください。
アーキテクチャのメリット
優れたアーキテクチャをアプリに実装することは、プロジェクト チームとエンジニアリング チームに次のような多くのメリットをもたらします。
- アプリ全体の保守性、品質、堅牢性が向上します。
- アプリのスケーリングが可能になります。より多くの人々とチームが、コードの競合を最小限に抑えながら、同じコードベースで開発に寄与できます。
- オンボーディングに役立ちます。アーキテクチャによってプロジェクトに一貫性がもたらされるため、新しいメンバーが速やかにチームに適応し、短時間でより効率的に作業できるようになります。
- テストが簡単になります。優れたアーキテクチャでは、一般的にテストしやすいシンプルな型が推奨されます。
- 適切に定義されたプロセスを使用して、体系的にバグを調査できます。
アーキテクチャへの投資は、ユーザーにも直接的な影響を及ぼします。エンジニアリング チームの生産性が高まることで、アプリの安定性と機能性が向上します。ただし、アーキテクチャの実装には事前の準備時間の投資も必要です。社内の各部門に対して実装を正当化するには、こちらのケーススタディが役立ちます。優れたアーキテクチャをアプリに実装した企業の成功事例が紹介されています。
サンプル
以下の Google サンプルは、優れたアプリ アーキテクチャを実証するものです。このガイダンスを実践するためにご利用ください。
テキストの一部にスタイルを適用すると、読みやすさを高め、ユーザー エクスペリエンスを向上させ、色やフォントを使って創造性を高めることができます。 ユーザーがテキスト フィールドに入力する際に、入力内容を検証できます(名前、メールアドレス、住所などの連絡先情報の入力など)。この検証により、エラーが減り、ユーザーの時間を節約できます。 1 つのテキスト文字列に複数のリンクをサポートして、ユーザーに選択肢を提供し、エンゲージメントを高めることができます。 コンテンツやユーザー アクションへのレスポンスとしてアプリに画像を表示するには、ディスクまたはインターネット上の外部ソースから画像を読み込みます。 カードは、UI のマテリアル デザイン コンテナを提供します。 TopAppBar コンポーザブルを使用して、ユーザーがアプリ内の機能に移動してアクセスできるようにトップ アプリバーを作成します。 Compose アニメーション API を使用して、状態値をアニメーション化する方法、遷移を使用する方法、可視性やサイズの変更をアニメーション化する方法、クロスフェードをアニメーション化する方法について学びます。 ユーザーの切り替えに基づいてパスワードを非表示または表示するアイコンを作成することで、セキュリティを強化し、ユーザー エクスペリエンスを向上させることができます。 ドローアブル ファイルを読み込んでアニメーション画像を表示することで、アプリでよりインタラクティブで魅力的なユーザー エクスペリエンスを実現できます。アニメーション画像は、読み込みインジケーター、成功またはエラーのインジケーター、ゲーム開発の促進、その他のさまざまな UI 機能の作成に役立ちます。 取り外し可能なキーボードの構成変更を管理する方法について学習する。 アプリバーは、画面の上部または下部にあるコンテナで、主な機能とナビゲーション アイテムが含まれています。 進行状況インジケータには、オペレーションのステータスが表示されます。 小画面ではアプリの向きを制限するが、大画面では制限しない。 1 つの画面に収まらない大きさのコンテンツにユーザーがスクロールしてアクセスできるように、ページング リストを作成します。水平方向のページング リストは、画像、スライドショー、商品カルーセルなどのコンテンツをユーザーが移動する際に役立ちます。縦方向のページング リストは、記事など、ユーザーが多数のアイテムをスクロールする必要があるコンテンツが多いアプリに適しています。 画像の切り抜き領域の周囲にシャドウを描画して、クリップの形で画像を表示できます。 最初の Jetpack Compose テストを作成します。Compose のテスト アーティファクトを使用して UI テストを作成し、テストルール、ファインダー、アサーションを使用できるようにします。 UI に適したコンポーネントを選択し、アプリに実装する方法を学びます。 チップ コンポーネントは、複雑なエンティティを視覚的に表します。多くの場合、アイコンとラベルが使用されます。 ボタンは特定のアクションをトリガーします。 切り替えスイッチを使用すると、ユーザーが 2 つの状態のいずれかを選択できます。 フローティング アクション ボタンを使用すると、ユーザーはアプリでメイン アクションを実行できます。 ダイアログは、ポップアップ メッセージを表示したり、メインのアプリ コンテンツの上のレイヤでユーザー入力をリクエストしたりします。 構成の変更をまたいで WebView の状態を管理する。 タッチペンで手のひらを誤ってタップした場合に拒否する。テキストの一部にスタイルを設定する
ユーザーが入力するときに入力を検証する
1 つのテキスト文字列に複数のリンクをサポート
画像を読み込んで表示する
コンテナとしてカードを作成する
トップ アプリバーを表示する
Compose のアニメーション
ユーザーの切り替えに基づいてパスワードを表示または非表示にする
アニメーション画像を表示する
取り外し可能なキーボードの構成変更を管理する
アプリバーを表示する
進行状況インジケーターを作成する
スマートフォンではアプリの向きを制限するが、大画面デバイスでは制限しない
ページング リストを表示する
形状にクリップされた画像を表示する
Compose でのテスト
インタラクティブなコンポーネントを表示する
複雑なエンティティを表すチップを作成する
ボタンを作成する
ユーザーが切り替えられるスイッチを追加する
フローティング アクション ボタン(FAB)を作成する
ポップアップ メッセージやユーザー入力のリクエストを表示する
WebView の状態を管理する
タッチペンの手のひらによるタップを拒否する
現在、おすすめはありません。
Google アカウントにログインしてください。