依存関係インジェクション(DI)はプログラミングで広く使用されている手法で、Android 開発にも適しています。DI の原則に従うことで、優れたアプリ アーキテクチャの土台を築くことができます。
依存関係インジェクションを実装すると、次のようなメリットがもたらされます。
- コードを再利用できる
- リファクタリングが容易になる
- テストが容易になる
依存関係インジェクションの基礎
このページでは、Android での依存関係インジェクションについて具体的に説明する前に、依存関係インジェクションの大まかな仕組みを示します。
依存関係インジェクションとは
多くの場合、クラスは他のクラスへの参照を必要とします。たとえば、Car
クラスが Engine
クラスへの参照を必要とする場合、このようなクラスの関係を「依存関係」と呼びます。この例では、Car
クラスは、実行に必要な Engine
クラスのインスタンスに依存します。
クラスが必要なオブジェクトを取得するには、次の 3 つの方法があります。
- クラス自身が必要な依存関係を構築する。上記の例では、
Car
はEngine
のインスタンスを独自に作成して初期化します。 - 別の場所から入手する。一部の Android API(
Context
ゲッターやgetSystemService()
など)は、この方法で機能します。 - パラメータとして受け取る。アプリを通じて、クラスの構築時に依存関係を提供したり、依存関係を必要とする関数に個別に渡したりできます。上記の例では、
Car
コンストラクタはパラメータとしてEngine
を受け取ります。
3 つ目の方法が依存関係インジェクションです。このアプローチでは、クラス インスタンスが独自に依存関係を取得するのではなく、アプリでクラスの依存関係を取得してクラスに渡します。
次の例をご覧ください。このコードは、依存関係インジェクションを行わないで独自に Engine
依存関係を作成する Car
を表しています。
class Car { private val engine = Engine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }
class Car { private Engine engine = new Engine(); public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }

これは依存関係インジェクションの例ではありません。Car
クラスは独自の Engine
を構築しているからです。この方法には次のような問題があります。
Car
とEngine
が緊密に結び付けられます。つまり、Car
のインスタンスは 1 種類のEngine
を使用し、サブクラスや代替実装を簡単に使用することができません。Car
が独自のEngine
を構築する場合、Gas
タイプとElectric
タイプのエンジンについて同じCar
を再利用する代わりに、2 つのタイプのCar
を作成する必要があります。Engine
への依存関係が固定されていると、テストが困難になります。Car
はEngine
の実際のインスタンスを使用するので、テストダブルを使用して別のテストケース用にEngine
を変更することができません。
依存関係インジェクションを行うコードがどのように動作するかを見てみましょう。Car
の各インスタンスが初期化時に独自の Engine
オブジェクトを構築するのではなく、Engine
オブジェクトをコンストラクタ内でパラメータとして受け取ります。
class Car(private val engine: Engine) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() }
class Car { private final Engine engine; public Car(Engine engine) { this.engine = engine; } public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Engine engine = new Engine(); Car car = new Car(engine); car.start(); } }

main
関数は Car
を使用します。Car
は Engine
に依存するため、アプリは Engine
のインスタンスを作成し、それを使用して Car
のインスタンスを構築します。この DI ベースのアプローチには、次の利点があります。
Car
を再利用できる。Engine
のさまざまな実装をCar
に渡すことができます。たとえば、Car
が使用するEngine
の新しいサブクラスをElectricEngine
という名前で定義できます。DI を使用する場合、必要なのは更新されたElectricEngine
サブクラスのインスタンスを渡すことだけです。Car
はそれ以上の変更なしで機能します。Car
のテストが容易になる。テストダブルを導入して、さまざまなシナリオをテストできます。たとえば、Engine
のテストダブルをFakeEngine
という名前で作成し、さまざまなテスト用に構成できます。
Android で依存関係インジェクションを行う主な方法は 2 つあります。
コンストラクタ インジェクション。上記の方法です。クラスの依存関係をコンストラクタに渡します。
フィールド インジェクション(またはセッター インジェクション)。アクティビティやフラグメントなど、一部の Android フレームワーク クラスはシステムによってインスタンス化されるため、コンストラクタ インジェクションは不可能です。フィールド インジェクションでは、クラスの作成後に依存関係がインスタンス化されます。次のようなコードを使用します。
class Car { lateinit var engine: Engine fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.engine = Engine() car.start() }
class Car { private Engine engine; public void setEngine(Engine engine) { this.engine = engine; } public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.setEngine(new Engine()); car.start(); } }
自動の依存関係インジェクション
上記の例では、ライブラリに依存せずに、さまざまなクラスの依存関係を独自に作成、提供、管理しています。この方法は、手動の依存関係インジェクション(手動 DI)と呼ばれます。Car
の例では依存関係は 1 つのみでしたが、依存関係とクラスの数が増えると、手動の依存関係インジェクションは面倒な作業になります。手動の依存関係インジェクションには、他にも問題がいくつかあります。
大規模なアプリでは、すべての依存関係を取得して正しく接続するために、大量のボイラープレート コードが必要になることがあります。マルチレイヤ アーキテクチャでは、最上位レイヤのオブジェクトを作成するために、その下のレイヤの依存関係をすべて指定する必要があります。たとえば、実際の自動車を組み立てるには、エンジン、トランスミッション、シャーシ、その他の部品が必要です。さらに、エンジンにはシリンダーと点火プラグが必要です。
遅延初期化を使用する場合やオブジェクトのスコープをアプリのフローに設定する場合など、依存関係を渡す前に依存関係を構築できない場合は、メモリ内で依存関係のライフタイムを管理するカスタム コンテナ(または依存関係のグラフ)を作成および管理する必要があります。
この問題は、依存関係の作成と提供のプロセスを自動化するライブラリにより解決できます。それには次の 2 種類の方法があります。
実行時に依存関係を接続するリフレクション ベースのソリューション。
コンパイル時に依存関係を接続するコードを生成する静的ソリューション。
Dagger は、Google が管理する Java、Kotlin、Android で広く使用されている依存関係インジェクション ライブラリです。Dagger は、アプリ用の依存関係のグラフを作成して管理することで、アプリでの DI の利用を促進します。完全に静的な依存関係をコンパイル時に提供することにより、Guice のようなリフレクション ベースのソリューションで発生する開発とパフォーマンスに関する問題の多くを解決できます。
依存関係インジェクションの代替手段
依存関係インジェクションの代替手段として、サービス ロケータがあります。サービス ロケータ設計パターンでは、実際の依存関係からクラスをより確実に分離することができます。サービス ロケータと呼ばれるクラスを作成すると、依存関係を作成して保存したうえで、その依存関係をオンデマンドで提供できます。
object ServiceLocator { fun getEngine(): Engine = Engine() } class Car { private val engine = ServiceLocator.getEngine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }
class ServiceLocator { private static ServiceLocator instance = null; private ServiceLocator() {} public static ServiceLocator getInstance() { if (instance == null) { synchronized(ServiceLocator.class) { instance = new ServiceLocator(); } } return instance; } public Engine getEngine() { return new Engine(); } } class Car { private Engine engine = ServiceLocator.getInstance().getEngine(); public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }
サービス ロケータ パターンは、要素の使用方法の点で依存関係インジェクションと異なります。サービス ロケータ パターンでは、注入されるオブジェクトをクラスが制御し、注入を要求します。依存関係インジェクションでは、必要なオブジェクトをアプリが制御し、事前に注入します。
依存関係インジェクションとの比較:
サービス ロケータでは依存関係のコレクションが必要となり、すべてのテストが同じグローバル サービス ロケータとやり取りする必要があるため、コードのテストが困難になります。
依存関係は、API サーフェスではなくクラス実装にエンコードされます。そのため、クラスが外部に必要とするものを把握することが困難です。その結果、
Car
またはサービス ロケータで利用可能な依存関係を変更すると、参照が失敗してランタイム エラーまたはテストエラーが発生する可能性があります。アプリ全体のライフタイム以外の期間にスコープを設定したい場合、オブジェクトのライフタイムの管理が難しくなります。
Android アプリでは Hilt を使用する
Hilt は、Android で依存関係インジェクションを行うための Jetpack の推奨ライブラリです。Hilt は、プロジェクト内のすべての Android クラスにコンテナを提供し、そのライフサイクルを自動で管理することで、アプリケーションで DI を行うための標準的な方法を定義します。
Hilt は、よく知られた DI ライブラリである Dagger の上に構築されているため、コンパイル時の正確性、実行時のパフォーマンス、スケーラビリティ、Android Studio のサポートといった Dagger の恩恵を受けられます。
Hilt の詳細については、Hilt を使用した依存関係インジェクションをご覧ください。
まとめ
依存関係インジェクションには次のような利点があります。
クラスの再利用と依存関係の分離: 依存関係の実装を簡単に切り替えられます。制御の反転により、コードの再利用性が向上します。クラスで依存関係の作成方法を管理する必要がなくなり、どのような構成でも機能します。
リファクタリングの容易さ: 依存関係が、実装の詳細部分として見えなくなるのではなく、API サーフェスの検証可能な部分に組み込まれるため、オブジェクトの作成時またはコンパイル時に依存関係をチェックできます。
テストの容易さ: クラスで依存関係を管理しないため、テストの際には複数の実装を用意して、さまざまなケースを検証できます。
依存関係インジェクションの利点を十分に理解するには、手動の依存関係インジェクションで説明している手動 DI を実際のアプリで試してみてください。
参考情報
依存関係インジェクションの詳細については、以下の参考情報をご覧ください。
サンプル
Mir 2: Return of the King は、Actoz Soft がライセンスを付与し、 HK ZHILI YAOAN LIMITED が Unity ゲームエンジンを使用して開発した、高品質の Legend IP モバイルゲームです。 このゲームは、韓国のファンタジー MMORPG を代表する Mir 2 の雰囲気を完璧に再現するだけでなく、装備の収集、大規模な砂攻撃、その他のコア ゲームプレイなど、最も人気のあるゲーム コンテンツも数多く提供しています。 Wuthering Waves は、Kuro Games が開発した高品質のアクション RPG ゲームです。長時間のゲーム セッションでプレミアムなユーザー エクスペリエンスを持続的に提供するには、消費電力を最適化することが非常に重要です。 Android Studio では、Hedgehog(2023.1.1)から Power Profiler が導入されました。これは、デベロッパーが On Device Power Rails Godot Engine は、Android を堅牢にサポートする人気の高いマルチプラットフォームのオープンソース ゲームエンジンです。Godot は、ほぼすべてのジャンルのゲームの作成に使用でき、2D グラフィックと 3D グラフィックの両方に対応しています。Godot バージョン 4 では、高忠実度グラフィック向けの高度な機能を備えた新しいレンダリング システムが導入されました。Godot 4 レンダラは、Vulkan などの最新のグラフィック API 向けに設計されています。 Godot Android Dynamic Performance Framework(ADPF)は、アプリケーションのパフォーマンスを最適化したいデベロッパー向けの Google の強力なツールです。ADPF は、サーマル API を介してデバイスの熱状態に関するリアルタイム情報を提供します。この情報は、アプリのグラフィック設定の調整に使用されます。 研究目的で、Arm は Unreal Engine と ADPF を使用してデモを開発し、ADPF NCSoft Lineage W は、NCSoft が開発した大規模多人数同時参加型オンライン ロールプレイング ゲーム(MMORPG)です。このゲームは、元の Lineage W ゲームの遺産を受け継いでおり、世界中のプレーヤーがグローバル サーバーを通じて協力して競い合うことができる環境を提供します。独自の中世ファンタジーの世界を舞台とする Lineage W は、さまざまなクラス、スキル、戦闘システムを通じて、プレイヤーに深いゲーム体験を提供します。 NCSoft は Android パフォーマンスと温度管理の改善は、Android で成功するゲームを開発するために不可欠です。従来、デベロッパーはゲームの忠実度を下げるかレンダラをさらに最適化することで、こうした問題に対処する必要がありました。こうした変更はゲームに固有の傾向があり、多くの場合、柔軟性に欠けます。 Android エコシステムには、アダプティブ パフォーマンス API がデベロッパーに提供されています。アダプティブ パフォーマンス機能の統合を簡素化し、エコシステムの断片化を減らすために、Google と Call of Duty: Warzone Mobile は、人気の コール オブ デューティ シリーズのファースト パーソン アクション ゲームです。非常に人気のあるコンソール ゲームと PC ゲームをモバイルで実現する場合は、モバイルの低レベル API を活用して、優れたプレーヤー エクスペリエンスを提供します。 技術的な観点から見ると、モバイル実装の目標は、幅広い Android モバイル デバイスをサポートすると同時に、コンソール バージョンに可能な限り近い形で実装を維持し、グラフィック サマナーズウォー: クロニクル は、韓国のゲーム デベロッパー Com2uS が 2023 年 3 月に全世界でリリースしたモバイル MMORPG です。これまでに『サマナーズウォー: Sky Arena』は全世界で 1 億 8, 000 万回以上ダウンロードされ、収益は 27 億ドルを超えています。ファンタジーの世界を舞台にさまざまなモンスターを集めてトレーニングし、他のプレーヤーと戦うサマナーズウォーは、世界で最も人気のあるモバイルゲームの一つです。 約 10 Com2uS の Summoners War: Chronicles US(WW) と KR では、Android でのレンダリングに Vulkan のみを使用し、パフォーマンスが最大 30% 向上しています。 Vulkan は、デバイスのグラフィック ハードウェアとゲームの間の抽象化を最小限に抑えるために設計された最新のクロス プラットフォーム 3D グラフィック API です。Vulkan は OpenGL ES と比較して CPU オーバーヘッドが少なく、Vulkan Ares: Rise of Guardians は、アクション RPG シリーズの開発実績で知られる韓国のゲームスタジオ Second Dive が開発したモバイルから PC への SF MMORPG です。このゲームは Kakao Games によって公開されています。 Cat Daddy Games は、ワシントン州カークランドに拠点を置く 2K 完全所有のスタジオで、NBA 2K Mobile のデベロッパーです。チームは、特に「アプリケーション応答なし」エラー(ANR)を減らすことで、ゲームの全体的な品質と安定性を改善する必要がありました。ANR は、Android アプリの UI スレッドが長時間ブロックされると発生します。そのような場合、UI Devsisters はグローバルなモバイルゲーム デベロッパー兼パブリッシャーで、Cookie Run IP に基づいてカジュアル ゲームを制作しています。特に人気の高いゲームには Cookie Run: OvenBreak (ランニング アーケード)や Cookie Run: Kingdom (ソーシャル RPG)などがあり、韓国、台湾、米国のユーザーをはじめ、世界中のユーザーに愛されています。Cookie Run: OvenBreak はカジュアル ゲームですが、5 NEW STATE Mobile は Krafton のバトルロイヤル ゲームで、2021 年 11 月に全世界でリリースされ、リリース 1 か月で 4, 500 万回以上ダウンロードされました。KRAFTON, Inc. は、独立系ゲーム開発スタジオの集合体で、世界中のゲーマーに革新的で魅力的なエンターテイメント体験を生み出しています。PUBG Studios、Bluehole Studio、Striking Distance ポーランドを拠点とする Spokko は、要求の厳しい IP を扱っている意欲的なクリエイターのグループです。Spokko は CD PROJEKT ファミリーの一員ですが、独立企業として『 The Witcher: Monster Slayer ] の素晴らしい世界をスマートフォンに移しました。 ウィッチャー: モンスター スレイヤーは、拡張現実技術を使ったロケーション ベースの RPG ゲームです。このゲームは計算負荷の高いゲームであり、多くのデバイスに挑戦する必要があります。Spokko Cat Daddy Games は、ワシントン州カークランドに拠点を置く 2K 完全所有のスタジオです。NBA 2K Mobile、NBA SuperCard、WWE SuperCard シリーズのチームは、特にユーザーをサポートするデバイスに高品質のアセットを提供することで、ユーザーに提供するゲームの全体的な品質を向上させるソリューションを探していました。 同社は Play Asset Delivery を実装し、各ユーザーのデバイス設定に合わせて最適化された APK Electronic Arts (EA)は、米国カリフォルニア州に本社を置くゲーム会社です。スポーツ、アクション、レース、シミュレーションなど、さまざまなジャンルのゲームが幅広く製造されています。EA の開発スタジオである Firemonkeys は、 Real Racing 3 、 The Sims FreePlay 、 Need For Speed: No Limits のデベロッパーとしてよく知られています。Firemonkeys は、カスタム Unreal Engine は、Epic Games が開発したゲームエンジンです。あらゆる業界のクリエイターが、最先端のエンターテイメント、魅力的な可視化、没入感のある仮想世界を自由かつ制御できるようにします。一部の主要な Android ゲームは Unreal Engine を使用して構築されています。 図 1. Google Pixel 4 で実行されている Unreal Engine Suntemple のサンプルのスクリーンショット Epic などのゲーム ポーランドのワルシャワを拠点とするゲーム デベロッパー、CD Projekt RED(CDPR)は、ウィッチャー 3 内のミニゲーム「 グウェント ウィッチャーカードゲーム 」を一新し、2020 年 3 月に Google Play でスタンドアロンの無料ゲームとしてリリースしました。ファイルサイズの初期値が大きく、定期的なアップデートで追加のデバイス 2000 年、 Gameloft は、ゲームへの情熱と、世界中のプレーヤーにゲームを届けたいという思いから誕生しました。同社はモバイル向けゲーム開発の先駆的企業であり、現在では 190 本以上のゲームを提供しています。Gameloft のモバイルゲームの多くは、グラフィックが多用されているためにダウンロード サイズが大きくなっていました。そのため、同社にとって、App Bundle インフラストラクチャを基盤とするゲームサービス向けの配信機能のセットである Google Play Asset 米国を拠点とするデベロッパー RV AppStudios は、カジュアル ゲーム、子供向け教育アプリ、ユーティリティ アプリのポートフォリオ全体で、これまでに 2 億回以上のダウンロード数を誇っています。開発チームは、同社のアプリ Puzzle Kids - Animals Shape and Jigsaw Puzzles を Google Play Asset Delivery Gameloft は 20 年以上にわたり、モバイルゲームから PC やゲーム機のクロス プラットフォームのゲームに至るまで、デジタル プラットフォーム向けに革新的なゲーム エクスペリエンスを創出してきました。Gameloft は、独自の確立されたシリーズに加え、LEGO、Universal、Hasbro などの人気ブランドのゲームを開発しています。世界 3,600 人のチームを擁する同社のゲームは、毎月 100 か国以上で 5, 500 万人のユニーク プレーヤーに利用されています。 2018 モスクワを拠点とするゲーム開発会社の Pixonic は、モバイルアプリをアップグレードしてさらに多くのゲーマーにリーチできるよう日々努めています。同社の最も有名なタイトルのひとつである War Robots は、12 人のプレーヤー対プレーヤー(PvP)で、カスタマイズしたロボットを操作して戦闘を行えるゲームです。 2014 年のリリース当初、War Robots は初期の Android Gameloft は、常に最新のポータブル ハードウェアに対応したいち早くゲームを提供し、外出先でもゲームにワクワクするようなエクスペリエンスを提供できるよう努めています。同社は、モバイル レーシング シリーズの最新作品、 アスファルト 8: Airborne を、ChromeOS で開発するのが一番だと考えました。 Gameloft は複数のデバイス向けのゲーム開発に慣れていましたが、アスファルトの操作性を Chromebook に移植するのは難しいと思われました。ChromebookMir 2 で Frame Pacing ライブラリを使用してレンダリング パフォーマンスを改善
Kuro Games が Android Studio Power Profiler と ODPM を使用して Wuthering Waves の消費電力を 9.68% 削減
Android 向け Godot Engine Vulkan の最適化
Unreal Engine での Android Dynamic Performance Framework(ADPF)のスタートガイド
NCSoft の Lineage W が ADPF を使用して持続的なパフォーマンスを向上させ、サーマル スロットリングを回避
MediaTek が Android SoC の動的パフォーマンスを強化
Call of Duty Warzone Mobile で Vulkan を使用してグラフィックを改善
Com2uS - PC 版 Google Play Games
Com2uS が Vulkan を使用してグラフィックを改善
Kakao Games: Android への適応性により FPS の安定性が 96% 向上
2K は Android Game Development Kit を使用して ANR 発生率を 35% 削減
クッキーラン: オーブンブレイク - Play Asset Delivery で CDN コストを 20 万ドル以上削減
NEW STATE Mobile、Android GPU Inspector を使用して GPU 使用率を 22% 削減
The Witcher: Monster Slayer - Android Performance Tuner でリーチを拡大
2K が Play Asset Delivery で高品質のグラフィックを実現
Firemonkeys は AGDE を使用して開発とデバッグの時間を短縮
「AGDE はすごい!」Unreal Engine による Android 開発に最適
CD Projekt RED: Play Asset Delivery でアップデート サイズを 90% 削減し、アップデート率が 10% 向上
Gameloft、Google の Play Asset Delivery により新規ユーザーが 10% 増加
RV AppStudios、Google の Play Asset Delivery でユーザー維持率を改善
Gameloft の事例: Game Mode API を使用してデバイスの消費電力を 70% 削減し、プレイ時間を 35% 延長
Pixonic: 大画面向けに最適化することで ChromeOS でエンゲージメントを 25% 増加
Gameloft の事例: ChromeOS 向けに最適化することで収益を 9 倍に拡大