Androidプロジェクト構成とは?フォルダ構造・Gradle・Manifest・テスト・モジュール設計を徹底解説
Androidアプリ開発を始めると、最初に理解しておきたいのがプロジェクト構成です。Android Studioで新しいプロジェクトを作成すると、app、src、main、res、AndroidManifest.xml、build.gradle.kts、test、androidTest など、多くのフォルダや設定ファイルが自動生成されます。Android公式ドキュメントでも、Android Studioのプロジェクトは、アプリのソースコード、アセット、テストコード、ビルド構成など、アプリの作業空間を定義するものを含むと説明されています。
これらのファイルやフォルダは、単なる置き場所ではありません。画面を作る場所、処理を書く場所、画像や文字列を管理する場所、アプリの権限を宣言する場所、依存関係を追加する場所、テストを書く場所、ビルドやリリースを制御する場所として、それぞれ明確な役割を持っています。Androidプロジェクト構成を理解しないまま開発を進めると、最初は動いていても、画面数が増えた段階でコードの置き場所が曖昧になり、修正やテスト、リリース作業が難しくなります。
本記事では、Androidプロジェクト構成を20の大きな観点に分けて詳しく解説します。初心者が最初に覚えるべき基本構成から、実務で重要になるGradle、Manifest、リソース管理、テスト、環境別設定、マルチモジュール、CI/CD、セキュリティ、長期運用しやすい設計まで、Android開発で役立つ構成の考え方を体系的に整理します。
1. Androidプロジェクト構成の全体像
Androidプロジェクト構成を理解する第一歩は、アプリが単なるKotlinコードの集合ではないことを知ることです。Androidアプリは、ソースコード、リソース、Manifest、Gradle設定、テスト、署名情報、依存関係、ビルドバリアントなど、複数の要素が組み合わさって完成します。公式ドキュメントでも、Androidビルドシステムはアプリのリソースとソースコードをコンパイルし、テスト、デプロイ、署名、配布できるAPKなどへパッケージ化すると説明されています。
1.1 プロジェクトはアプリ全体の作業空間
Android Studioのプロジェクトは、アプリを作るための作業空間です。ここには、画面やロジックを書くKotlin・Javaコードだけでなく、画像、文字列、テーマ、Manifest、テストコード、Gradle設定、依存ライブラリ、署名設定などが含まれます。初心者は MainActivity.kt や build.gradle.kts だけを見てしまいがちですが、実際にはプロジェクト全体の構成を理解しなければ、アプリがどのようにビルドされ、どの設定が最終成果物へ反映されるのかを把握できません。特に実務では、開発環境、本番環境、無料版、有料版、顧客別版などを1つのプロジェクトで管理することがあるため、全体構成の理解が非常に重要になります。
1.2 モジュールは機能や成果物を分ける単位
Androidプロジェクトでは、1つのプロジェクトの中に複数のモジュールを持つことができます。最初に作成される代表的なモジュールは app で、これはアプリ本体をビルドする中心的な単位です。小規模アプリでは app モジュールだけでも問題ありませんが、規模が大きくなると、core、feature-login、feature-home、data、domain のように役割ごとにモジュールを分けることがあります。Android公式のモジュール化ガイドでも、複数のGradleモジュールを持つプロジェクトはマルチモジュールプロジェクトと呼ばれ、コードベースを整理するための推奨パターンが紹介されています。
1.3 srcはコードとリソースの基本入口
src フォルダは、Androidプロジェクトのソースファイルをまとめる基本的な場所です。通常は src/main が共通部分として使われ、そこにKotlin・Javaコード、リソース、Manifestなどが配置されます。公式ドキュメントでは、src はソースファイルをソースセットとしてまとめ、main ソースセットはすべてのビルドバリアントに共通するソースコードとリソースを含むと説明されています。 この仕組みを理解すると、debug用、release用、flavor用の差分をどこへ置けばよいかが分かりやすくなり、環境別の開発やリリース管理も整理しやすくなります。
1.4 resはUIと表示資産を管理する場所
res フォルダは、画像、文字列、色、テーマ、レイアウト、メニュー、ナビゲーションなど、アプリの見た目や表示に関わるリソースを管理する場所です。Androidでは、文字列や色、画像をコードに直接書くのではなく、リソースとして外部化する考え方が重要です。これにより、多言語対応、ダークモード対応、端末密度対応、画面サイズ対応がしやすくなります。たとえば、同じ文字列でも日本語用は values-ja、英語用は values-en に分けられ、同じ画像でも端末密度に応じて適切なリソースを選べるようになります。
1.5 Gradleはビルド全体を制御する
Gradleは、Androidプロジェクトのビルドを制御する中心的な仕組みです。どのSDKを使うか、どのライブラリに依存するか、どのビルドタイプを作るか、どの署名設定を使うか、どのプラグインを適用するかなど、ビルドに関する多くの情報をGradleが管理します。Android公式ドキュメントでも、モジュールレベルの build.gradle.kts では重要なアプリ設定を構成すると説明されています。 Gradleを理解していないと、依存関係の衝突、ビルドエラー、releaseビルドの不具合、variant設定の混乱に対応しにくくなります。
1.6 構成理解は保守性に直結する
Androidプロジェクト構成を理解することは、コードを綺麗に置くためだけではありません。構成を理解しているチームは、画面、状態管理、データ取得、リソース、テスト、ビルド設定を明確に分けられるため、機能追加や不具合修正が早くなります。逆に、構成ルールが曖昧なプロジェクトでは、同じ処理が複数の場所に作られ、不要なリソースが残り、ManifestやGradleが肥大化し、テストも追加しにくくなります。長期運用するアプリほど、最初のプロジェクト構成とその後の整理方針が開発効率に大きく影響します。
2. appモジュールの役割
app モジュールは、多くのAndroidプロジェクトで最初に作成されるアプリ本体のモジュールです。初心者が最初に触るコードや設定の多くはこの中にあり、画面、リソース、Manifest、依存関係、ビルド設定が集約されています。
2.1 appモジュールはアプリ本体を作る中心
app モジュールは、最終的なAPKやAABを生成するための中心です。ここには、src/main、AndroidManifest.xml、res、build.gradle.kts などが含まれ、Android Studioで実行ボタンを押したときも、多くの場合この app モジュールがビルドされます。小規模アプリでは、すべての画面や処理を app モジュール内で管理しても十分に開発できます。重要なのは、app が単なるフォルダではなく、Android Gradle Pluginによってアプリとしてビルドされる単位であることです。
2.2 appはアプリ全体の組み立て役になる
マルチモジュール構成では、app モジュールは個別機能の詳細実装を持つ場所というより、アプリ全体を組み立てる場所になります。たとえば、feature-login、feature-home、core-network、core-database のようなモジュールを app が依存関係として読み込み、アプリ全体のナビゲーションやDI設定、テーマ設定、起動処理をまとめます。この構成にすると、個別機能の実装はそれぞれのモジュールへ分離され、app は全体の入口として薄く保てます。大規模アプリでは、このように app を軽くすることが保守性の向上につながります。
2.3 小規模アプリではappだけでも成立する
画面数が少なく、開発者も少ないアプリでは、最初からマルチモジュールにする必要はありません。app モジュールだけで始め、パッケージを ui、data、domain、model、network のように分ければ、十分に整理された構成を作れます。最初から過度に分割すると、Gradle設定や依存関係が増えて、かえって学習コストや開発コストが上がる場合があります。重要なのは、現在のアプリ規模に合った構成にすることです。
2.4 appに詰め込みすぎると問題が起きる
アプリが成長しても app モジュールにすべてを詰め込み続けると、徐々に保守が難しくなります。ログイン、ホーム、検索、通知、課金、分析、設定、データベース、ネットワーク処理がすべて同じモジュールにあると、依存関係が複雑になり、ビルド時間も長くなります。さらに、ある機能を修正しただけなのに別機能へ影響する可能性が高まり、テスト範囲も広がります。app が肥大化してきたら、共通処理や独立性の高い機能から段階的にモジュール分割を検討するべきです。
2.5 app/build.gradle.ktsは重要設定の中心
app/build.gradle.kts には、applicationId、minSdk、targetSdk、versionCode、versionName、buildTypes、productFlavors、dependenciesなど、アプリ本体に関わる重要設定が入ります。ここを正しく理解していないと、依存ライブラリを追加したときのエラー、SDKバージョン変更時の問題、releaseビルドの署名設定ミスなどに対応しにくくなります。特にストア公開を行うプロジェクトでは、versionCodeや署名設定のミスがリリースに直接影響するため、app/build.gradle.kts は慎重に管理する必要があります。
2.6 appモジュールは薄く保つのが理想
実務では、app モジュールをできるだけ薄く保つ設計が好まれます。アプリの起動、全体テーマ、依存注入、ナビゲーションの入口、各モジュールの接続といった「組み立て」に集中させ、ビジネスロジックやデータ処理は別パッケージまたは別モジュールへ分けると、変更に強い構成になります。もちろん、小規模段階では無理に分ける必要はありませんが、将来的に分離しやすいように、最初から責務を意識してコードを配置することが重要です。
3. src/mainの役割
src/main は、Androidアプリの基本ソースセットです。ほとんどの通常コード、リソース、Manifestはここに配置され、すべてのビルドバリアントに共通する土台になります。
3.1 mainはすべてのビルドの基礎になる
src/main は、debug、release、flavorなどに関係なく共通で使われる基本ソースセットです。公式ドキュメントでも、main ソースセットはすべてのビルドバリアントに共通するソースコードとリソースを含み、より具体的なソースセットがそれに追加または上書きされると説明されています。 つまり、通常の画面コード、共通ロジック、標準リソース、基本的なManifest設定は src/main に置くのが原則です。ここを正しく整理すれば、どのビルドでも安定して使われる共通部分を管理しやすくなります。
3.2 kotlinまたはjavaにアプリコードを置く
src/main/kotlin または src/main/java には、Activity、Fragment、ViewModel、Repository、UseCase、データクラス、ユーティリティなどのアプリコードを配置します。Kotlinだけで開発しているプロジェクトでも、歴史的な理由から java フォルダにKotlinファイルが置かれることがあります。実務上はフォルダ名よりもパッケージ構成が重要で、ui、data、domain、di、model のように責務ごとに分けることで、コードの見通しが大きく改善します。すべてのKotlinファイルを同じ階層へ置くと、画面数が増えた段階で探しにくくなります。
3.3 resには共通リソースを置く
src/main/res には、すべてのビルドで使う共通リソースを置きます。画像、文字列、色、テーマ、寸法、レイアウトなどがここに含まれます。多言語対応やダークモード対応を行う場合も、基本的には res 配下の代替リソースを使います。たとえば、values/strings.xml に標準文字列を置き、values-ja/strings.xml に日本語文字列を置くことで、端末の言語設定に応じた表示が可能になります。リソースを正しく分けることで、UI変更や翻訳対応がかなり楽になります。
3.4 AndroidManifest.xmlはmainに置かれる
標準的なManifestファイルは src/main/AndroidManifest.xml に置かれます。Manifestは、アプリのコンポーネント、権限、アプリ名、テーマ、起動Activity、外部連携などをAndroidシステムへ伝える重要なファイルです。コードが正しくても、Manifest設定が間違っているとActivityが起動しなかったり、権限が使えなかったり、外部リンクが開けなかったりします。src/main のManifestは全体の基本になるため、不要な権限や危険な設定を入れないように慎重に管理する必要があります。
3.5 debugやreleaseで差分を追加できる
src/main は共通部分ですが、必要に応じて src/debug や src/release に差分を置けます。たとえば、debug専用のログ設定、開発用のAPI設定、検証用の画面、release専用の設定などを分けることができます。これにより、本番コードにデバッグ用の処理を混ぜずに済みます。ただし、差分を増やしすぎると、どのビルドでどのコードが使われるのか分かりにくくなります。環境別差分は便利ですが、管理できる範囲に抑えることが大切です。
3.6 mainを整理すると将来の変更に強くなる
src/main はプロジェクトの中心なので、ここが整理されているかどうかで保守性が大きく変わります。画面コード、状態管理、データ処理、リソース、Manifestが分かりやすく配置されていれば、新しい機能を追加するときも迷いにくくなります。逆に、main の中が無秩序だと、開発者ごとにファイルの置き場所が変わり、同じような処理が重複します。Androidアプリは時間とともに機能が増えやすいため、最初から src/main の整理方針を決めておくことが重要です。
4. Kotlin・Javaコードの配置
Androidアプリの主要な処理は、KotlinまたはJavaで書きます。現在の新規開発ではKotlinが中心ですが、既存プロジェクトではJavaとKotlinが混在することもあります。
4.1 Kotlinコードは責務ごとに配置する
Kotlinコードは、単に kotlin フォルダに置けばよいわけではありません。アプリが大きくなるほど、画面、状態管理、データ処理、通信、保存、共通処理を分けて配置する必要があります。たとえば、ui/login にログイン画面関連、data/remote にAPI通信、domain/usecase に業務処理、model にデータ表現を置くようにすると、コードの責務が明確になります。責務が明確な構成は、レビューしやすく、テストしやすく、新しい開発者が参加したときにも理解しやすいです。
4.2 Activityは画面の入口として扱う
ActivityはAndroidアプリの画面や起動の入口として重要な役割を持ちます。しかし、Activityに通信処理、データ加工、画面状態管理、ナビゲーション、権限処理をすべて詰め込むと、すぐに巨大なクラスになります。実務では、ActivityはUIの初期化やナビゲーションの接続、ライフサイクル処理に集中させ、画面状態はViewModelへ、データ取得はRepositoryへ分ける設計がよく使われます。Activityを薄く保つことで、テストしやすく、保守しやすい構成になります。
4.3 Fragmentは画面単位で整理する
Fragmentを使うプロジェクトでは、画面ごとにFragmentを作成し、XMLレイアウトやViewModelと組み合わせて画面を構成します。Fragmentは柔軟ですが、ライフサイクルが複雑なため、コードの置き場所や責務を明確にしないと不具合が起こりやすくなります。特にViewBindingを使う場合は、Viewのライフサイクルに合わせてbindingを破棄する必要があります。Fragmentには画面表示とユーザー操作の受け取りを中心に持たせ、データ取得や複雑な状態管理はViewModelへ分けるのが安全です。
4.4 ViewModelは状態管理の中心になる
ViewModelは、画面に表示する状態やユーザー操作に対する処理を管理する重要なクラスです。ローディング中、成功、エラー、入力値、選択状態などをViewModelで管理すると、ActivityやFragment、Composableをシンプルに保てます。ViewModelがRepositoryやUseCaseを呼び出し、その結果をUI状態として公開する構成にすると、画面側は状態を表示することに集中できます。特にJetpack ComposeやMVVM構成では、ViewModelを適切に設計することがアプリ全体の品質に大きく影響します。
4.5 Repositoryはデータ取得の窓口になる
Repositoryは、API、データベース、キャッシュ、DataStoreなどのデータソースをまとめる窓口です。画面やViewModelが直接APIクライアントやDAOを呼ぶと、データ取得方法が変更されたときにUI側まで影響します。Repositoryを挟むことで、データの取得元がAPIからDBへ変わっても、呼び出し側への影響を抑えられます。実務では、Repositoryのインターフェースをdomain層に置き、実装をdata層に置くことで、依存方向を整理することもあります。
4.6 UseCaseは業務処理を分離する
UseCaseは、アプリ固有の操作や業務処理を表すクラスです。ログインする、商品一覧を取得する、ユーザー情報を更新する、通知設定を保存するなど、画面から見た操作単位をUseCaseとして切り出すと、ViewModelが肥大化しにくくなります。すべての小さな処理をUseCase化する必要はありませんが、複数画面で使う処理や条件分岐が複雑な処理はUseCaseへ分ける価値があります。UseCaseを純粋なKotlinコードに近づけると、Unit Testも書きやすくなります。
5. resフォルダの詳細構成
res フォルダは、Androidアプリの見た目や表示を支える非常に重要な場所です。画像、文字列、色、テーマ、レイアウトなどをコードから分離して管理することで、保守性、多言語対応、端末対応が向上します。
5.1 drawableは画像と図形を管理する
drawable には、PNGやWebPなどの画像、Vector Drawable、shape XMLなどを配置します。アイコン、背景、ボタン形状、区切り線、イラストなど、画面上に表示される視覚要素の多くがここで管理されます。実務では、画像名を ic_、bg_、img_、shape_ のように用途別に命名すると、後から探しやすくなります。命名が乱れると、似た画像が重複し、不要なリソースが残り、アプリサイズにも影響します。drawableは単なる画像置き場ではなく、UI品質を支える資産管理の場所です。
5.2 mipmapは主にランチャーアイコンを管理する
mipmap は、主にアプリのランチャーアイコンを管理するために使われます。Android端末は画面密度が多様であるため、アイコンは複数密度向けに用意されることがあります。ランチャーアイコンはユーザーがアプリを認識する最初の要素なので、品質の低い画像や密度対応の不足は印象を悪くします。実務では、アプリアイコン、丸型アイコン、適応型アイコンなどを適切に設定し、ストア公開前に複数端末で表示を確認することが重要です。
5.3 valuesは文字列・色・テーマの中心
values には、strings.xml、colors.xml、themes.xml、dimens.xml などを配置します。文字列、色、寸法、スタイル、テーマをここへまとめることで、コードやレイアウトに直接値を書かずに済みます。たとえば、画面に表示する文言をすべて strings.xml に置けば、多言語対応や文言修正が簡単になります。ブランドカラーや背景色を colors.xml やテーマで管理すれば、デザイン変更時の修正漏れも減らせます。valuesはアプリ全体の一貫性を保つための重要な基盤です。
5.4 layoutはXMLベースUIを管理する
XMLレイアウトを使うプロジェクトでは、res/layout にActivity、Fragment、RecyclerView項目、ダイアログ、共通部品などのXMLファイルを配置します。XML構成では、UI定義がコードから分離されるため、画面構造を視覚的に理解しやすいメリットがあります。一方で、Kotlin側のActivityやFragmentとの対応関係を明確にしないと、どのXMLがどの画面で使われているか分かりにくくなります。fragment_login.xml と LoginFragment.kt のように、命名を揃えることが保守性に直結します。
5.5 navigationやmenuもresで管理する
Navigation Componentを使う場合、res/navigation にナビゲーショングラフを置くことがあります。また、ツールバーやオプションメニューを使う場合は、res/menu にメニューXMLを配置します。これらは画面遷移やユーザー操作に関わるため、画面構成を理解するうえで重要です。ナビゲーショングラフが大きくなりすぎると、画面遷移の全体像が把握しにくくなります。大規模アプリでは、機能ごとにナビゲーションを分け、appモジュール側で統合する構成も検討されます。
5.6 代替リソースで端末差分に対応する
Androidでは、言語、画面サイズ、端末密度、ダークモードなどに応じて代替リソースを提供できます。たとえば、values-ja で日本語文字列、drawable-night でダークテーマ用画像、layout-sw600dp でタブレット向けレイアウトを用意できます。この仕組みを使えば、同じコードでも端末条件に応じて最適なリソースを自動選択できます。ただし、代替リソースが増えるほど管理も複雑になるため、どの条件に対応しているのかをチーム内で明確にしておくことが重要です。
6. AndroidManifest.xmlの構成
AndroidManifest.xml は、アプリの基本情報をAndroidシステムへ伝える重要な設定ファイルです。Activity、Service、Receiver、権限、アプリ名、テーマ、外部連携など、アプリの動作に深く関わる情報が含まれます。
6.1 Manifestはアプリの説明書になる
Android公式ドキュメントでは、すべてのアプリプロジェクトにはManifestファイルが必要であり、Androidビルドツール、Android OS、Google Playに対してアプリに関する重要情報を説明するとされています。つまり、Manifestはアプリがどのようなコンポーネントを持ち、どの権限を必要とし、どの機能を使うのかをシステムへ伝える説明書のようなものです。Manifestが間違っていると、コードが正しくてもアプリが起動しなかったり、機能が使えなかったりします。そのため、Manifestはコードと同じくらい慎重に管理する必要があります。
6.2 Activity宣言は画面起動に関わる
Activityを使う場合、Manifestに宣言が必要になることがあります。特にアプリ起動時に表示されるActivityには、launcher intent filterを設定し、ホーム画面から起動できるようにします。画面数が増えるとActivity宣言も増えますが、すべてを無秩序にManifestへ置くと見通しが悪くなります。実務では、Activityの役割、外部公開の有無、Deep Link対応の有無を整理し、必要な宣言だけを正しく管理することが重要です。特に android:exported の設定はセキュリティにも関わるため、慎重な確認が必要です。
6.3 Permission宣言は必要最小限にする
カメラ、位置情報、通知、Bluetooth、インターネット、ストレージなどを使う場合、Manifestに権限を宣言します。権限が不足していると、機能が動かなかったり、実行時にエラーが発生したりします。一方で、不要な権限を宣言すると、ユーザーの不信感につながり、ストア審査でも問題になる可能性があります。権限はアプリのプライバシーやセキュリティに直結するため、本当に必要なものだけを宣言し、利用目的を明確にすることが重要です。リリース前にはManifestを確認し、古い機能で使っていた不要権限が残っていないかを見るべきです。
6.4 ServiceやReceiverもManifestで管理する
バックグラウンド処理、通知処理、外部イベント受信などでは、ServiceやBroadcastReceiverを使うことがあります。これらもManifestで宣言される場合があり、設定によっては外部アプリから呼び出される可能性があります。Androidのバックグラウンド制限は年々厳しくなっているため、Serviceを使う場合は用途を慎重に検討する必要があります。単純な定期処理ならWorkManager、ユーザーに明示する長時間処理ならForeground Serviceなど、適切な仕組みを選ぶことが重要です。
6.5 Manifest mergeを理解する
Androidプロジェクトでは、アプリ本体のManifestだけでなく、ライブラリが持つManifestも最終的にマージされます。公式ドキュメントでは、ビルドバリアント、ビルドタイプ、product flavorなどのManifestに優先順位があり、それらがマージされることが説明されています。 この仕組みにより、ライブラリを追加しただけで権限やコンポーネントが最終Manifestに入る場合があります。開発者が直接書いていない設定も含まれる可能性があるため、リリース前にはMerged Manifestを確認する習慣が必要です。
6.6 Manifest変更はレビュー対象にする
Manifestは設定ファイルですが、アプリの起動、権限、外部公開、セキュリティ、ストア公開に深く関わります。そのため、Manifest変更はコード変更と同じようにレビュー対象にするべきです。特に、intent filter、FileProvider、Deep Link、exported設定、権限追加は慎重に確認します。小さな設定ミスが外部からの不正呼び出しや情報漏えいにつながる可能性もあります。安全なAndroidプロジェクトでは、Manifestを単なる自動生成ファイルとして扱わず、アプリ仕様の重要な一部として管理します。
7. Gradleファイルの構成
GradleはAndroidプロジェクトのビルドを管理する中心です。アプリがどのようにコンパイルされ、どの依存関係を使い、どのvariantを作り、どのように署名されるかはGradle設定に大きく依存します。
7.1 settings.gradle.ktsはモジュール一覧を管理する
settings.gradle.kts は、プロジェクトに含めるモジュールやプラグイン管理を定義するファイルです。小規模プロジェクトでは :app だけが含まれることが多いですが、マルチモジュール構成では :core:network、:feature:login、:feature:home のような複数モジュールが登録されます。このファイルを見ると、プロジェクト全体がどのようなモジュールで構成されているかを把握できます。大規模アプリでは、settings.gradle.kts がプロジェクト構成の地図のような役割を持ちます。
7.2 ルートbuild.gradle.ktsは共通設定を管理する
ルートの build.gradle.kts は、プロジェクト全体に関わるビルド設定やプラグイン設定を管理する場所です。すべてのモジュールに共通する設定、プラグインのバージョン、リポジトリ設定などがここに置かれることがあります。ただし、ルートGradleに多くの処理を書きすぎると、設定の見通しが悪くなります。近年は、Version Catalogやconvention pluginを使い、依存関係や共通ビルド設定を整理する構成も増えています。ビルド設定はコードと同じく保守されるべき資産です。
7.3 app/build.gradle.ktsはアプリ設定の中心
app/build.gradle.kts は、アプリ本体に関する最も重要なGradleファイルです。ここでは、namespace、compileSdk、minSdk、targetSdk、applicationId、versionCode、versionName、buildTypes、productFlavors、dependenciesなどを管理します。公式ドキュメントでも、モジュールレベルのGradleファイルで重要なアプリ設定を行うことが説明されています。 このファイルの設定ミスは、ビルドエラーだけでなく、ストア公開、アプリ更新、端末対応にも影響します。リリース前には特に慎重に確認すべき場所です。
7.4 buildTypesは開発版と本番版を分ける
buildTypes は、debugやreleaseのようにビルド目的ごとの差分を管理します。debugではデバッグしやすい設定を使い、releaseでは署名、難読化、最適化、ログ抑制などを行うのが一般的です。この仕組みを使うと、開発中だけ必要な処理をdebugに限定し、本番版へ混ぜないようにできます。buildTypesを適切に使わず、コード内のif文だけで環境差分を管理すると、本番にデバッグ設定が混入するリスクが高まります。Gradleで差分を管理するほうが安全です。
7.5 productFlavorsは製品差分を管理する
productFlavors は、無料版・有料版、国内版・海外版、顧客別版など、製品バリエーションを管理するための仕組みです。Android公式ドキュメントでも、build variantsによって1つのプロジェクトから異なるバージョンのアプリを作れることが説明されています。 productFlavorsを使うと便利ですが、flavorが増えるほどビルドvariantも増え、テスト対象が広がります。必要以上にflavorを増やすと、プロジェクト構成が複雑になり、保守コストが高くなります。
7.6 dependenciesは依存関係の入口になる
dependencies ブロックには、アプリが利用する外部ライブラリや内部モジュールを定義します。Jetpack、Compose、Room、Retrofit、Hilt、Firebase、テストライブラリなど、多くの依存関係がここに追加されます。依存ライブラリは開発効率を上げますが、増えすぎるとビルド時間、脆弱性対応、バージョン衝突、アプリサイズに影響します。依存関係は追加するだけでなく、定期的に棚卸しし、不要なものを削除し、バージョンを管理する必要があります。
8. buildTypesとproductFlavors
buildTypesとproductFlavorsを理解すると、Androidアプリの環境別・製品別ビルドを正しく管理できます。これらは、1つのコードベースから複数の成果物を作るための重要な仕組みです。
8.1 buildTypesは目的別のビルドを作る
buildTypesは、開発用、本番用、検証用など、ビルドの目的を分けるために使います。標準ではdebugとreleaseが用意され、debugは開発中の実行やデバッグに使われ、releaseは本番配布に使われます。debugではログを出し、デバッグ可能にし、テスト用設定を使うことがあります。一方、releaseでは難読化や最適化、署名、本番API接続を行うことが一般的です。目的ごとにビルドを分けることで、開発しやすさと本番安全性を両立できます。
8.2 debugは開発効率を重視する
debugビルドは、開発者が素早く動作確認できることを重視します。ログ出力、デバッグツール、開発用API、検証用機能を使う場合があります。Android Studioで通常アプリを実行すると、多くの場合debug variantが使われます。debugビルドは便利ですが、ここで使う設定がreleaseへ混ざらないように注意が必要です。たとえば、開発用サーバーのURL、テスト用APIキー、内部ログ、検証画面などは、本番版に含まれないようGradleやソースセットで分離するべきです。
8.3 releaseは配布品質を重視する
releaseビルドは、Google Playなどへ配布する本番用成果物を作るためのビルドです。署名、難読化、最適化、不要リソース削除、ログ抑制、本番API接続などが関わります。releaseビルドは、debugと違って実行環境や最適化条件が変わるため、debugで動いたからreleaseでも必ず動くとは限りません。R8やProGuardの設定によって、リフレクションやシリアライズが壊れることもあります。そのため、release相当のビルドで必ずテストすることが重要です。
8.4 productFlavorsはビジネス差分を表現する
productFlavorsは、同じアプリを複数の製品バリエーションとして出す場合に使います。無料版と有料版、企業A向けと企業B向け、日本向けと海外向けなど、ビジネス上の差分をGradleで管理できます。flavorごとにapplicationId、アプリ名、アイコン、リソース、API接続先、機能有効化を変えることもできます。ただし、flavorを使い始めると、debug・releaseとの組み合わせでvariantが増えます。管理できないほどvariantが増えると、テスト漏れや設定ミスが起こりやすくなります。
8.5 build variantは組み合わせの結果
buildTypesとproductFlavorsを組み合わせると、build variantが作られます。たとえば、freeDebug、freeRelease、paidDebug、paidRelease のようなvariantです。Android公式ドキュメントでも、Build Variantを選択してビルド・実行できることが説明されています。 この仕組みを理解すると、どのコード、どのリソース、どのManifest、どの依存関係が最終ビルドに含まれるかを把握しやすくなります。variantは便利ですが、増やしすぎるとCIやテストの設計も難しくなります。
8.6 差分は最小限に抑える
buildTypesやproductFlavorsは強力ですが、差分を増やしすぎるとプロジェクトが複雑になります。特定variantだけで発生する不具合は発見が遅れやすく、リリース前の確認も大変になります。基本方針として、共通部分は src/main に置き、必要な差分だけを src/debug、src/release、flavor別ソースセットに置くべきです。差分管理の目的は、構成を複雑にすることではなく、共通部分と違いを明確にすることです。
9. testフォルダの役割
test フォルダは、ローカルJVM上で実行するテストを置く場所です。端末やエミュレーターを使わずに高速に実行できるため、日常的な品質確認に向いています。
9.1 testは高速なローカルテスト用
Android公式ドキュメントでは、test はホストマシン上のローカルJVMで実行されるテスト用ソースセットであり、Androidデバイスで実行されるテストより高速だと説明されています。 ローカルテストは、ビジネスロジック、UseCase、ViewModelの状態変化、入力チェック、計算処理、データ変換などを確認するのに向いています。高速に実行できるため、CIで毎回実行しやすく、開発中のフィードバックも早く得られます。Androidアプリの品質を安定させるには、まずローカルテストを充実させることが重要です。
9.2 Unit Testはロジック確認に向いている
Unit Testは、特定の関数やクラスが期待通りに動くかを確認するテストです。たとえば、ログイン条件、金額計算、日付変換、フォームバリデーション、Repositoryのデータ変換などはUnit Testに向いています。UIやAndroidフレームワークに依存しない処理ほど、ローカルテストで安定して検証できます。逆に、ContextやResourcesに強く依存する設計にすると、Unit Testが書きにくくなります。テストしやすい設計を意識することは、プロジェクト構成にも大きく関係します。
9.3 ViewModelはテスト対象にしやすい
ViewModelは、画面状態を管理するため、Unit Testの対象として非常に重要です。ユーザーがボタンを押したときに状態がどう変わるか、API失敗時にエラー状態になるか、ローディング表示が正しく切り替わるかなどを確認できます。ViewModelをテストしやすくするには、RepositoryやUseCaseを外から渡せる設計にし、FakeやMockへ差し替えられるようにする必要があります。ViewModelにAndroid Contextや具体的なAPI実装を直接持たせると、テストが難しくなるため注意が必要です。
9.4 RepositoryはFakeを使って確認する
Repositoryのテストでは、実際のAPIやデータベースを使わず、FakeのDataSourceやMockを使って動作を確認することが多いです。これにより、ネットワーク状態や外部サービスの影響を受けずに、データ変換やエラーハンドリングを確認できます。Repositoryが複雑な責務を持ちすぎるとテストも複雑になるため、RemoteDataSource、LocalDataSource、Mapperなどへ分けると整理しやすくなります。データ層の構成を綺麗にしておくことは、テスト品質にも直結します。
9.5 UtilityやMapperもテストすべき
日付変換、金額表示、文字列整形、APIレスポンスから画面モデルへの変換など、UtilityやMapperは小さな処理に見えても重要です。これらは複数画面で使われることが多く、バグがあると広範囲に影響します。Unit Testで境界値や異常値を確認しておけば、後から仕様変更があったときも安心です。特に金額、日付、タイムゾーン、多言語表示に関わる処理は、ユーザー影響が大きいため、テストを書く価値が高いです。
9.6 testをCIに組み込む
test フォルダに書いたローカルテストは高速に実行できるため、CIに組み込みやすいです。プルリクエストごとにUnit Testを実行すれば、基本的なロジック破壊を早期に検出できます。テストは書くだけでなく、継続的に実行されて初めて効果を発揮します。CIで常に実行されるテストがあると、開発者は安心してリファクタリングや機能追加を行えます。Androidプロジェクト構成では、テストが自然に追加・実行される状態を作ることが重要です。
10. androidTestフォルダの役割
androidTest フォルダは、実機またはエミュレーター上で実行するテストを置く場所です。Androidフレームワークに依存する処理やUI操作を確認するために使います。
10.1 androidTestは実端末環境で動く
Android公式ドキュメントでは、androidTest はAndroidデバイスまたはエミュレーター上で実行されるテスト用ソースセットであり、実際のAndroid環境へアクセスできるが、ホストテストより実行が遅いと説明されています。 つまり、Context、Resources、Activity、Fragment、Room、DataStore、UI操作など、Android環境が必要な処理を確認するために使います。実端末環境に近い確認ができる一方で、実行時間が長く、安定性にも注意が必要です。
10.2 UIテストは重要フローに絞る
androidTestでは、EspressoやCompose UI Testを使って画面操作を確認できます。ボタンを押す、入力欄に文字を入れる、一覧をスクロールする、画面遷移を確認するなど、ユーザー操作に近いテストが可能です。ただし、UIテストはUnit Testより遅く、レイアウトや文言の変更で壊れやすい傾向があります。そのため、すべての画面操作をUIテストで網羅するのではなく、ログイン、購入、登録、主要導線など、ビジネス上重要なフローに絞って書くのが現実的です。
10.3 RoomやDataStoreの確認に向いている
RoomデータベースやDataStoreのようにAndroid環境と関係する処理は、androidTestで確認することがあります。DAOのinsert、update、delete、queryが正しく動くか、保存した設定値が読み戻せるかなどを、実際のAndroid環境で検証できます。テスト時にはin-memory databaseを使い、テストごとに状態を初期化することが重要です。状態が残ったままだと、テストの順序によって結果が変わり、不安定なテストになります。
10.4 Context依存処理を確認できる
Context、Resources、PackageManager、権限、ファイル保存など、Androidフレームワークに依存する処理はローカルテストでは扱いにくい場合があります。androidTestを使えば、実際の端末環境でこれらの処理を確認できます。ただし、すべてをandroidTestへ寄せると実行時間が長くなります。可能な限りロジックをAndroid非依存に切り出し、どうしてもAndroid環境が必要な部分だけをandroidTestにすることで、テスト速度と信頼性のバランスを取れます。
10.5 variant別テストも作れる
ビルドバリアントごとに異なるソースセットを持つプロジェクトでは、variant別のandroidTestを作ることもできます。公式ドキュメントでは、build variant固有のsource setがある場合、それに対応するinstrumented testを独自のsource setに配置できると説明されています。 たとえば、特定flavorだけに存在する機能をテストしたい場合、variant別テストが役立ちます。ただし、variant別テストを増やしすぎると管理が複雑になるため、共通テストと差分テストの役割を明確にする必要があります。
10.6 androidTestは実行戦略が重要
androidTestは強力ですが、CIで毎回すべて実行すると時間がかかります。そのため、Unit Testは毎回、重要なandroidTestはプルリクエスト時、重いUIテストは夜間やリリース前に実行するなど、実行戦略を設計することが重要です。テストは多ければよいわけではなく、必要な品質をどのタイミングで確認するかが大切です。Androidプロジェクト構成では、testとandroidTestの役割を分け、開発速度を落とさず品質を守る仕組みを作る必要があります。
11. Jetpack Composeプロジェクト構成
Jetpack Composeを使う場合、UIをXMLではなくKotlinコードで書くため、従来のAndroidプロジェクトとは少し違う整理が必要になります。画面、状態、テーマ、Preview、共通Composableをどう分けるかが重要です。
11.1 ComposeではUIがKotlinコードになる
Jetpack Composeでは、画面レイアウトをXMLではなくComposable関数としてKotlinで記述します。これにより、UIと状態を近い場所で扱いやすくなり、再利用可能なコンポーネントも作りやすくなります。一方で、UIがすべてKotlinコードになるため、画面ファイルが大きくなりやすいという問題もあります。1つのComposableに画面全体、入力処理、条件分岐、リスト表示、ダイアログまで詰め込むと、すぐに読みにくくなります。Composeでは、UIを小さな部品へ分ける設計が非常に重要です。
11.2 screenとcomponentを分ける
Composeプロジェクトでは、画面単位のComposableを screen、再利用可能なUI部品を component や ui/component に分ける構成がよく使われます。たとえば、LoginScreen は画面全体を表し、PrimaryButton や ErrorMessage は共通部品として切り出します。この分け方により、画面固有のUIとアプリ全体で使う部品を区別できます。共通部品を適切に切り出せば、デザイン統一や修正の効率化につながります。ただし、何でも共通化すると逆に複雑になるため、複数箇所で本当に再利用するものだけを切り出すべきです。
11.3 themeはデザイン基盤になる
Composeでは、ui/theme にColor、Typography、Themeなどを置く構成が一般的です。テーマを整理しておくと、アプリ全体の色、文字、形状、余白の一貫性を保ちやすくなります。画面ごとに直接色や文字サイズを書くと、デザイン変更時に修正箇所が増え、統一感も失われます。テーマを基盤として管理すれば、ダークモードやブランド変更にも対応しやすくなります。Composeプロジェクトでは、見た目の一貫性を保つためにthemeフォルダの設計が非常に重要です。
11.4 ViewModelとStateを分ける
Composeでは、画面状態をどこで持つかが重要です。Composableが直接状態を持ちすぎると、再利用性やテスト性が下がります。実務では、ViewModelがUI Stateを公開し、ComposableはそのStateを受け取って表示する構成がよく使われます。この考え方を使うと、UIは状態の表示に集中でき、データ取得や条件分岐はViewModel側へ分離できます。結果として、画面コードが読みやすくなり、ViewModelのUnit Testも書きやすくなります。
11.5 Previewは整理して使う
ComposeのPreviewは、エミュレーターを起動せずにUIを確認できる便利な機能です。ボタン、カード、画面、ダークモード、フォントサイズ違いなどをPreviewで確認すると、開発効率が上がります。ただし、Preview用のFakeデータやPreview関数を本番コードと混ぜすぎると、ファイルが読みにくくなります。実務では、Preview用データを別ファイルへ分けたり、PreviewParameterProvider を使ったりして整理することがあります。Previewは強力ですが、構成ルールを決めて使う必要があります。
11.6 Composeでもres管理は必要
Composeを使うとXMLレイアウトは減りますが、res フォルダが不要になるわけではありません。文字列、画像、色、フォント、アプリアイコン、多言語リソースなどは引き続き res で管理します。Compose内で文字列を直接書くと、多言語対応や文言変更が難しくなります。stringResource や painterResource を使い、リソース管理とCompose UIを連携させることが重要です。Composeプロジェクトでも、リソースを外部化するAndroidの基本思想は変わりません。
12. XMLレイアウトプロジェクト構成
XMLレイアウトを使うAndroidプロジェクトでは、res/layout、Activity、Fragment、ViewBinding、DataBindingの関係を理解する必要があります。既存アプリでは今でもXML構成が多く使われています。
12.1 XMLレイアウトは画面構造を分離する
XMLレイアウトは、画面の構造をKotlinコードから分離して管理する仕組みです。res/layout にActivityやFragmentのレイアウトを置き、Kotlin側から読み込んで表示します。この構成では、UIの階層やViewのIDをXMLで確認できるため、画面構造が比較的分かりやすいです。一方で、UI定義はXML、処理はKotlinに分かれるため、対応関係を明確にしないと保守しにくくなります。命名規則やフォルダ整理が非常に重要になります。
12.2 ActivityとXMLは対応名を揃える
ActivityがXMLレイアウトを使う場合、activity_main.xml と MainActivity.kt のように対応名を揃えると分かりやすくなります。ファイル名に一貫性がないと、どのActivityがどのXMLを使っているのか探す時間が増えます。小さな命名ルールですが、画面数が増えるほど効果が大きくなります。特にチーム開発では、命名規則が統一されているだけで新しいメンバーの理解速度が上がります。XML構成では、名前がプロジェクトの地図になると考えるべきです。
12.3 FragmentとViewBindingの扱いに注意する
FragmentでViewBindingを使う場合、bindingのライフサイクル管理が重要です。Fragmentは自身のライフサイクルとViewのライフサイクルが異なるため、Viewが破棄された後にbindingを保持するとメモリリークにつながる可能性があります。実務では、onDestroyView でbindingをnullにするパターンがよく使われます。ViewBindingは安全で便利ですが、ライフサイクル理解なしに使うと問題が起こります。XML構成のプロジェクトでは、FragmentとViewBindingの扱いをチームで統一することが重要です。
12.4 DataBindingは強力だが慎重に使う
DataBindingを使うと、XMLからViewModelの値を参照したり、イベントを結びつけたりできます。画面の表示とデータを結びつけるには便利ですが、XML内にロジックを書きすぎると、画面処理が見えにくくなります。複雑な式や条件分岐をXMLへ入れると、Kotlinコードから追いにくくなり、テストもしにくくなります。DataBindingを使う場合は、XMLは表示に集中させ、複雑な判断はViewModel側へ置くのが安全です。強力な機能ほど、使い方のルールが重要になります。
12.5 RecyclerView itemは専用レイアウトにする
RecyclerViewを使う場合、一覧項目用のXMLを item_user.xml、item_product.xml のように専用ファイルとして作ります。項目レイアウトは再利用されるため、Viewの構造、余白、画像サイズ、テキスト表示を丁寧に設計する必要があります。AdapterやViewHolderとの対応関係も分かりやすく命名すると、保守しやすくなります。リスト項目はアプリ内で頻繁に変更される部分なので、レイアウトとデータバインドの責務を明確にしておくことが大切です。
12.6 XMLとComposeが混在する場合
既存アプリへComposeを段階導入する場合、XMLとComposeが混在する構成になります。この場合、どの画面がXMLで、どの画面がComposeなのか、どの共通UIがどちらで実装されているのかを整理する必要があります。混在期にルールがないと、同じボタンがXML版とCompose版で別々に作られ、デザイン差分が生まれることがあります。移行中のプロジェクトでは、共通デザイン、テーマ、ナビゲーション、状態管理の方針を明確にしておくことが重要です。Compose導入は技術移行だけでなく、構成整理の機会でもあります。
13. パッケージ設計
パッケージ設計は、Androidプロジェクトの読みやすさと保守性を左右します。どのコードをどのパッケージに置くかが曖昧だと、機能追加のたびに構成が崩れていきます。
13.1 機能別パッケージは探しやすい
機能別パッケージでは、login、home、profile、settings のように機能単位でコードをまとめます。ログインに関する画面、ViewModel、UseCase、Repository、Modelを同じ機能配下に置くため、関連ファイルを探しやすいメリットがあります。チーム開発でも、機能ごとに担当範囲を分けやすくなります。ただし、共通処理まで各機能に置いてしまうと重複が増えます。機能別構成では、共通処理をどこに置くかを最初から決めておくことが重要です。
13.2 レイヤー別パッケージは責務が明確になる
レイヤー別パッケージでは、ui、domain、data、model のように役割で分けます。UI、業務ロジック、データ取得、データ表現を分離できるため、クリーンアーキテクチャやMVVMと相性がよいです。依存方向を整理しやすく、domain層をAndroid非依存にすればテストもしやすくなります。ただし、小規模アプリでは抽象度が高くなりすぎ、ファイルを探すときに複数階層を移動する必要があります。プロジェクト規模に合った分け方が重要です。
13.3 coreは本当に共通なものだけにする
core パッケージやcoreモジュールには、アプリ全体で使う共通処理を置きます。ネットワーク基盤、ログ、エラー処理、共通UI、拡張関数、デザインシステムなどが候補です。ただし、coreは便利すぎるため、何でも置く場所になりがちです。coreが肥大化すると、結局すべての機能がcoreに依存し、変更影響が広がります。coreへ入れる基準は、本当に複数機能で必要か、特定機能に依存していないかで判断するべきです。
13.4 dataは外部データとの境界になる
data パッケージには、API、データベース、DataStore、Repository実装、DTO、Mapperなどを置きます。外部データとのやり取りを担当するため、アプリの中でも変更が起こりやすい層です。APIレスポンスの形式が変わったり、保存方式が変わったりしても、data層で吸収できればUIやdomainへの影響を抑えられます。data層を整理していないと、画面側が直接API形式に依存し、仕様変更のたびに広範囲を修正することになります。長期運用ではdata層の設計が非常に重要です。
13.5 domainはアプリ固有のルールを表す
domain パッケージには、UseCase、ドメインモデル、Repositoryインターフェースなどを置きます。ここはアプリ固有の業務ルールを表す層であり、可能な限りAndroidフレームワークに依存しない形にするのが理想です。domain層が純粋なKotlinコードに近いほど、Unit Testが書きやすくなり、UIやデータ保存方式の変更にも強くなります。たとえば、ログイン条件や注文計算のような処理は、Activityではなくdomainに置くことで再利用しやすくなります。domainはアプリの中核ロジックを守る場所です。
13.6 uiは表示と操作に集中させる
ui パッケージには、Activity、Fragment、Composable、ViewModel、UI State、画面部品などを置きます。UI層はユーザーに最も近く、変更頻度も高い場所です。そのため、UI層にデータベース処理やAPI処理、複雑な業務ロジックを直接書くと、変更のたびに影響が広がります。UIは状態を表示し、ユーザー操作をViewModelへ渡すことに集中させるべきです。表示とロジックを分けることで、画面変更、状態管理、テストがそれぞれ扱いやすくなります。
14. マルチモジュール構成
マルチモジュール構成は、大規模Androidアプリでよく使われる設計です。コードを複数のGradleモジュールへ分けることで、責務分離、ビルド効率、チーム開発のしやすさを高められます。
14.1 マルチモジュールは大規模化への対応策
Android公式のモジュール化ガイドでは、複数のGradleモジュールを持つプロジェクトをマルチモジュールプロジェクトと呼び、コードベースを整理するための推奨パターンが紹介されています。 マルチモジュールは、単にフォルダを増やすためのものではありません。機能や共通基盤を独立させ、変更影響を小さくし、ビルドやテストを効率化するための設計です。特に複数チームで開発するアプリでは、モジュール境界がチームの責務境界にもなります。
14.2 featureモジュールは機能を分離する
featureモジュールは、ログイン、ホーム、検索、通知、設定などの機能ごとに作るモジュールです。機能単位でコードを分離すると、その機能に関するUI、ViewModel、UseCase、依存関係をまとめて管理できます。別チームが別機能を開発している場合、featureモジュールは開発範囲を明確にする助けになります。ただし、feature同士が直接依存しすぎると、結局複雑な依存関係になります。feature間の連携は、appモジュールやapiモジュールを通して整理するのが望ましいです。
14.3 coreモジュールは共通基盤を提供する
coreモジュールには、複数featureで使う共通基盤を置きます。たとえば、core-network、core-database、core-ui、core-analytics、core-designsystem のように分けることがあります。coreを分けることで、各featureが同じ基盤を再利用でき、重複実装を防げます。ただし、coreが巨大化すると、すべてのモジュールがcoreへ依存し、変更影響が広がります。coreモジュールは責務ごとに分け、本当に共通なものだけを置くことが重要です。
14.4 libraryモジュールは再利用性を高める
Android Libraryモジュールは、アプリ本体ではなくライブラリとして再利用できるモジュールです。公式ドキュメントでも、Androidライブラリはアプリモジュールと構造的に似ており、ソースコード、リソース、Manifestなどを含められると説明されています。 社内で複数アプリを開発している場合、デザインシステム、認証基盤、通信基盤、共通UI部品をライブラリ化すると再利用しやすくなります。ただし、ライブラリ化すると公開API設計や互換性管理も必要になります。
14.5 dynamic featureは必要時配信に使う
Dynamic Feature Moduleは、アプリの一部機能を必要に応じて後から配信するための仕組みです。初回インストールサイズを減らしたい場合や、特定ユーザーだけが使う機能を分離したい場合に有効です。ただし、通常のfeatureモジュールより設計が難しく、インストール状態、ナビゲーション、依存関係、テスト、配布設定を慎重に扱う必要があります。Dynamic Featureは便利ですが、導入コストもあるため、アプリサイズや機能利用率を見て判断するべきです。
14.6 モジュール分割はやりすぎない
マルチモジュールは強力ですが、分ければ分けるほどよいわけではありません。小規模アプリで過度に分割すると、Gradle設定、依存関係、モジュール間API、ビルド設定が増え、開発速度が落ちます。モジュール化は、コード量、チーム人数、ビルド時間、機能独立性、再利用性を見て判断するべきです。最初はシンプルに始め、問題が見えてきた段階で段階的に分割するのが現実的です。良いモジュール構成は、複雑さを増やすのではなく、複雑さを管理しやすくするためにあります。
15. 依存関係管理
Androidプロジェクトでは、多くの外部ライブラリを利用します。依存関係を正しく管理しないと、ビルドエラー、脆弱性、アプリサイズ増加、バージョン衝突につながります。
15.1 dependenciesは慎重に追加する
外部ライブラリは開発効率を高めますが、追加するたびに保守責任も増えます。implementation、api、testImplementation、androidTestImplementation などを使い分け、必要な範囲にだけ依存関係を追加することが重要です。特にライブラリモジュールで api を使いすぎると、依存関係が利用側へ漏れ、プロジェクト全体が複雑になります。便利だからといって安易にライブラリを増やすのではなく、更新状況、利用実績、ライセンス、代替手段を確認してから導入するべきです。
15.2 Version Catalogでバージョンを集中管理する
Version Catalogを使うと、依存ライブラリのバージョンを1か所で管理できます。複数モジュールで同じライブラリを使う場合、各モジュールのGradleファイルにバージョンを直接書くと、更新漏れや不一致が起こりやすくなります。Version Catalogを導入すれば、libs.versions.toml などでライブラリ名とバージョンをまとめられ、依存関係の見通しがよくなります。大規模プロジェクトでは、依存関係の統一管理が保守性に大きく影響します。
15.3 BOMを使うと整合性を保ちやすい
FirebaseやJetpack Composeなど、一部のライブラリ群ではBOMを使って関連ライブラリのバージョン整合性を管理できます。BOMを使うと、関連パッケージのバージョンを個別に指定しなくても、互換性のある組み合わせを使いやすくなります。複数の関連ライブラリを個別に更新すると、互換性問題が起こる場合があります。BOMが提供されている場合は、公式推奨に従って利用することで、バージョン衝突や不整合を減らせます。
15.4 不要な依存関係は削除する
プロジェクトが長く続くと、過去に使っていたライブラリが残ったままになることがあります。不要な依存関係は、ビルド時間、アプリサイズ、脆弱性対応、ライセンス管理の負担を増やします。定期的に依存関係を棚卸しし、使っていないライブラリを削除することが重要です。特に大規模アプリでは、依存関係が多すぎると、どのライブラリが何のために使われているのか分からなくなります。依存関係にも所有者と導入理由を残すと管理しやすくなります。
15.5 脆弱性対応を継続する
外部ライブラリには脆弱性が見つかることがあります。依存関係を管理していないと、自社アプリが影響を受けるかどうか判断できません。CIで依存関係スキャンを行ったり、ライブラリの更新情報を確認したりすることで、危険なバージョンを早期に把握できます。脆弱性対応は一度だけの作業ではなく、継続的な運用です。Androidアプリはユーザー端末に配布されるため、問題が見つかったら迅速に修正版を出せる体制も必要です。
15.6 更新方針を決めておく
ライブラリ更新は、放置しすぎても危険ですが、無計画に上げても問題が出ます。更新前には変更履歴を確認し、テストを実行し、互換性の影響を見ます。大規模プロジェクトでは、月次や四半期ごとに依存関係更新の日を決めると運用しやすくなります。更新を長期間放置すると、後から一気に上げる必要があり、移行負担が大きくなります。小さく継続的に更新することが、長期的には安全で低コストです。
16. リソース命名規則
Androidプロジェクトでは、リソース名の整理が非常に重要です。画像、文字列、色、レイアウト、寸法の名前が分かりやすいだけで、開発効率と保守性は大きく変わります。
16.1 drawableは用途が分かる名前にする
drawableリソースは、ic_、bg_、img_、shape_ のように接頭辞を使って用途を表すと分かりやすくなります。たとえば、ホームアイコンなら ic_home、ログイン画面の背景なら bg_login_header のようにします。名前が曖昧だと、似た画像が増えたり、どこで使っているか分からなくなったりします。チーム開発では、リソースを追加する人が複数いるため、命名規則を決めておくことが重要です。リソース名はUI資産を探すための検索キーでもあります。
16.2 stringは画面名と用途を含める
文字列リソースは、login_button_submit、home_title_welcome、error_network_unavailable のように、画面名や用途を含めて命名すると管理しやすくなります。単に title や message のような名前にすると、どこで使われているか分からなくなります。多言語対応では、翻訳者が文字列の意味を理解できることも重要です。文字列名が具体的であれば、文脈を把握しやすく、翻訳ミスも減らせます。文字列リソースはアプリの品質とユーザー体験に直接関係します。
16.3 colorは意味ベースで管理する
色リソースは、具体的な色名だけでなく、用途や意味に基づいて命名することが重要です。たとえば、primary、on_primary、background、surface、error のようにすると、デザインシステムと連携しやすくなります。blue_500 のような名前だけで管理すると、ブランドカラーが変わったときに意味がずれることがあります。色はUI全体の一貫性に関わるため、テーマ設計と合わせて考えるべきです。特にダークモード対応では、意味ベースの色管理が重要になります。
16.4 layoutは対応する画面が分かる名前にする
XMLレイアウトでは、activity_main、fragment_login、item_user、dialog_confirm のように、種類と用途を組み合わせた名前にします。これにより、Kotlinファイルとの対応関係が分かりやすくなります。命名が統一されていないと、画面数が増えたときに目的のレイアウトを探すだけで時間がかかります。RecyclerViewの項目、ダイアログ、BottomSheet、共通部品なども、種類が分かる接頭辞を付けると整理しやすくなります。layout名は画面構造を理解するための重要な手がかりです。
16.5 dimensは共通ルールとして使う
dimens.xml には、余白、文字サイズ、高さ、幅などを定義できます。spacing_small、spacing_medium、spacing_large、text_size_body のように共通値を定義すると、画面ごとのばらつきを減らせます。ただし、すべての数値を無理にdimensへ入れる必要はありません。本当に共通化する価値がある値、デザインルールとして使う値を中心に管理するのが現実的です。dimensを使いすぎると、逆にどの値がどこで使われているのか分かりにくくなることもあります。
16.6 命名規則はレビューで守る
命名規則は、ドキュメントに書くだけでは守られません。コードレビューで確認し、既存リソースとの重複を避け、不要なリソースを削除する運用が必要です。リソース名が整理されているプロジェクトでは、画面修正やデザイン変更が早くなります。逆に、命名が乱れたプロジェクトでは、同じような画像や文字列が複数作られ、不要なリソースが残ります。リソース命名は小さな作業に見えますが、長期的な開発効率を大きく左右します。
17. 環境別設定の構成
Androidアプリでは、開発環境、検証環境、本番環境で設定を分けることがよくあります。API URL、ログ出力、Firebase設定、機能フラグなどを安全に管理する必要があります。
17.1 debugとreleaseで設定を分ける
debugとreleaseでは、利用するAPI接続先、ログ出力、クラッシュレポート、機能フラグが異なることがあります。debugでは開発用サーバーへ接続し、releaseでは本番サーバーへ接続する構成が一般的です。この差分をコード内のif文だけで管理すると、本番に開発設定が混ざるリスクがあります。GradleのbuildTypesやソースセットを使って設定を分ければ、ビルドごとの差分を明確にできます。環境設定は、アプリの安全性とリリース品質に直結します。
17.2 BuildConfigで値を注入する
BuildConfigを使うと、Gradleで定義した値をKotlinコードから参照できます。API URL、ログ有効化、機能フラグなどをビルドタイプやflavorごとに変える場合に便利です。ただし、BuildConfigに入れた値はアプリに含まれるため、秘密情報を置くべきではありません。APIキーや秘密鍵、認証情報のような重要情報は、解析される可能性があります。BuildConfigは設定値の切り替えには便利ですが、機密情報の保管場所ではないことを理解する必要があります。
17.3 flavorでブランドや顧客差分を管理する
productFlavorsを使うと、顧客別、ブランド別、国別に異なる設定を持たせられます。アプリ名、アイコン、API URL、機能有効化、リソース差分などをflavorごとに管理できます。受託開発やホワイトラベルアプリでは特に便利です。ただし、flavorが増えるほど、テスト対象とリリース対象も増えます。顧客ごとの差分をすべてflavorで表現すると複雑になりすぎる場合もあるため、サーバー側設定やリモート設定と組み合わせることも検討すべきです。
17.4 google-services.jsonは環境ごとに管理する
Firebaseを使う場合、google-services.json を環境やflavorごとに分けることがあります。開発用Firebaseプロジェクト、本番用Firebaseプロジェクト、顧客別Firebaseプロジェクトがある場合、それぞれ対応する設定ファイルが必要になります。このファイルを誤って配置すると、分析データや通知、クラッシュレポートが別環境へ送られる可能性があります。Firebase設定は一度間違えると調査が面倒になるため、フォルダ配置とリリース前確認を慎重に行うべきです。
17.5 local.propertiesは個人環境用に使う
local.properties は、開発者のローカル環境に依存する設定を置くためのファイルです。通常はAndroid SDKのパスなどが含まれ、Git管理しないことが一般的です。個人ごとに異なる設定をここで管理できますが、チーム全体で共有すべき設定を入れるべきではありません。共有すべき設定をlocal.propertiesに置くと、CIや他の開発者環境で再現できない問題が起きます。個人環境設定とプロジェクト共通設定は明確に分ける必要があります。
17.6 環境別設定は誤配布を防ぐ仕組みが必要
環境別設定では、開発用、本番用、検証用の差分を明確にし、誤配布を防ぐことが最も重要です。リリース版が開発サーバーへ接続したり、debug機能が本番に残ったりすると、大きな事故につながります。CI/CDでは、どのvariantをビルドしているか、どの設定が使われているかを明示し、成果物名にも環境名を含めると安全です。環境設定は人の注意だけに頼ら安全です。環境設定は人の注意だけに頼らず、ビルド設定と自動チェックでミスを防ぐべきです。
18. CI/CDとプロジェクト構成
CI/CDを前提にしたAndroidプロジェクト構成は、品質とリリース速度を安定させます。ビルド、テスト、Lint、署名、配布を自動化できる構成にしておくことが重要です。
18.1 CIではビルドとテストを自動化する
CIでは、プルリクエストやmainブランチへのマージ時に、ビルド、Unit Test、Lint、静的解析を自動実行することが一般的です。これにより、壊れたコードや品質問題を早期に発見できます。手作業で確認していると、開発者によって確認範囲が変わり、見落としが発生します。CIに組み込むことで、チーム全体で同じ品質基準を保てます。Androidプロジェクト構成では、CIで実行しやすいGradleタスクやテスト配置にしておくことが重要です。
18.2 Gradleタスクを明確にする
CIで使うGradleタスクは、プロジェクト構成に合わせて整理する必要があります。assembleDebug、testDebugUnitTest、lintDebug、connectedAndroidTest など、目的に応じたタスクを使い分けます。build variantが多い場合は、どのvariantをCIで常時確認するかを決める必要があります。すべてのvariantを毎回ビルドすると時間がかかるため、主要variantは毎回、その他は定期実行にするなどの工夫が必要です。CI設計はプロジェクト構成と密接に関係します。
18.3 Lintと静的解析を組み込む
Android Lint、ktlint、detektなどをCIへ組み込むことで、コード品質や潜在的な問題を自動検出できます。未使用リソース、危険なAPI利用、命名規則違反、複雑すぎる関数などを早期に見つけられます。ただし、警告が多すぎると開発者が無視するようになります。最初は重要なルールから導入し、徐々に厳しくしていくと現場に定着しやすいです。静的解析は、単なるルール押し付けではなく、長期保守性を守るための仕組みです。
18.4 署名情報はCIの秘密情報で管理する
releaseビルドでは署名が必要です。署名キーやパスワードをリポジトリに直接置くのは危険であり、CIのシークレット管理機能を使うのが一般的です。署名情報の管理を誤ると、第三者に悪用されるリスクや、アプリ更新ができなくなるリスクがあります。CI/CD構成では、署名ファイルの復号、環境変数の注入、リリースvariantのビルドを安全に自動化する必要があります。リリース作業を属人化させないためにも、署名管理は慎重に設計すべきです。
18.5 配布先に応じてパイプラインを分ける
Androidアプリの配布先は、社内テスター、Firebase App Distribution、Google Play内部テスト、本番リリースなど複数あります。それぞれ必要なビルドvariant、署名、配布設定が異なります。CI/CDでは、配布先ごとにパイプラインを分けると管理しやすくなります。たとえば、developブランチではdebug配布、releaseブランチではrelease候補配布、tag作成時に本番向けAAB生成という流れが考えられます。配布フローを明確にすると、リリースミスを減らせます。
18.6 CI/CDを前提に構成すると属人化を防げる
CI/CDを前提にしたプロジェクト構成では、誰がビルドしても同じ成果物が作れる状態を目指します。ローカル環境だけでしかビルドできない、特定担当者だけが署名手順を知っている、リリース前確認が手作業だけになっている状態は危険です。Gradle設定、環境変数、署名、テスト、Lint、配布をCIに組み込めば、リリース作業の再現性が高まります。Androidプロジェクトは、開発だけでなくリリースまで含めて構成を設計する必要があります。
19. セキュリティを意識した構成
Androidプロジェクト構成では、セキュリティを常に意識する必要があります。権限、Manifest、ログ、依存関係、署名、秘密情報の扱いを誤ると、ユーザーや企業に大きな影響を与える可能性があります。
19.1 秘密情報をコードに置かない
APIキー、秘密鍵、パスワード、アクセストークンなどをソースコードやリソースに直接置くのは危険です。Androidアプリはユーザー端末に配布されるため、アプリ内に含まれる情報は解析される可能性があります。BuildConfigやstrings.xmlに入れた値も完全に安全ではありません。どうしてもクライアント側に設定値が必要な場合は、公開されても問題ない値かどうかを確認する必要があります。重要な秘密情報は、原則としてサーバー側で管理するべきです。
19.2 Manifestのexport設定を確認する
Manifestの android:exported 設定は、Activity、Service、Receiverなどが外部アプリから呼び出されるかどうかに関わります。外部公開する必要のないコンポーネントがexportされていると、攻撃面が広がる可能性があります。特にintent filterを持つコンポーネントは注意が必要です。リリース前にはMerged Manifestを確認し、不要なexport設定や危険なintent filterがないかを見ます。Manifest設定はアプリのセキュリティ境界を決める重要な要素です。
19.3 ログに機密情報を出さない
debugビルドではログ出力が便利ですが、releaseビルドで個人情報、トークン、位置情報、決済情報、APIレスポンス全体をログに出すのは危険です。ログは障害解析に役立つ一方、扱いを誤ると情報漏えいの原因になります。buildTypesを使ってreleaseではログを抑制する、ログ出力ライブラリで本番時の出力を制御するなどの対策が必要です。ログは開発者向けの情報ですが、本番環境ではユーザーの安全にも関わります。
19.4 外部ライブラリの安全性を確認する
外部ライブラリは便利ですが、脆弱性、メンテナンス停止、ライセンス問題のリスクがあります。導入前には、更新頻度、利用実績、ライセンス、脆弱性対応状況を確認するべきです。長期間更新されていないライブラリや、メンテナーが少ないライブラリを重要機能に使う場合は注意が必要です。依存関係スキャンや定期的なバージョン更新をCIに組み込むことで、リスクを下げられます。ライブラリ選定もAndroidプロジェクト構成の一部です。
19.5 難読化と最適化を正しく使う
releaseビルドでは、R8による最適化や難読化を使うことがあります。これにより、アプリサイズを削減し、コード解析を難しくできます。ただし、難読化設定を誤ると、リフレクション、シリアライズ、DI、JSON変換、外部SDKの動作に影響する場合があります。releaseビルドでのみ発生する不具合を避けるためには、難読化ありの成果物でテストする必要があります。ProGuardルールやconsumer rulesも、依存ライブラリと合わせて管理することが重要です。
19.6 セキュリティ確認を運用に組み込む
セキュリティは、リリース直前に一度だけ確認すればよいものではありません。Manifest変更、権限追加、外部ライブラリ追加、API通信追加、署名設定変更、ログ処理変更のたびに確認する必要があります。コードレビュー、CI、静的解析、依存関係スキャンを組み合わせることで、セキュリティ確認を日常的な開発プロセスへ組み込めます。安全なAndroidプロジェクトは、個人の注意に頼るのではなく、仕組みとしてセキュリティを維持します。
20. Androidプロジェクト構成のまとめ
Androidプロジェクト構成は、アプリ開発の基礎であり、長期運用の土台です。小規模アプリでも、大規模アプリでも、構成の意味を理解しておくことで、開発速度、保守性、品質、安全性が大きく変わります。
20.1 初心者は基本フォルダから覚える
初心者が最初に覚えるべきなのは、app、src/main、kotlin/java、res、AndroidManifest.xml、build.gradle.kts、test、androidTest です。この基本を理解すれば、Android Studioが自動生成する構成に振り回されにくくなります。最初からマルチモジュールやCI/CDまで完璧に理解する必要はありません。まずは、どのフォルダが何を担当し、どのファイルを変更すると何に影響するのかを把握することが大切です。
20.2 小規模アプリはシンプルでよい
小規模アプリでは、app モジュールだけで始めても問題ありません。ただし、シンプルであることと無秩序であることは違います。ui、data、domain、model、network のように基本的な責務を分け、リソース名やManifest、Gradle依存関係を丁寧に管理するべきです。最初から過剰設計にする必要はありませんが、将来整理しやすいように最低限のルールを持つことが重要です。小さなアプリでも構成の乱れは後から大きな負担になります。
20.3 中規模アプリは責務分離を重視する
中規模アプリでは、画面数や機能数が増え、コードの置き場所が曖昧になりやすくなります。この段階では、UI、ViewModel、UseCase、Repository、DataSource、Modelを分け、テストも整備する必要があります。Gradle依存関係やリソース命名、環境別設定も複雑になり始めるため、プロジェクト構成ルールを明文化するとよいです。中規模アプリで構成整理を怠ると、大規模化したときに一気に保守が難しくなります。早めの整理が重要です。
20.4 大規模アプリはモジュール化が重要になる
大規模アプリでは、マルチモジュール構成、依存関係管理、Version Catalog、CI/CD、feature module、core module、テスト戦略が重要になります。複数チームが同時に開発する場合、モジュール境界が責務境界として機能します。feature同士が直接依存しないようにし、共通処理はcoreへ整理し、appモジュールは全体の組み立て役にする構成が有効です。ただし、モジュールを増やしすぎると設定が複雑になるため、目的を持って分割する必要があります。
20.5 よくある失敗を避ける
Androidプロジェクトでよくある失敗は、app にすべてを詰め込む、ActivityやFragmentが肥大化する、Manifestに不要な権限が残る、リソース名がバラバラになる、Gradle依存関係が整理されていない、テストがない、debug設定がreleaseへ混ざる、というものです。これらは最初は小さな問題に見えますが、機能追加やリリース時に大きな負担になります。失敗を避けるには、構成ルールを決め、レビューし、定期的にリファクタリングすることが重要です。
20.6 理想は変更しやすい構成
理想的なAndroidプロジェクト構成は、見た目が複雑な構成ではなく、変更しやすい構成です。新しい画面を追加しやすく、既存機能を修正しやすく、テストを書きやすく、依存関係を追いやすく、リリースしやすいことが重要です。プロジェクト構成に唯一の正解はありませんが、責務が明確で、命名が統一され、GradleとManifestが整理され、テストとCI/CDが自然に動く構成は、長期的に強いです。Androidプロジェクト構成は、単なるフォルダ整理ではなく、アプリの品質を支える設計そのものです。
おわりに
Androidプロジェクト構成を理解すると、Android Studioが自動生成するファイルやフォルダの意味が明確になります。app モジュールはアプリ本体、src/main は共通ソースセット、res はリソース管理、AndroidManifest.xml はアプリ情報の宣言、Gradleはビルド管理、test と androidTest は品質保証の場所です。それぞれの役割を理解することで、エラー対応、機能追加、リリース作業がかなり進めやすくなります。
良いAndroidプロジェクト構成は、最初から大規模で複雑なものにする必要はありません。小規模ではシンプルに始め、アプリが成長したらパッケージ整理、テスト強化、依存関係管理、CI/CD、マルチモジュール化へ段階的に進めるのが現実的です。重要なのは、現在の規模に合った構成を選びながら、将来の変更に耐えられる余地を残すことです。
Android開発では、画面を作る力だけでなく、構成を設計する力が長期的な品質を左右します。プロジェクト構成を理解し、責務を分け、リソースとGradleを整理し、Manifestとセキュリティを慎重に管理し、テストとCI/CDを組み合わせることで、保守しやすく成長に強いAnroidアプリを作ることができます。
EN
JP
KR