依存性注入入門
依存性注入 (DI) は、依存性の作成と管理の制御がアプリケーションから外部エンティティに移される制御の反転 (IoC) を実装するために使用される設計パターンです。これにより、よりモジュール化され、テスト可能で、保守しやすいコードを作成できます。これは、オブジェクトの作成責任がコードの他の部分に転送される手法です。これにより疎結合が促進され、コードがよりモジュール化され、管理しやすくなります。
クラスが適切に機能するには、他のクラスへの参照が必要になることがよくあります。たとえば、 Book
クラスを必要とするLibrary
クラスを考えてみましょう。これらの必要なクラスは依存関係と呼ばれます。Library クラスはBook
Library
のインスタンスがないと動作しません。
クラスが必要なオブジェクトを取得するには、主に次の 3 つの方法があります。
- 自己構築: クラスは独自の依存関係を作成して初期化します。たとえば、
Library
クラスはBook
クラスの独自のインスタンスを作成し、初期化します。 - 外部取得: クラスは外部ソースから依存関係を取得します。Context
Context
やgetSystemService()
などの一部の Android API は、このように動作します。 - 依存性の注入: 依存性は、クラスが構築されるとき、または依存性を必要とするメソッドを通じてクラスに提供されます。たとえば、
Library
コンストラクターは、Book
インスタンスをパラメーターとして受け取ります。
3 番目のオプションは依存性注入です。DI を使用すると、クラス インスタンスが依存関係を自ら取得するのではなく、クラスの依存関係を提供します。
依存性注入なしの例
DI がない場合、独自のBook
依存関係を作成するLibrary
次のようになります。
class Library { private Book book = new Book(); void open() { book.read(); } } public class Main { public static void main(String[] args) { Library library = new Library(); library.open(); } }
これは、 Library
クラスが独自のBook
を構築するため、DI の例ではありません。次の理由で問題が発生する可能性があります。
- 密結合:
Library
とBook
密結合されています。LibraryLibrary
インスタンスはBook
の 1 つのタイプを使用するため、サブクラスや代替実装を使用することが困難になります。 - テストの難しさ:
Book
への強い依存により、テストはより困難になります。Library
Book
の実際のインスタンスを使用するため、異なるテスト ケースに合わせてBook
を変更するテスト ダブルの使用が妨げられます。
依存性注入の例
DI を使用すると、 Library
の各インスタンスが独自のBook
オブジェクトを構築する代わりに、コンストラクターのパラメーターとしてBook
オブジェクトを受け取ります。
class Library { private Book book; Library(Book book) { this.book = book; } void open() { book.read(); } } public class Main { public static void main(String[] args) { Book book = new Book(); Library library = new Library(book); library.open(); }
メイン関数はLibrary
使用します。Library Library
Book
に依存しているため、アプリはBook
のインスタンスを作成し、それを使用してLibrary
のインスタンスを構築します。この DI ベースのアプローチの利点は次のとおりです。
-
Library
の再利用性:Book
のさまざまな実装をLibrary
に渡すことができます。たとえば、Library
で使用するEBook
というBook
の新しいサブクラスを定義するとします。DI を使用すると、EBook
のインスタンスをLibrary
に渡すだけで、それ以上の変更なしで動作します。 -
Library
の簡単なテスト: テスト ダブルを渡してさまざまなシナリオをテストできます。
別の DI の例
NotificationService
クラスがNotification
クラスに依存するシナリオを考えてみましょう。DI がない場合、 NotificationService
はNotification
のインスタンスを直接作成するため、異なるタイプの通知を使用したり、さまざまな通知実装でサービスをテストしたりすることが難しくなります。
DI を説明するために、この例をリファクタリングしてみましょう。
interface Notification { void send(); } class EmailNotification implements Notification { @Override public void send() { // Send email notification } } class SMSNotification implements Notification { @Override public void send() { // Send SMS notification } } class NotificationService { void sendNotification(Notification notification) { notification.send(); } }
現在、 NotificationService
特定のクラスではなくNotification
インターフェースに依存しています。これにより、 Notification
のさまざまな実装を相互に使用できます。使用する実装は、 sendNotification
メソッドを通じて設定できます。
NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification());
Android における依存性注入の方法
DI には主に 3 つのタイプがあります。
- メソッド (インターフェース) インジェクション: 依存関係は、クラスがインターフェースまたは別のクラスを介してアクセスできるメソッドを通じて渡されます。前の例は、メソッド インジェクションを示しています。
- コンストラクター インジェクション: 依存関係はコンストラクターを通じてクラスに渡されます。
class NotificationService { private final Notification notification; public NotificationService(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(new EmailNotification()); service.sendNotification(); } }
3. フィールド インジェクション (またはセッター インジェクション) : アクティビティやフラグメントなどの特定の Android フレームワーク クラスはシステムによってインスタンス化されるため、コンストラクター インジェクションは不可能です。フィールド インジェクションでは、クラスの作成後に依存関係がインスタンス化されます。
class NotificationService { private Notification notification; public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(); service.setNotification(new EmailNotification()); service.sendNotification(); } }
4. メソッド インジェクション: 依存関係はメソッドを通じて提供され、多くの場合@Inject
アノテーションが使用されます。
依存性注入の利点
- クラスの再利用性が向上し、特定の実装への依存度が低くなります。これは制御の反転によるもので、クラスは依存関係を管理せず、提供された任意の構成で動作するようになります。
- 依存関係は API サーフェスの一部であり、オブジェクトの作成時またはコンパイル時に検証できるため、リファクタリングが容易になります。
- クラスは依存関係を管理しないため、さまざまなシナリオをカバーするためにテスト中にさまざまな実装を渡すことが可能です。
自動化された依存性注入
前の例では、ライブラリを使用せずに、さまざまなクラスの依存関係を手動で作成、提供、管理しました。このアプローチは、手動の依存関係の注入と呼ばれます。単純なケースでは機能しますが、依存関係とクラスの数が増えると面倒になります。手動の依存関係の注入には、いくつかの欠点があります。
- 定型コード: 大規模なアプリケーションでは、すべての依存関係を管理し、それらを正しく接続すると、多くの繰り返しコードが発生する可能性があります。多層アーキテクチャでは、最上位層のオブジェクトを作成するには、その下の層のすべての依存関係を提供する必要があります。たとえば、コンピューターを構築するには、CPU、マザーボード、RAM、およびその他のコンポーネントが必要です。CPU には、トランジスタとコンデンサが必要になる場合があります。
- 複雑な依存関係の管理: 遅延初期化や、アプリ内の特定のフローにオブジェクトのスコープを設定するなど、事前に依存関係を構築できない場合は、カスタム コンテナー (または依存関係グラフ) を作成して維持し、メモリ内の依存関係の有効期間を管理する必要があります。
ライブラリは依存関係を作成して提供することで、このプロセスを自動化できます。これらのライブラリは、次の 2 つのカテゴリに分類されます。
- リフレクションベースのソリューション: 実行時に依存関係を接続します。
- 静的ソリューション: コンパイル時に依存関係を接続するコードを生成します。
Dagger は、Google が管理する Java、Kotlin、Android 向けの人気の依存性注入ライブラリです。Dagger は依存性グラフを作成および管理することで、アプリの DI を簡素化します。完全に静的なコンパイル時の依存性を提供し、Guice などのリフレクション ベースのソリューションに関連する開発およびパフォーマンスの問題の多くに対処します。
リフレクションベースのソリューション
これらのフレームワークは実行時に依存関係を接続します。
- Toothpick : リフレクションを使用して依存関係を接続するランタイム DI フレームワーク。軽量かつ高速に設計されており、Android アプリケーションに適しています。
静的ソリューション
これらのフレームワークは、コンパイル時に依存関係を接続するコードを生成します。
- Hilt : Dagger 上に構築された Hilt は、Android アプリケーションに Dagger 依存性注入を組み込む標準的な方法を提供します。定義済みのコンポーネントとスコープを提供することで、Dagger のセットアップと使用を簡素化します。
- Koin : Kotlin 用の軽量でシンプルな DI フレームワーク。Koin は DSL を使用して依存関係を定義し、セットアップと使用が簡単です。
- Kodein : 使いやすく理解しやすい Kotlin ベースの DI フレームワーク。依存関係を管理するためのシンプルで柔軟な API を提供します。
依存性注入の代替
依存性注入の代替として、サービス ロケーター パターンがあります。この設計パターンは、クラスを具体的な依存性から分離するのにも役立ちます。依存性を作成して保存し、必要に応じて提供する、サービス ロケーターと呼ばれるクラスを作成します。
object ServiceLocator { fun getProcessor(): Processor = Processor() } class Computer { private val processor = ServiceLocator.getProcessor() fun start() { processor.run() } } fun main(args: Array<String>) { val computer = Computer() computer.start() }
サービス ロケーター パターンは、依存関係の使用方法において依存性の注入とは異なります。サービス ロケーター パターンでは、クラスが必要な依存関係を要求しますが、依存性の注入では、アプリが必要なオブジェクトを積極的に提供します。
Dagger 2とは何ですか?
Dagger 2 は、Android 向けの人気の DI フレームワークです。コンパイル時のコード生成を使用し、高いパフォーマンスで知られています。Dagger 2 は、依存関係を処理するために必要なコードを生成することで依存性注入のプロセスを簡素化し、定型句を減らして効率を向上させます。
Dagger 2 は、Android での依存性注入のためのアノテーションベースのライブラリです。主なアノテーションとその目的は次のとおりです。
- @Module : 依存関係を提供するクラスを定義するために使用されます。たとえば、モジュールは Retrofit 用の
ApiClient
を提供できます。 - @Provides : モジュール内のメソッドに注釈を付けて、依存関係を作成して返す方法を指定します。
- @Inject : 依存関係を要求するために使用されます。フィールド、コンストラクター、メソッドに適用できます。
- @Component :
@Module
と@Inject
を橋渡しするインターフェース。すべてのモジュールが含まれ、アプリケーションのビルダーを提供します。 - @Singleton : 依存関係の単一のインスタンスが作成されるようにします。
- @Binds : 依存関係を提供するために抽象クラスで使用されます。
@Provides
に似ていますが、より簡潔です。
ダガーコンポーネント
Dagger はプロジェクトの依存関係グラフを生成し、必要に応じて依存関係を取得する場所を決定できます。これを有効にするには、インターフェースを作成し、 @Component
で注釈を付ける必要があります。
@Component
インターフェース内で、必要なクラス (例: BookRepository
) のインスタンスを返すメソッドを定義します。 @Component
アノテーションは、Dagger が公開する型を満たすために必要なすべての依存関係を含むコンテナーを生成するように Dagger に指示します。このコンテナーは Dagger コンポーネントと呼ばれ、Dagger が提供できるオブジェクトのグラフとその依存関係が含まれています。
例
LibraryRepository
に関する例を考えてみましょう。
- コンストラクターに注釈を付ける:
LibraryRepository
コンストラクターに@Inject
注釈を追加して、Dagger がLibraryRepository
のインスタンスを作成する方法を認識できるようにします。
public class LibraryRepository { private final LocalLibraryDataSource localDataSource; private final RemoteLibraryDataSource remoteDataSource; @Inject public LibraryRepository(LocalLibraryDataSource localDataSource, RemoteLibraryDataSource remoteDataSource) { this.localDataSource = localDataSource; this.remoteDataSource = remoteDataSource; } }
2. 依存関係に注釈を付ける: 同様に、依存関係のコンストラクター ( LocalLibraryDataSource
およびRemoteLibraryDataSource
) に注釈を付けて、Dagger が依存関係の作成方法を認識できるようにします。
public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } }
3. コンポーネントを定義する: 依存関係グラフを定義するために、 @Component
アノテーションが付けられたインターフェースを作成します。
@Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); }
プロジェクトをビルドすると、Dagger によって、通常はDaggerApplicationComponent
という名前のApplicationComponent
インターフェースの実装が生成されます。
使用法
生成されたコンポーネントを使用して、依存関係が自動的に注入されたクラスのインスタンスを取得できるようになりました。
public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }
アクティビティまたはフラグメントでは、 LibraryRepository
インスタンスを取得できます。
public class MainActivity extends AppCompatActivity { @Inject LibraryRepository libraryRepository; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MainApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected libraryRepository } }
Dagger 2 の主要概念
1. モジュール
∘ モジュールの主要概念
∘ コンポーネントにモジュールを含める
2. スコープ
3. コンポーネント
4. コンポーネントの依存関係
5. ランタイムバインディング
1. モジュール
Dagger 2 のモジュールは、コンポーネントに依存関係を提供する@Module
アノテーションが付けられたクラスです。モジュールには、依存関係の作成方法と提供方法を指定するために@Provides
または@Binds
アノテーションが付けられたメソッドが含まれています。モジュールは、アプリケーションに必要なオブジェクトの作成を整理および管理するために不可欠です。
モジュールの主要概念
- @Module アノテーション: このアノテーションは、クラスを Dagger モジュールとして定義するために使用されます。モジュール クラスには、依存関係を提供するメソッドが含まれています。
- @Provides アノテーション: このアノテーションは、モジュール内のメソッドで使用され、メソッドが特定の依存関係を提供することを示します。これらのメソッドは、依存関係のインスタンスを作成して返す役割を担います。
- @Binds アノテーション: このアノテーションは、抽象クラスで実装をインターフェースにバインドするために使用されます。これは
@Provides
よりも簡潔で、モジュールが抽象クラスの場合に使用されます。
モジュールの例
@Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } @Provides @Singleton OkHttpClient provideOkHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); } }
この例では、 NetworkModule
@Module
アノテーションが付けられたクラスです。このクラスには、 Retrofit
とOkHttpClient
のインスタンスを作成して返す、 @Provides
アノテーションが付けられた 2 つのメソッドが含まれています。
@Binds の使用
インターフェースとその実装がある場合は、 @Binds
を使用して実装をインターフェースにバインドできます。これは、 @Provides
を使用するよりも簡潔です。
public interface ApiService { void fetchData(); } public class ApiServiceImpl implements ApiService { @Override public void fetchData() { // Implementation } } @Module public abstract class ApiModule { @Binds abstract ApiService bindApiService(ApiServiceImpl apiServiceImpl); }
この例では、 ApiModule
@Module
アノテーションが付けられた抽象クラスです。bindApiService メソッドには@Binds
アノテーションが付けられ、 ApiServiceImpl
bindApiService
ApiService
バインドします。
モジュールは、提供する機能に基づいて整理できます。たとえば、ネットワーク操作、データベース操作、UI 関連の依存関係ごとに個別のモジュールを用意できます。
例:
- NetworkModule :
Retrofit
やOkHttpClient
などのネットワーク関連の依存関係を提供します。 - DatabaseModule :
RoomDatabase
のようなデータベース関連の依存関係を提供します。 - UIModule :
ViewModel
やPresenter
などの UI 関連の依存関係を提供します。
コンポーネントにモジュールを含める
モジュールは、それを必要とするクラスに依存関係を提供するためにコンポーネントに含まれています。設定方法は次のとおりです。
アプリケーションコンポーネント.java:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
この例では、 ApplicationComponent
には、アプリケーションへの依存関係を提供するためにNetworkModule
とDatabaseModule
が含まれています。
2. スコープ
Dagger 2 のスコープは、依存関係のライフサイクルを定義する注釈です。スコープにより、依存関係の単一のインスタンスが指定されたスコープ内で作成され、共有されることが保証されます。これにより、メモリを効率的に管理し、依存関係が適切な場所で再利用されることが保証されます。
- シングルトン スコープ: アプリケーションのライフサイクル全体を通じて依存関係のインスタンスが 1 つだけであることを保証します。
- アクティビティ スコープ: アクティビティのライフサイクル内で依存関係のインスタンスが 1 つだけであることを保証します。
- フラグメント スコープ: フラグメントのライフサイクル内で依存関係のインスタンスが 1 つだけであることを保証します。
1. シングルトンスコープ
定義: @Singleton
スコープは、依存関係の単一のインスタンスが作成され、アプリケーションのライフサイクル全体を通じて共有されることを保証します。
このスコープは通常、ネットワーク クライアント、データベース インスタンス、共有設定など、アプリケーション全体で共有する必要がある依存関係に使用されます。
例:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
この例では、 @Singleton
アノテーションにより、 NetworkModule
およびDatabaseModule
によって提供されるRetrofit
インスタンスとDatabase
インスタンスがシングルトンであり、アプリケーション全体で共有されることが保証されます。
2. 活動範囲
定義: @ActivityScope
(カスタム スコープ) は、アクティビティのライフサイクル内で依存関係の単一インスタンスが作成され、共有されることを保証します。
このスコープは、プレゼンターやビュー モデルなど、アクティビティに固有で、アクティビティが再作成されるたびに再作成する必要がある依存関係に役立ちます。
例:
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
この例では、 @ActivityScope
アノテーションにより、 ActivityModule
によって提供される依存関係がアクティビティのライフサイクルにスコープされることが保証されます。
3. フラグメントスコープ
定義: @FragmentScope
(別のカスタム スコープ) は、フラグメントのライフサイクル内で依存関係の単一のインスタンスが作成され、共有されることを保証します。
使用例: このスコープは、フラグメント固有のプレゼンターやビュー モデルなど、フラグメントが再作成されるたびに再作成する必要がある、フラグメントに固有の依存関係に役立ちます。
例:
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); }
この例では、 @FragmentScope
アノテーションにより、 FragmentModule
によって提供される依存関係がフラグメントのライフサイクルにスコープされることが保証されます。
3. コンポーネント
コンポーネントの依存関係により、あるコンポーネントが別のコンポーネントに依存することが可能になり、依存関係の再利用が可能になります。コンポーネントの依存関係には、主に次の 2 つの種類があります。
- アプリケーション コンポーネント: アプリケーション全体に必要な依存関係を提供します。
- アクティビティ コンポーネント: 特定のアクティビティ内で必要な依存関係を提供します。
1. アプリケーションコンポーネント
定義: アプリケーション コンポーネントは、アプリケーション全体で必要な依存関係を提供します。通常は、依存関係がアプリケーション全体で共有されるように、 @Singleton
でスコープ設定されます。
このコンポーネントは、ネットワーク クライアント、データベース インスタンス、共有設定など、グローバルに使用可能にする必要がある依存関係に使用されます。
例:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
この例では、 ApplicationComponent
、アプリケーション全体で共有されるRetrofit
インスタンスとDatabase
インスタンスを提供する役割を担っています。
2. アクティビティコンポーネント
定義: アクティビティ コンポーネントは、特定のアクティビティ内で必要な依存関係を提供します。通常、アクティビティが再作成されるたびに依存関係が再作成されるように、 @ActivityScope
などのカスタム スコープでスコープ設定されます。
このコンポーネントは、プレゼンターやビュー モデルなど、アクティビティに固有の依存関係に使用されます。
例:
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
この例では、 ActivityComponent
ApplicationComponent
に依存し、 MainActivity
に固有の依存関係を提供します。
4. コンポーネントの依存関係
コンポーネントの依存関係により、あるコンポーネントが別のコンポーネントに依存することが可能になり、依存関係の再利用が可能になります。コンポーネントの依存関係には、主に次の 2 つの種類があります。
- サブコンポーネント: サブコンポーネントは別のコンポーネントの子であり、親の依存関係にアクセスできます。
- 依存属性: これにより、コンポーネントはサブコンポーネントにならずに別のコンポーネントに依存できるようになります。
1. サブコンポーネント:
サブコンポーネントは別のコンポーネントの子であり、親の依存関係にアクセスできます。サブコンポーネントは親コンポーネント内で定義され、そのスコープを継承できます。
例:
@ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); }
この例では、 ActivitySubcomponent
親コンポーネントのサブコンポーネントであり、その依存関係にアクセスできます。
2. 依存属性
これにより、コンポーネントはサブコンポーネントにならずに別のコンポーネントに依存できるようになります。依存コンポーネントは、親コンポーネントによって提供される依存関係にアクセスできます。
例:
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
この例では、 ActivityComponent
ApplicationComponent
に依存しており、その依存関係にアクセスできます。
5. ランタイムバインディング
Dagger 2 のランタイム バインディングとは、必要なコンテキストに基づいて実行時に作成および管理される依存関係の提供を指します。
- アプリケーション コンテキスト: アプリケーションと同じ期間存続する必要がある依存関係に使用されます。
- アクティビティ コンテキスト: アクティビティと同じ期間存続する必要がある依存関係に使用されます。
1. アプリケーションのコンテキスト
定義: アプリケーション コンテキストは、アプリケーション全体のライフサイクルに関連付けられたコンテキストです。アプリケーション自体と同じ期間存続する必要がある依存関係に使用されます。
アプリケーション全体で共有され、アクティビティやフラグメントごとに再作成する必要のない依存関係。例としては、ネットワーク クライアント、データベース インスタンス、共有設定などがあります。
例:
@Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application provideApplication() { return application; } @Provides @Singleton Context provideApplicationContext() { return application.getApplicationContext(); } }
この例では、 AppModule
アプリケーション コンテキストをシングルトン依存関係として提供します。provideApplicationContext メソッドは、提供されるコンテキストがアプリケーションのライフサイクルに関連付けられていることprovideApplicationContext
保証します。
2. アクティビティのコンテキスト
定義: アクティビティ コンテキストは、特定のアクティビティのライフサイクルに関連付けられたコンテキストです。アクティビティ自体と同じ期間存続する必要がある依存関係に使用されます。
アクティビティに固有の依存関係であり、アクティビティが再作成されるたびに再作成する必要があります。例としては、ビュー モデル、プレゼンター、UI 関連の依存関係などがあります。
例:
@Module public class ActivityModule { private final Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } @Provides @ActivityScope Activity provideActivity() { return activity; } @Provides @ActivityScope Context provideActivityContext() { return activity; } }
この例では、 ActivityModule
アクティビティ コンテキストをスコープ指定された依存関係として提供します。provideActivityContext メソッドは、提供されたコンテキストがアクティビティのライフサイクルに関連付けられていることprovideActivityContext
保証します。
コンポーネントでのランタイムバインディングの使用
これらのランタイム バインディングを使用するには、コンポーネントに対応するモジュールを含める必要があります。
アプリケーションコンポーネント:
@Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); }
アクティビティコンポーネント:
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); }
コンテキストの挿入
コンポーネントとモジュールを設定したら、必要に応じてコンテキストをクラスに挿入できます。
アクティビティの例:
public class MainActivity extends AppCompatActivity { @Inject Context activityContext; @Inject Context applicationContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ApplicationComponent appComponent = ((MyApplication) getApplication()).getApplicationComponent(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .applicationComponent(appComponent) .activityModule(new ActivityModule(this)) .build(); activityComponent.inject(this); // Use the injected contexts Log.d("MainActivity", "Activity Context: " + activityContext); Log.d("MainActivity", "Application Context: " + applicationContext); } }
この例では、 MainActivity
依存性の注入を通じてアクティビティ コンテキストとアプリケーション コンテキストの両方を受け取ります。これにより、アクティビティは依存性の特定のニーズに基づいて適切なコンテキストを使用できるようになります。
例: Android アプリケーションで Dagger 2 を使用する
ダガー2の設定
プロジェクトで Dagger 2 を使用するには、 build.gradle
ファイルに次の依存関係を追加する必要があります。
dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }
2.x
を Dagger 2 の最新バージョンに置き換えます。
ステップ1: モジュールを定義する
依存関係を提供するモジュールを作成します。たとえば、 Retrofit
インスタンスを提供するNetworkModule
次のようになります。
@Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } }
ステップ2: コンポーネントを定義する
モジュールと依存関係を必要とするクラスをブリッジするコンポーネントを作成します。
@Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
ステップ3: 依存関係を注入する
コンポーネントを使用してクラスに依存関係を挿入します。たとえば、 Application
クラスでは次のようになります。
public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .networkModule(new NetworkModule()) .build(); applicationComponent.inject(this); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }
ステップ4: 注入された依存関係を使用する
これで、注入された依存関係をクラスで使用できるようになります。たとえば、 Activity
では次のようになります。
public class MainActivity extends AppCompatActivity { @Inject Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected Retrofit instance // ... } }
結論
このトピックを要約してみましょう:
- DI の主なポイントは、結合を緩め、依存関係を管理しやすくすることです。
- DI を使用すると、コードの柔軟性が向上し、テスト プロセスが簡素化されます。
- DI は、シナリオに基づいてさまざまな実装が行われる複雑なトピックです。
- さまざまな言語の DI には、操作方法に影響を与える可能性のある特殊性があります。
- Dagger 2 は依存関係の作成と提供のプロセスを自動化し、定型コードを削減して保守性を向上させます。
- Dagger 2 はコンパイル時の安全性を提供し、アプリケーションの実行前にすべての依存関係が満たされることを保証します。
- Dagger 2 はコンパイル時にコードを生成することで、リフレクション ベースのソリューションに関連するパフォーマンスのオーバーヘッドを回避します。