Dependency Injection (DI) ist ein Entwurfsmuster, das zur Implementierung von Inversion of Control (IoC) verwendet wird, wobei die Kontrolle über die Erstellung und Verwaltung von Abhängigkeiten von der Anwendung auf eine externe Entität übertragen wird. Dies hilft bei der Erstellung modulareren, testbareren und wartbareren Codes. Es handelt sich um eine Technik, bei der die Verantwortung für die Erstellung von Objekten auf andere Teile des Codes übertragen wird. Dies fördert eine lose Kopplung und macht den Code modularer und einfacher zu verwalten.
Klassen benötigen häufig Verweise auf andere Klassen, um richtig zu funktionieren. Betrachten Sie beispielsweise eine Library
, die eine Book
erfordert. Diese erforderlichen Klassen werden als Abhängigkeiten bezeichnet. Die Library
ist für ihre Funktion auf eine Instanz der Book
angewiesen.
Es gibt drei grundlegende Möglichkeiten für eine Klasse, die benötigten Objekte abzurufen:
Library
ihre eigene Instanz der Klasse Book
erstellen und initialisieren.Context
Getter und getSystemService()
, funktionieren auf diese Weise.Library
eine Book
als Parameter erhalten.Die dritte Option ist die Abhängigkeitsinjektion! Mit DI stellen Sie die Abhängigkeiten einer Klasse bereit, anstatt sie von der Klasseninstanz selbst abrufen zu lassen.
Ohne DI könnte eine Library
, die ihre eigene Book
erstellt, folgendermaßen aussehen:
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(); } }
Dies ist kein Beispiel für DI, da die Klasse Library
ihr eigenes Book
erstellt. Dies kann aus folgenden Gründen problematisch sein:
Library
und Book
sind eng gekoppelt. Eine Instanz von Library
verwendet einen Book
, was die Verwendung von Unterklassen oder alternativen Implementierungen erschwert.Book
macht das Testen anspruchsvoller. Library
verwendet eine echte Instanz von Book
und verhindert so die Verwendung von Testdoubles, um Book
für verschiedene Testfälle zu modifizieren. Mit DI erstellt nicht jede Instanz von Library
ihr eigenes Book
Objekt, sondern erhält ein Book
Objekt als Parameter in ihrem Konstruktor:
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(); }
Die Hauptfunktion verwendet Library
. Da Library
von Book
abhängt, erstellt die App eine Instanz von Book
und verwendet diese dann, um eine Instanz von Library
zu konstruieren. Die Vorteile dieses DI-basierten Ansatzes sind:
Library
: Sie können verschiedene Implementierungen von Book
an Library
übergeben. Sie können beispielsweise eine neue Unterklasse von Book
namens EBook
definieren, die Library
verwenden soll. Mit DI übergeben Sie einfach eine Instanz von EBook
an Library
und es funktioniert ohne weitere Änderungen.Library
: Sie können Test-Doubles übergeben, um verschiedene Szenarien zu testen. Stellen Sie sich ein Szenario vor, in dem eine NotificationService
Klasse auf einer Notification
Klasse basiert. Ohne DI erstellt der NotificationService
direkt eine Instanz von Notification
, was die Verwendung unterschiedlicher Benachrichtigungstypen oder das Testen des Dienstes mit verschiedenen Benachrichtigungsimplementierungen erschwert.
Um DI zu veranschaulichen, überarbeiten wir dieses Beispiel:
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(); } }
Jetzt hängt NotificationService
von der Notification
Schnittstelle ab und nicht von einer bestimmten Klasse. Dadurch können verschiedene Implementierungen von Notification
austauschbar verwendet werden. Sie können die Implementierung, die Sie verwenden möchten, über die Methode sendNotification
festlegen:
NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification());
Es gibt drei Haupttypen von DI:
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. Feldinjektion (oder Setter-Injektion) : Bestimmte Android-Framework-Klassen, wie Aktivitäten und Fragmente, werden vom System instanziiert, sodass eine Konstruktorinjektion nicht möglich ist. Bei der Feldinjektion werden Abhängigkeiten nach der Erstellung der Klasse instanziiert.
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. Methodeninjektion : Abhängigkeiten werden durch Methoden bereitgestellt, häufig unter Verwendung der Annotation @Inject
.
Im vorherigen Beispiel haben Sie die Abhängigkeiten verschiedener Klassen manuell erstellt, bereitgestellt und verwaltet, ohne eine Bibliothek zu verwenden. Dieser Ansatz wird als manuelle Abhängigkeitsinjektion bezeichnet. Während dieser Ansatz in einfachen Fällen funktioniert, wird er umständlich, wenn die Anzahl der Abhängigkeiten und Klassen zunimmt. Die manuelle Abhängigkeitsinjektion hat mehrere Nachteile:
Bibliotheken können diesen Prozess automatisieren, indem sie Abhängigkeiten für Sie erstellen und bereitstellen. Diese Bibliotheken fallen in zwei Kategorien:
Dagger ist eine beliebte, von Google verwaltete Bibliothek zur Abhängigkeitsinjektion für Java, Kotlin und Android. Dagger vereinfacht die Abhängigkeitsinjektion in Ihrer App, indem es den Abhängigkeitsgraphen für Sie erstellt und verwaltet. Es bietet vollständig statische Abhängigkeiten zur Kompilierungszeit und behebt viele der Entwicklungs- und Leistungsprobleme, die mit reflexionsbasierten Lösungen wie Guice verbunden sind.
Diese Frameworks verbinden Abhängigkeiten zur Laufzeit:
Diese Frameworks generieren Code, um Abhängigkeiten zur Kompilierzeit zu verbinden:
Eine Alternative zur Abhängigkeitsinjektion ist das Service Locator-Muster. Dieses Entwurfsmuster hilft auch dabei, Klassen von ihren konkreten Abhängigkeiten zu entkoppeln. Sie erstellen eine Klasse namens Service Locator, die Abhängigkeiten erstellt und speichert und sie bei Bedarf bereitstellt.
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() }
Das Service Locator-Muster unterscheidet sich von der Abhängigkeitsinjektion in der Art und Weise, wie Abhängigkeiten genutzt werden. Beim Service Locator-Muster fordern Klassen die Abhängigkeiten an, die sie benötigen. Bei der Abhängigkeitsinjektion stellt die App die erforderlichen Objekte proaktiv bereit.
Dagger 2 ist ein beliebtes DI-Framework für Android. Es verwendet Codegenerierung zur Kompilierungszeit und ist für seine hohe Leistung bekannt. Dagger 2 vereinfacht den Prozess der Abhängigkeitsinjektion, indem es den erforderlichen Code zur Handhabung von Abhängigkeiten generiert, Boilerplate-Code reduziert und die Effizienz verbessert.
Dagger 2 ist eine annotationsbasierte Bibliothek für die Abhängigkeitsinjektion in Android. Hier sind die wichtigsten Annotationen und ihre Zwecke:
ApiClient
für Retrofit bereitstellen.@Module
und @Inject
verbindet. Sie enthält alle Module und stellt den Builder für die Anwendung bereit.@Provides
, aber prägnanter. Dagger kann einen Abhängigkeitsgraphen für Ihr Projekt generieren, sodass es bei Bedarf feststellen kann, wo Abhängigkeiten abgerufen werden können. Um dies zu ermöglichen, müssen Sie eine Schnittstelle erstellen und diese mit @Component
kommentieren.
Innerhalb der @Component
-Schnittstelle definieren Sie Methoden, die Instanzen der benötigten Klassen zurückgeben (z. B. BookRepository
). Die @Component
Annotation weist Dagger an, einen Container mit allen Abhängigkeiten zu generieren, die erforderlich sind, um die von ihm bereitgestellten Typen zu erfüllen. Dieser Container wird als Dagger-Komponente bezeichnet und enthält ein Diagramm von Objekten, die Dagger zusammen mit ihren Abhängigkeiten bereitstellen kann.
Betrachten wir ein Beispiel mit einem LibraryRepository
:
LibraryRepository
Konstruktor eine @Inject
-Annotation hinzu, damit Dagger weiß, wie eine Instanz von LibraryRepository
erstellt wird. 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. Abhängigkeiten kommentieren : Kommentieren Sie auf ähnliche Weise die Konstruktoren der Abhängigkeiten ( LocalLibraryDataSource
und RemoteLibraryDataSource
), damit Dagger weiß, wie sie erstellt werden.
public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } }
3. Definieren Sie die Komponente : Erstellen Sie eine mit @Component
kommentierte Schnittstelle, um den Abhängigkeitsgraphen zu definieren.
@Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); }
Wenn Sie das Projekt erstellen, generiert Dagger für Sie eine Implementierung der ApplicationComponent
Schnittstelle, die normalerweise DaggerApplicationComponent
heißt.
Sie können jetzt die generierte Komponente verwenden, um Instanzen Ihrer Klassen mit automatisch eingefügten Abhängigkeiten abzurufen:
public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }
In Ihrer Aktivität oder Ihrem Fragment können Sie die LibraryRepository
Instanz abrufen:
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 } }
1. Module
∘ Schlüsselkonzepte der Module
∘ Module in Komponenten einbinden
2. Geltungsbereich
3. Komponenten
4. Komponentenabhängigkeiten
5. Laufzeitbindungen
Module in Dagger 2 sind mit @Module
annotierte Klassen, die Abhängigkeiten zu den Komponenten bereitstellen. Sie enthalten mit @Provides
oder @Binds
annotierte Methoden, um anzugeben, wie Abhängigkeiten erstellt und bereitgestellt werden. Module sind für die Organisation und Verwaltung der Erstellung von Objekten, die Ihre Anwendung benötigt, unerlässlich.
@Provides
und wird verwendet, wenn das Modul eine abstrakte Klasse ist.Beispiel eines Moduls
@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(); } }
In diesem Beispiel ist NetworkModule
eine mit @Module
annotierte Klasse. Sie enthält zwei mit @Provides
annotierte Methoden, die Instanzen von Retrofit
und OkHttpClient
erstellen und zurückgeben.
Verwenden von @Binds
Wenn Sie eine Schnittstelle und deren Implementierung haben, können Sie @Binds
verwenden, um die Implementierung an die Schnittstelle zu binden. Dies ist prägnanter als die Verwendung von @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); }
In diesem Beispiel ist ApiModule
eine abstrakte Klasse, die mit @Module
annotiert ist. Die Methode bindApiService
ist mit @Binds
annotiert, um ApiServiceImpl
an ApiService
zu binden.
Module können basierend auf der von ihnen bereitgestellten Funktionalität organisiert werden. Sie können beispielsweise separate Module für Netzwerkvorgänge, Datenbankvorgänge und UI-bezogene Abhängigkeiten haben.
Beispiel:
Retrofit
und OkHttpClient
.RoomDatabase
.ViewModel
und Presenter
.Module werden in Komponenten eingebunden, um Abhängigkeiten für die Klassen bereitzustellen, die sie benötigen. So können Sie dies einrichten:
Anwendungskomponente.java:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
In diesem Beispiel umfasst ApplicationComponent
NetworkModule
und DatabaseModule
, um Abhängigkeiten für die Anwendung bereitzustellen.
Bereiche in Dagger 2 sind Anmerkungen, die den Lebenszyklus von Abhängigkeiten definieren. Sie stellen sicher, dass eine einzelne Instanz einer Abhängigkeit erstellt und innerhalb eines angegebenen Bereichs freigegeben wird. Dies hilft bei der effizienten Verwaltung des Speichers und stellt sicher, dass Abhängigkeiten bei Bedarf wiederverwendet werden.
1. Singleton-Bereich
Definition : Der @Singleton
-Bereich stellt sicher, dass während des gesamten Lebenszyklus der Anwendung eine einzelne Instanz einer Abhängigkeit erstellt und gemeinsam genutzt wird.
Dieser Bereich wird normalerweise für Abhängigkeiten verwendet, die von der gesamten Anwendung gemeinsam genutzt werden müssen, z. B. Netzwerkclients, Datenbankinstanzen oder gemeinsame Einstellungen.
Beispiel:
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
In diesem Beispiel stellt die Annotation @Singleton
sicher, dass die von NetworkModule
und DatabaseModule
bereitgestellten Retrofit
und Database
Singletons sind und in der gesamten Anwendung gemeinsam genutzt werden.
2. Tätigkeitsbereich
Definition : @ActivityScope
(ein benutzerdefinierter Bereich) stellt sicher, dass innerhalb des Lebenszyklus einer Aktivität eine einzelne Instanz einer Abhängigkeit erstellt und freigegeben wird.
Dieser Bereich ist nützlich für Abhängigkeiten, die spezifisch für eine Aktivität sind und bei jeder Neuerstellung der Aktivität neu erstellt werden sollten, wie z. B. Präsentatoren oder Ansichtsmodelle.
Beispiel :
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
In diesem Beispiel stellt die Annotation @ActivityScope
sicher, dass die von ActivityModule
bereitgestellten Abhängigkeiten auf den Lebenszyklus der Aktivität beschränkt sind.
3. Fragmentumfang
Definition : @FragmentScope
(ein weiterer benutzerdefinierter Bereich) stellt sicher, dass innerhalb des Lebenszyklus eines Fragments eine einzelne Instanz einer Abhängigkeit erstellt und gemeinsam genutzt wird.
Anwendungsfall: Dieser Bereich ist nützlich für Abhängigkeiten, die spezifisch für ein Fragment sind und bei jeder Neuerstellung des Fragments neu erstellt werden sollten, z. B. fragmentspezifische Presenter oder Ansichtsmodelle.
Beispiel :
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); }
In diesem Beispiel stellt die Annotation @FragmentScope
sicher, dass die von FragmentModule
bereitgestellten Abhängigkeiten auf den Lebenszyklus des Fragments beschränkt sind.
Komponentenabhängigkeiten ermöglichen die Abhängigkeit einer Komponente von einer anderen Komponente und ermöglichen so die Wiederverwendung von Abhängigkeiten. Es gibt zwei Haupttypen von Komponentenabhängigkeiten:
1. Anwendungskomponente
Definition : Die Anwendungskomponente stellt Abhängigkeiten bereit, die in der gesamten Anwendung benötigt werden. Sie wird normalerweise mit @Singleton
definiert, um sicherzustellen, dass die Abhängigkeiten in der gesamten Anwendung gemeinsam genutzt werden.
Diese Komponente wird für Abhängigkeiten verwendet, die global verfügbar sein müssen, wie z. B. Netzwerkclients, Datenbankinstanzen oder gemeinsame Einstellungen.
Beispiel :
@Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
In diesem Beispiel ist die ApplicationComponent
für die Bereitstellung Retrofit
und Database
verantwortlich, die von der gesamten Anwendung gemeinsam genutzt werden.
2. Aktivitätskomponente
Definition : Die Aktivitätskomponente stellt Abhängigkeiten bereit, die innerhalb einer bestimmten Aktivität benötigt werden. Sie wird normalerweise mit einem benutzerdefinierten Bereich wie @ActivityScope
versehen, um sicherzustellen, dass die Abhängigkeiten bei jeder Neuerstellung der Aktivität neu erstellt werden.
Diese Komponente wird für Abhängigkeiten verwendet, die spezifisch für eine Aktivität sind, wie etwa Präsentatoren oder Ansichtsmodelle.
Beispiel :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
In diesem Beispiel ist die ActivityComponent
von der ApplicationComponent
abhängig und weist spezifische Abhängigkeiten zur MainActivity
auf.
Komponentenabhängigkeiten ermöglichen die Abhängigkeit einer Komponente von einer anderen Komponente und ermöglichen so die Wiederverwendung von Abhängigkeiten. Es gibt zwei Haupttypen von Komponentenabhängigkeiten:
1. Unterkomponenten:
Eine Unterkomponente ist ein untergeordnetes Element einer anderen Komponente und kann auf die Abhängigkeiten ihrer übergeordneten Komponente zugreifen. Unterkomponenten werden innerhalb der übergeordneten Komponente definiert und können deren Umfang erben.
Beispiel :
@ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); }
In diesem Beispiel ist ActivitySubcomponent
eine Unterkomponente der übergeordneten Komponente und kann auf deren Abhängigkeiten zugreifen.
2. Abhängigkeitsattribut
Dadurch kann eine Komponente von einer anderen Komponente abhängig sein, ohne eine Unterkomponente zu sein. Die abhängige Komponente kann auf die von der übergeordneten Komponente bereitgestellten Abhängigkeiten zugreifen.
Beispiel :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
In diesem Beispiel ist ActivityComponent
von ApplicationComponent
abhängig und kann auf deren Abhängigkeiten zugreifen.
Laufzeitbindungen in Dagger 2 beziehen sich auf die Bereitstellung von Abhängigkeiten, die zur Laufzeit erstellt und verwaltet werden, basierend auf dem Kontext, in dem sie benötigt werden.
1. Anwendungskontext
Definition : Der Anwendungskontext ist ein Kontext, der an den Lebenszyklus der gesamten Anwendung gebunden ist. Er wird für Abhängigkeiten verwendet, die so lange bestehen müssen wie die Anwendung selbst.
Abhängigkeiten, die von der gesamten Anwendung gemeinsam genutzt werden und nicht für jede Aktivität oder jedes Fragment neu erstellt werden müssen. Beispiele hierfür sind Netzwerkclients, Datenbankinstanzen und gemeinsame Einstellungen.
Beispiel :
@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(); } }
In diesem Beispiel stellt AppModule
den Anwendungskontext als Singleton-Abhängigkeit bereit. Die Methode provideApplicationContext
stellt sicher, dass der bereitgestellte Kontext an den Lebenszyklus der Anwendung gebunden ist.
2. Aktivitätskontext
Definition : Der Aktivitätskontext ist ein Kontext, der an den Lebenszyklus einer bestimmten Aktivität gebunden ist. Er wird für Abhängigkeiten verwendet, die so lange bestehen müssen wie die Aktivität selbst.
Abhängigkeiten, die spezifisch für eine Aktivität sind und bei jeder Neuerstellung der Aktivität neu erstellt werden sollten. Beispiele hierfür sind Ansichtsmodelle, Präsentatoren und UI-bezogene Abhängigkeiten.
Beispiel :
@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; } }
In diesem Beispiel stellt ActivityModule
den Aktivitätskontext als bereichsbezogene Abhängigkeit bereit. Die Methode provideActivityContext
stellt sicher, dass der bereitgestellte Kontext an den Lebenszyklus der Aktivität gebunden ist.
Um diese Laufzeitbindungen zu verwenden, müssen Sie die entsprechenden Module in Ihre Komponenten einbinden:
Anwendungskomponente :
@Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); }
Aktivitätskomponente :
@ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); }
Einfügen von Kontexten
Nachdem Sie Ihre Komponenten und Module eingerichtet haben, können Sie die Kontexte nach Bedarf in Ihre Klassen einfügen.
Beispiel in einer Aktivität :
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); } }
In diesem Beispiel erhält MainActivity
sowohl den Aktivitätskontext als auch den Anwendungskontext durch Abhängigkeitseinfügung. Dadurch kann die Aktivität den geeigneten Kontext basierend auf den spezifischen Anforderungen der Abhängigkeiten verwenden.
Um Dagger 2 in Ihrem Projekt zu verwenden, müssen Sie Ihrer Datei build.gradle
die folgenden Abhängigkeiten hinzufügen:
dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }
Ersetzen Sie 2.x
durch die neueste Version von Dagger 2.
Erstellen Sie ein Modul, um Abhängigkeiten bereitzustellen. Beispielsweise ein NetworkModule
, um eine Retrofit
Instanz bereitzustellen:
@Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } }
Erstellen Sie eine Komponente, um eine Brücke zwischen dem Modul und den Klassen zu schlagen, die die Abhängigkeiten benötigen:
@Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }
Verwenden Sie die Komponente, um Abhängigkeiten in Ihre Klassen einzufügen. Beispielsweise in Ihrer 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; } }
Jetzt können Sie die eingefügten Abhängigkeiten in Ihren Klassen verwenden. Beispielsweise in einer 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 // ... } }
Lassen Sie uns dieses Thema zusammenfassen: