Ich habe keine verständlichen Anleitungen für diejenigen gefunden, 
Kodein zum ersten Mal gesehen haben, und die Dokumentation ist nicht an allen Stellen transparent und konsistent. 
Kodein möchte ich die Hauptfunktionen der Bibliothek mit Ihnen teilen. Einige Bibliotheksfunktionen werden veröffentlicht, dies ist jedoch im Grunde der erweiterte Teil. Hier finden Sie alles, um normal zu beginnen und Abhängigkeiten mit 
Kodein implementieren, während Sie den Artikel lesen. Der Artikel basiert auf 
Kodein 5.3.0 , da für 
Kodein 6.0.0 die 
Support Library 28 oder 
AndroidX und keinesfalls jeder zu ihnen wechseln wird, da viele Bibliotheken von Drittanbietern noch keine kompatiblen Versionen anbieten.

Kodein ist eine Bibliothek zur Implementierung der Abhängigkeitsinjektion (DI). Wenn Sie mit diesem Konzept nicht vertraut sind, lesen Sie den Anfang 
des Dagger2-Artikels , in dem der Autor die theoretischen Aspekte von DI kurz erläutert.
In diesem Artikel werden wir alles am Beispiel von Android betrachten, aber laut den Entwicklern verhält sich Kodein auf allen von Kotlin unterstützten Plattformen (JVM, Android, JS, Native) gleich.
Installation
Aufgrund der Tatsache, dass Java über eine 
type erasure , tritt ein Problem auf - der Compiler löscht den generischen Typ. Auf Bytecode-Ebene sind 
List<String> und 
List<Date> nur 
List . Es gibt immer noch eine Möglichkeit, Informationen über generische Typen zu erhalten, aber es wird viel kosten und nur unter JVM und Android funktionieren. In diesem Zusammenhang schlagen die 
Kodein Entwickler vor, eine von zwei Abhängigkeiten zu verwenden: Eine empfängt Informationen über verallgemeinerte Typen ( 
kodein-generic ) während der Arbeit und die andere nicht ( 
kodein-erased ). Wenn Sie beispielsweise 
kodein-erased List<String> und 
List<Date > als 
List<*> speichern, und wenn Sie 
kodein-generic alles zusammen mit dem angegebenen Typ gespeichert, 
kodein-generic als 
List<String> und 
List<Date> jeweils.
Wie wähle ich?
Schreiben Sie 
nicht unter die JVM - verwenden Sie 
kodein-erased , sonst ist es unmöglich.
Schreiben Sie unter die JVM, und das Leistungsproblem ist für Sie sehr wichtig. Sie können 
kodein-erased verwenden. 
kodein-erased Sie jedoch vorsichtig, diese Erfahrung kann im schlechten Sinne dieser Wörter unerwartet sein. Wenn Sie eine reguläre Anwendung ohne besondere Leistungsanforderungen 
kodein-generic , verwenden Sie 
kodein-generic .
Wenn Sie über die Auswirkungen von DI auf die Leistung nachdenken, wird der Großteil der Abhängigkeiten meistens einmal erstellt oder die Abhängigkeiten werden für die wiederholte Wiederverwendung erstellt. Es ist unwahrscheinlich, dass Sie mit solchen Aktionen die Leistung Ihrer Anwendung erheblich beeinträchtigen können.
Installieren Sie also:
Zuerst sollte in build.gradle unter den Repositorys jcenter () sein, wenn es nicht vorhanden ist - add.
 buildscript { repositories { jcenter() } } 
Fügen Sie als Nächstes im Abhängigkeitsblock eine der oben genannten grundlegenden Abhängigkeiten hinzu:
 implementation "org.kodein.di:kodein-di-generic-jvm:$version" 
 implementation "org.kodein.di:kodein-di-erased-jvm:$version" 
Da es sich um Android handelt, wird es mehr Abhängigkeiten geben. Sie können natürlich darauf verzichten, Kodein funktioniert normal, aber warum sollten Sie zusätzliche Funktionen ablehnen, die für Android nützlich sind (ich werde am Ende des Artikels darüber sprechen)? Sie haben die Wahl, aber ich schlage vor, hinzuzufügen.
Hier gibt es auch Optionen.
Erstens verwenden Sie 
SupportLibrary implementation "org.kodein.di:kodein-di-framework-android-core:$version" 
Die zweite - Verwendung
 implementation "org.kodein.di:kodein-di-framework-android-support:$version" 
Drittens - Sie verwenden AndroidX
 implementation "org.kodein.di:kodein-di-framework-android-x:$version" 
Wir beginnen Abhängigkeiten zu erstellen
Mit 
Dagger2 bin ich es gewohnt, Abhängigkeiten beim Start der Anwendung in der Anwendungsklasse zu erstellen und zu initialisieren.
Bei Kodein geschieht dies folgendermaßen:
 class MyApp : Application() { val kodein = Kodein {  } } 
Abhängigkeitserklärungen beginnen immer mit
 bind<TYPE>() with 
Tags
Das Kodein-Abhängigkeits-Tagging ist eine ähnliche Funktion wie 
Qualifier von 
Dagger2 . In 
Dagger2 müssen Sie entweder einen separaten 
Qualifier @Named("someTag") oder 
@Named("someTag") , der tatsächlich auch 
Qualifier . Das Endergebnis ist einfach: Auf diese Weise unterscheiden Sie zwei Abhängigkeiten desselben Typs. Beispielsweise müssen Sie je nach Situation den 
ontext Anwendung oder einer bestimmten 
Activity abrufen. Daher müssen Sie Tags angeben, um Abhängigkeiten dafür zu deklarieren. 
Kodein können 
Kodein eine Abhängigkeit ohne Tag deklarieren. 
Kodein die 
Kodein Wenn Sie das Tag beim Empfang der Abhängigkeit nicht angeben, erhalten wir es, die anderen müssen markiert werden, und wenn die Abhängigkeit empfangen wird, muss das Tag angegeben werden.
 val kodein = Kodein { bind<Context>() with ... bind<Context>(tag = "main_activity") with ... bind<Context>(tag = "sale_activity") with ... } 
Der 
tag Parameter ist vom Typ 
Any , sodass Sie mehr als nur Zeichenfolgen verwenden können. Denken Sie jedoch daran, dass Klassen, die als Tags verwendet werden, die Methoden 
equals und 
hashCode implementieren müssen. Es ist immer erforderlich, ein Tag als benanntes Argument an eine Funktion zu übergeben, unabhängig davon, ob Sie eine Abhängigkeit erstellen oder empfangen.
Arten der Abhängigkeitsinjektion
Es gibt verschiedene Möglichkeiten, Abhängigkeiten in 
Kodein bereitzustellen, 
Kodein mit dem Wesentlichen - Erstellen von Singletones. Der Singleton wird im Rahmen der erstellten Instanz von 
Kodein .
Einführung in Singleton
Beginnen wir mit einem Beispiel:
 val kodein = Kodein { bind<IMyDatabase>() with singleton { RoomDb() } } 
Daher stellen wir 
IMyDatabase , hinter der eine Instanz von 
RoomDb versteckt wird. Eine Instanz von 
RoomDb wird bei der ersten Anforderung der Abhängigkeit 
Kodein erst neu 
Kodein wenn eine neue 
Kodein Instanz 
Kodein . Ein Singleton wird synchronisiert erstellt, kann jedoch bei Bedarf unsynchronisiert werden. Dies erhöht die Produktivität, aber Sie müssen die folgenden Risiken verstehen.
 val kodein = Kodein { bind<IMyDatabase>() with singleton(sync = false) { RoomDb() } } 
Wenn Sie eine Abhängigkeitsinstanz nicht beim ersten Aufruf, sondern unmittelbar nach dem Erstellen der 
Kodein Instanz erstellen müssen, verwenden Sie eine andere Funktion:
 val kodein = Kodein { bind<IMyDatabase>() with eagerSingleton { RoomDb() } } 
Ständig eine neue Instanz der Abhängigkeit erstellen
Es ist möglich, keine Singletones zu erstellen, sondern ständig, wenn auf eine Abhängigkeit zugegriffen wird, um eine neue Instanz davon zu erhalten. Hierzu wird die 
provider Funktion verwendet:
 val kodein = Kodein { bind<IMainPresenter>() with provider { QuantityPresenter() } } 
In diesem Fall wird jedes Mal, wenn wir eine 
IMainPresenter Abhängigkeit 
IMainPresenter , eine neue Instanz von 
QuantityPresenter erstellt.
Erstellen Sie ständig eine neue Instanz der Abhängigkeit und übergeben Sie die Parameter an den Konstruktor
Sie können jedes Mal eine neue Instanz davon abrufen, wenn Sie eine Abhängigkeit hinzufügen, wie im vorherigen Beispiel, aber die Parameter zum Erstellen der Abhängigkeit angeben. Parameter können maximal 
5 sein . Verwenden Sie für dieses Verhalten die 
factory Methode.
 val kodein = Kodein { bind<IColorPicker>() with factory { r: Int, g: Int, b: Int, a: Int -> RgbColorPicker(r, g, b, a) } } 
Jedes Mal, wenn wir eine zwischengespeicherte Instanz erstellen, hängt dies von den Parametern ab
Wenn Sie den vorherigen Absatz lesen, denken Sie vielleicht, dass es schön wäre, nicht jedes Mal eine neue Instanz gemäß den übergebenen Parametern zu empfangen, sondern dieselbe Instanz der Abhängigkeit von demselben Parameter zu erhalten.
 val kodein = Kodein { bind<IRandomIntGenerator>() with multiton { from: Int, to: Int -> IntRandom(from, to) } } 
Wenn wir im obigen Beispiel zum ersten Mal die Abhängigkeit mit den Parametern 
5 und 
10 erstellen wir eine neue Instanz von 
IntRandom(5, 10) . Wenn wir die Abhängigkeit erneut mit denselben Parametern aufrufen, erhalten wir die zuvor erstellte Instanz. Somit wird eine 
map von Singleton mit verzögerter Initialisierung erhalten. Die Argumente sind, wie im Fall der 
factory maximal 
5 .
Wie bei Singletones können Sie hier die Synchronisation deaktivieren.
 val kodein = Kodein { bind<IRandomIntGenerator>() with multiton(sync = false) { from: Int, to: Int -> IntRandom(from, to) } } 
Verwenden von weichen und schwachen Links in Kodein
Wenn Sie Abhängigkeiten mit 
singleton oder 
multiton bereitstellen 
multiton Sie den Referenztyp für die gespeicherte Instanz angeben. Im üblichen Fall, den wir oben betrachtet haben, wird dies die übliche 
strong Verbindung sein. Es ist jedoch möglich, 
soft und 
weak Glieder zu verwenden. Wenn Sie mit diesen Konzepten noch nicht vertraut sind, 
schauen Sie hier .
Daher werden Ihre Singletones möglicherweise als Teil des Anwendungslebenszyklus neu erstellt oder nicht.
 val kodein = Kodein { bind<IMyMap>() with singleton(ref = softReference) { WorldMap() } bind<IClient>() with singleton(ref = weakReference) { id -> clientFromDB(id) } } 
Separater Singleton für jeden Stream
Dies ist derselbe Singleton, aber für jeden Thread, der eine Abhängigkeit anfordert, wird ein Singleton erstellt. Verwenden Sie dazu den bekannten Parameter 
ref .
 val kodein = Kodein { bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) } } 
Konstanten als einbettbare Abhängigkeiten
Sie können Konstanten als Abhängigkeiten angeben. In der Dokumentation wird darauf 
Kodein , dass Sie mit 
Kodein Konstanten einfacher Typen ohne Vererbung oder Schnittstellen bereitstellen müssen, z. B. Grundelemente oder Datenklassen.
 val kodein = Kodein { constant(tag = "maxThread") with 8 constant(tag = "serverURL") with "https://my.server.url" 
Erstellen Sie Abhängigkeiten, ohne den Typ zu ändern
Beispielsweise möchten Sie die Abhängigkeit als Singleton bereitstellen, sie jedoch nicht hinter der Schnittstelle ausblenden. Sie können den Typ einfach nicht angeben, wenn Sie 
bind aufrufen und 
from anstelle von 
with .
 val kodein = Kodein { bind() from singleton { Gson() } 
Die Abhängigkeit im obigen Beispiel hat den Rückgabetyp der Funktion, 
Gson es wird eine 
Gson vom 
Gson Typ 
Gson .
Erstellen Sie Unterklassenabhängigkeiten einer Oberklasse oder Schnittstelle
Kodein können 
Kodein Abhängigkeiten für die Nachkommen einer bestimmten Klasse oder von Klassen, die eine einzelne Schnittstelle implementieren, auf unterschiedliche Weise bereitstellen.
 val kodein = Kodein { bind<Animal>().subTypes() with { animalType -> when (animalType.jvmType) { Dog::class.java -> eagerSingleton { Dog() } else -> provider { WildAnimal(animalType) } } } 
Die 
Animal Klasse kann entweder eine Oberklasse oder eine Schnittstelle sein. Unter Verwendung von 
.subtypes wir 
animalType mit dem 
TypeToken<*> , von dem wir bereits die Java-Klasse 
TypeToken<*> und abhängig davon die Abhängigkeit auf unterschiedliche Weise bereitstellen können. Diese Funktion kann nützlich sein, wenn Sie 
TypeToken oder seine Ableitungen als Konstruktorparameter für eine Reihe von Fällen verwenden. Auf diese Weise können Sie auch unnötigen Code mit derselben Abhängigkeitserstellung für verschiedene Typen vermeiden.
Erstellen Sie Abhängigkeiten, die andere Abhängigkeiten als Parameter benötigen
Meistens erstellen wir nicht nur eine Klasse ohne Parameter als Abhängigkeit, sondern eine Klasse, an die wir Parameter an den Konstruktor übergeben müssen.
 class ProductGateway(private val api: IProductApi, private val dispatchers: IDispatchersContainer) : IProductGateway 
Um eine Klasse mit Abhängigkeiten zu erstellen, die zuvor in 
Kodein aus, einen Funktionsaufruf instance () als Parameter zu übergeben. In diesem Fall ist die Reihenfolge der Erstellung nicht wichtig.
 bind<IDispatchersContainer>() with singleton { DispatchersContainer() } bind<IProductGateway>() with singleton { ProductGateway(instance(), instance()) } bind<IProductApi>() with singleton { ProductApi() } 
Anstelle von 
instance() es zu Aufrufen von 
provider() oder 
factory() . Wir werden uns diese Methoden im Abschnitt zum Abrufen und Implementieren von Abhängigkeiten genauer ansehen.
Erstellen Sie eine Abhängigkeit, indem Sie die zuvor erstellte Abhängigkeitsmethode aufrufen
Es klingt nicht sehr gut, aber Sie können die 
instance<TYPE> aufrufen, um eine Klasse 
instance<TYPE> , die wir bereits irgendwo bereitstellen, und die Methode dieser Klasse aufrufen, um eine neue Abhängigkeit zu erhalten.
 bind<DataSource>() with singleton { MySQLDataSource() } bind<Connection>() with provider { instance<DataSource>().openConnection() } 
Module
Mit 
Dagger2 bin ich es 
Dagger2 , 
Dagger2 . In 
Kodein sieht auf den ersten Blick nicht alles sehr gut aus. Sie müssen viele Abhängigkeiten direkt in der 
Application erstellen, und ich persönlich mag es nicht wirklich. Es gibt jedoch eine Lösung: Mit 
Kodein können Sie auch Module erstellen und diese bei Bedarf an den entsprechenden Stellen verbinden.
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } bind<HttpClient>() with singleton { provideHttpClient() } } val kodein: Kodein = Kodein { import(appModule) bind<ISchedulersContainer>() with singleton { SchedulersContainer() }  
Aber seien Sie vorsichtig, Module sind nur Container, die Methoden zum Abrufen von Abhängigkeiten deklarieren. Sie selbst erstellen keine Klassen. Wenn Sie daher den Empfang der Abhängigkeit als Singleton im Modul deklarieren und dieses Modul dann in zwei verschiedene Instanzen von 
Kodein importieren, erhalten Sie zwei verschiedene Singuletts, eines pro Instanz von 
Kodein .
Außerdem muss der Name jedes Moduls eindeutig sein. Wenn Sie jedoch ein Modul aus einem anderen Projekt importieren müssen, ist es schwierig, die Eindeutigkeit des Namens zu gewährleisten. Dazu können Sie das Modul umbenennen oder seinem Namen ein Präfix hinzufügen.
 import(apiModule.copy(name = "firstAPI")) import(secondApiModule.copy(prefix = "secondAPI-")) 
Ich bin es gewohnt zu arbeiten, wenn die Module voneinander abhängig sind und eine Art Hierarchie bilden. 
Kodein können jedes Modul einmal in 
Kodein importieren. Wenn Sie also versuchen, zwei Module mit denselben abhängigen Modulen in ein 
Kodein zu importieren, stürzt die Anwendung ab. Die Lösung ist einfach: Sie müssen zum 
importOnce(someModule) Aufruf 
importOnce(someModule) , mit dem überprüft wird, ob das gleichnamige Modul zuvor importiert wurde, und bei Bedarf importiert werden.
In solchen Fällen stürzt die Anwendung beispielsweise ab:
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { import(appModule) } val thirdModule = Kodein.Module("third") { import(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) } 
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { importOnce(appModule) } val thirdModule = Kodein.Module("third") { import(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) } 
Wenn der 
importOnce Aufruf jedoch bei einem zweiten Verbindungsversuch ausgeführt wird, funktioniert alles. Seien Sie aufmerksam.
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { import(appModule) } val thirdModule = Kodein.Module("third") { importOnce(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) } 
Vererbung
Wenn Sie dasselbe Modul zweimal verwenden, werden unterschiedliche Abhängigkeiten erstellt. Wie steht es jedoch mit der Vererbung und dem Implementierungsverhalten, das den 
Subcomponents in 
Dagger2 ? Alles ist einfach, Sie müssen nur von der 
Kodein Instanz erben und erhalten Zugriff auf alle Abhängigkeiten des Elternteils im Erben.
 val kodein: Kodein = Kodein { bind<ISchedulersContainer>() with singleton { SchedulersContainer() }  
Neudefinition
Standardmäßig können Sie die Abhängigkeit nicht überschreiben, da Benutzer sonst verrückt werden und nach Gründen suchen, warum die Anwendung nicht ordnungsgemäß funktioniert. Dies ist jedoch mit einem zusätzlichen Parameter der 
bind . Diese Funktionalität ist beispielsweise zum Organisieren von Tests hilfreich.
 val kodein = Kodein { bind<Api>() with singleton { ApiImpl() }  bind<Api>(overrides = true) with singleton { OtherApiImpl() } } 
Standardmäßig können Module und ihre Abhängigkeiten bereits im 
Kodein Objekt deklarierte Abhängigkeiten nicht überschreiben. Beim Importieren eines Moduls können Sie jedoch angeben, dass vorhandene Abhängigkeiten seine Abhängigkeiten überschreiben können, und in diesem Modul können Sie bereits Abhängigkeiten angeben, die andere überschreiben können.
Es klingt nicht sehr klar, verwenden wir Beispiele. In diesen Fällen stürzt die Anwendung ab:
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule) } 
  val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule, allowOverride = true) } 
Dabei überschreibt die Modulabhängigkeit die im 
Kodein Objekt deklarierte Abhängigkeit.
  val appModule = Kodein.Module("app") { bind<Gson>(overrides = true) with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule, allowOverride = true) } 
Wenn Sie jedoch wirklich möchten und verstehen, was Sie tun, können Sie ein Modul erstellen, das bei identischen Abhängigkeiten mit dem 
Kodein Objekt diese neu definiert und die Anwendung nicht abstürzt. Wir verwenden den Parameter 
allowSilentOverride für das Modul.
 val testModule = Kodein.Module(name = "test", allowSilentOverride = true) { bind<EmailClient>() with singleton { MockEmailClient() } } 
In der Dokumentation werden komplexere Situationen mit Vererbung und Neudefinition von Abhängigkeiten sowie mit dem Kopieren von Abhängigkeiten bei Erben erörtert. Diese Situationen werden hier jedoch nicht berücksichtigt.
Abhängigkeiten abrufen und einfügen
Schließlich haben wir herausgefunden, wie man Abhängigkeiten auf viele Arten deklariert. Es ist Zeit herauszufinden, wie man sie in ihre Klassen bringt.
Kodein Entwickler teilen zwei Möglichkeiten, um Abhängigkeiten zu erhalten - 
injection und 
retieval . Kurz gesagt, 
injection ist, wenn die Klasse alle Abhängigkeiten empfängt, wenn sie erstellt wird, dh im Konstruktor, und 
retrieval ist, wenn die Klasse selbst dafür verantwortlich ist, ihre Abhängigkeiten zu erhalten.
Wenn Sie 
injection weiß Ihre Klasse nichts über 
Kodein und der Code in der Klasse ist sauberer. Wenn Sie jedoch 
retrieval , haben Sie die Möglichkeit, Abhängigkeiten flexibler zu verwalten. Beim 
retrieval alle Abhängigkeiten nur zum Zeitpunkt der ersten Berufung auf die Abhängigkeit träge erhalten.
Kodein Methoden zur Verwendung von Abhängigkeiten
Eine Instanz der 
Kodein Klasse verfügt über drei Methoden, die eine Abhängigkeit, eine Abhängigkeitsfactory oder einen Abhängigkeitsanbieter zurückgeben - 
instance() , 
factory() bzw. 
provider() . Wenn Sie also eine Abhängigkeit über eine 
factory oder einen 
provider bereitstellen, können Sie nicht nur das Ergebnis der Funktionsausführung, sondern auch die Funktion selbst empfangen. Denken Sie daran, dass Sie Tags in allen Variationen verwenden können.
  val kodein: Kodein = Kodein { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } private val number: BigDecimal by instance(arg = "23.87") private val numberFactory: (value: String) -> BigDecimal by factory() private val random: Random by instance() private val randomProvider: () -> Random by provider() 
Abhängigkeitsinjektion durch Konstruktor
Wie Sie bereits verstanden haben, geht es um 
injection . Zum Implementieren müssen Sie zuerst alle Abhängigkeiten der Klasse in ihren Konstruktor übernehmen und dann eine Instanz der Klasse erstellen, indem Sie 
kodein.newInstance aufrufen
 class ProductApi(private val client: HttpClient, private val gson: Gson) : IProductApi class Application : Application() { val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } bind<HttpClient>() with singleton { provideHttpClient() } } private val productApi: IProductApi by kodein.newInstance { ProductApi(instance(), instance()) } } 
Abhängigkeitsinjektion in nullbaren Eigenschaften
Möglicherweise wissen Sie nicht, ob eine Abhängigkeit deklariert wurde. Wenn die Abhängigkeit in der 
Kodein Instanz nicht deklariert ist, führt der Code aus dem obigen Beispiel zu einer 
Kodein.NotFoundException . Wenn Sie als Ergebnis eine 
null möchten und keine Abhängigkeit besteht, gibt es hierfür drei Hilfsfunktionen: 
instanceOrNull() , 
factoryOrNull() und 
providerOrNull() .
 class ProductApi(private val client: HttpClient?, private val gson: Gson) : IProductApi class Application : Application() { val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } } private val productApi: IProductApi by kodein.newInstance { ProductApi(instanceOrNull(), instance()) } } 
Holen Sie sich Abhängigkeiten innerhalb der Klasse.
Wie bereits erwähnt, ist in dem Fall, in dem der 
retrieval , die Initialisierung aller Abhängigkeiten standardmäßig verzögert. Auf diese Weise können Sie Abhängigkeiten nur dann abrufen, wenn sie benötigt werden, und Abhängigkeiten in den vom System erstellten Klassen abrufen.
Activity , 
Fragment und andere Klassen mit ihrem eigenen Lebenszyklus, es geht nur um sie.
Um Abhängigkeiten in 
Activity zu implementieren 
Activity benötigen wir nur einen Link zu einer Instanz von Kodein, wonach wir bereits bekannte Methoden verwenden können. Tatsächlich haben Sie oben bereits Beispiele für den 
retrieval . Sie müssen lediglich eine Eigenschaft deklarieren und an eine der folgenden Funktionen delegieren: 
instance() , 
factory() oder 
provider() private val number: BigDecimal by kodein.instance(arg = "23.87") private val numberFactory: (value: String) -> BigDecimal by kodein.factory() private val random: Random? by kodein.instanceOrNull() private val randomProvider: (() -> Random)? by kodein.providerOrNull() 
Parameter an Fabriken übergeben
Sie haben bereits gesehen, dass es ausreicht, den Parameter 
arg der 
arg zu verwenden, um einen Parameter an die Factory zu übergeben.
Aber was ist, wenn es mehrere Parameter gibt (ich habe bereits gesagt, dass es in einer Fabrik bis zu 5 Parameter geben kann )? Sie müssen nur eine argKlasse an den Parameter übergeben M, der Konstruktoren überladen hat und 2 bis 5 Argumente annehmen kann. val kodein = Kodein { bind<IColorPicker>() with factory { r: Int, g: Int, b: Int, a: Int -> RgbColorPicker(r, g, b, a) } } val picker: IColorPicker by kodein.instance(arg = M(255, 211, 175, 215)) 
Abhängigkeitsinitialisierung erzwingen
Wie bereits erwähnt, ist die Initialisierung standardmäßig verzögert. Sie können jedoch einen Trigger erstellen, ihn an eine Eigenschaft, mehrere Eigenschaften oder eine gesamte Instanz binden Kodein, nachdem Sie diesen Trigger gedrückt haben, und die Abhängigkeiten werden initialisiert. val myTrigger = KodeinTrigger() val gson: Gson by kodein.on(trigger = myTrigger).instance()  myTrigger.trigger()  
 val myTrigger = KodeinTrigger() val kodeinWithTrigger = kodein.on(trigger = myTrigger) val gson: Gson by kodeinWithTrigger.instance()  myTrigger.trigger()  
Lazy Kodein Instanzerstellung
Vorher haben wir ständig explizit eine Instanz erstellt Kodein, aber es ist möglich, die Initialisierung dieser Eigenschaft mithilfe einer Klasse zu verschieben LazyKodein, die eine Funktion im Konstruktor übernimmt, die ein Objekt zurückgeben soll Kodein.Dieser Ansatz kann nützlich sein, wenn beispielsweise nicht bekannt ist, ob Abhängigkeiten von einer bestimmten Kodein-Instanz überhaupt benötigt werden. val kodein: Kodein = LazyKodein { Kodein { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } } private val number: BigDecimal by kodein.instance(arg = "13.4")  number.toPlainString()  
Ein Aufruf von Kodein.lazy führt zu einem ähnlichen Ergebnis.  val kodein: Kodein = Kodein.lazy { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } private val number: BigDecimal by kodein.instance(arg = "13.4")  number.toPlainString()  
Kodein verzögerte Initialisierung
Für die verzögerte Initialisierung ist Kodeinein Objekt vorhanden LateInitKodein. Sie können dieses Objekt erstellen, die Erstellung von Eigenschaften an es delegieren und nach der Initialisierung des Objekts selbst die Eigenschaft darauf festlegen. baseKodeinDanach können Sie bereits auf die Abhängigkeiten zugreifen. val kodein = LateInitKodein() val gson: Gson by kodein.instance()  kodein.baseKodein =   gson.fromJson(someStr) 
Ruft alle Instanzen des angegebenen Typs ab
Sie können Kodein nach einer Instanz des angegebenen Typs und allen seinen Nachkommen im Formular fragen List. Alles ist nur innerhalb des angegebenen Tags. Um dies zu tun, gibt es Methoden allInstances, allProviders, allFactories.  val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } val numbers: List<Number> by kodein.allInstances() 
Wenn Sie in das Protokoll drucken, sehen Sie dort [32767, 136.88, 4562, 12.46]. Die Abhängigkeit mit dem Tag ist nicht in der Liste enthalten.Vereinfachen Sie die Erfassung von Abhängigkeiten mithilfe der KodeinAware-Schnittstelle
Diese Schnittstelle verpflichtet Sie, die type-Eigenschaft zu überschreiben Kodein, und bietet im Gegenzug Zugriff auf alle Funktionen, die der Instanz zur Verfügung stehen Kodein. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } val numbers: List<Number> by allInstances() } 
Wie Sie sehen, können Sie jetzt einfach schreiben by allInstances()anstatt by kodein.allInstances()zuvor. Wir haben bereits über den Auslöser für den Empfang von Abhängigkeiten gesprochen. In der Schnittstelle können KodeinAwareSie einen Trigger überschreiben und alle deklarierten Abhängigkeiten abrufen, wenn dieser Trigger aufgerufen wird. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } override val kodeinTrigger = KodeinTrigger() val numbers: List<Number> by allInstances() override fun onCreate() { super.onCreate() kodeinTrigger.trigger() } } 
Da der Zugriff auf die Abhängigkeiten und die Instanz Kodeinverzögert ist, können Sie die Instanzinitialisierung an die Kodeinin Kotlin integrierte Funktion delegieren lazy. Ein solcher Ansatz kann in Klassen abhängig von ihrem Kontext nützlich sein, z Activity. class CategoriesActivity : Activity(), KodeinAware { override val kodein: Kodein by lazy { (application as MyApplication).kodein } private val myFloat: Float by instance() 
Aus den gleichen Gründen können Sie einen Modifikator verwenden lateinit. class CategoriesActivity : Activity(), KodeinAware { override lateinit var kodein: Kodein private val myFloat: Float by instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) kodein = (application as MyApplication).kodein } 
Zugriff auf Abhängigkeiten ohne Delegierung von Eigenschaften
Wenn Sie aus irgendeinem Grund keine Eigenschaftsdelegierung verwenden möchten, können Sie den direkten Zugriff über DKodein(von direkt) verwenden. Der Hauptunterschied besteht darin, dass es keine verzögerte Initialisierung mehr gibt, die Abhängigkeit sofort zum Zeitpunkt des Aufrufs erhalten instancewird providerund ähnliche Funktionen. Sie DKodeinkönnen es von einer vorhandenen Kodein-Instanz herunterladen oder von Grund auf neu erstellen. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<BigDecimal>() with singleton { BigDecimal.TEN } } val directKodein: DKodein = kodein.direct val directKodein2: DKodein = Kodein.direct { bind<BigDecimal>() with singleton { BigDecimal.ONE } } val someNumber:BigDecimal = directKodein.instance() val someNumber2:BigDecimal = directKodein2.instance() 
Kodein kann im Framework verwendet werden KodeinAware, und DKodeinim Framework DKodeinAwarekönnen Sie experimentieren.Holen Sie sich Abhängigkeiten in jedem Kontext
Um Kodeinmehrere Abhängigkeiten desselben Typs von einem Objekt zu erhalten, haben wir bereits die Möglichkeit untersucht, Tags und Fabriken mit Argumenten zu verwenden. Es gibt jedoch noch eine weitere Möglichkeit: die Verwendung eines Kontexts (und dies ist nicht der Kontext in Android).Unterschiede zur Abhängigkeit vom Tag:- Ein Tag kann nicht in einer Funktion verwendet werden, in der wir eine Abhängigkeit erstellen
- Bei Verwendung von Kontext haben wir Zugriff auf die Kontextinstanz in der Funktion zur Erstellung von Abhängigkeiten
Anstelle des Kontexts können Sie häufig eine Factory mit einem Argument verwenden. EntwicklerKodeinempfehlen dies, wenn Sie sich nicht sicher sind, was Sie verwenden sollen. Der Kontext kann jedoch nützlich sein, wenn Sie beispielsweise nicht zwei Argumente für denselben Typ umwandeln können.Zum Beispiel haben Sie Activityund Presenter, wie Sie wollen, ein einzelnes Objekt mit Kodein, mehrere verschiedene Arten von Abhängigkeiten auf verschiedene Weise zur Verfügung zu stellen, in Abhängigkeit von der Klasse , in der sie erhalten werden. Zu führen Activityund Presenterzu einer Art - Sie benötigen eine optionale Schnittstelle, und die Fabrik müssen die Art des resultierenden Arguments überprüfen. Das Schema ist nicht sehr bequem. Daher schauen wir uns an, wie der Kontext verwendet wird: class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<BigDecimal>() with contexted<CategoriesActivity>().provider { context.getActivityBigDecimal() } bind<BigDecimal>() with contexted<CategoriesPresenter>().factory { initialValue:BigDecimal -> context.getPresenterBigDecimal(initialValue) } } } class CategoriesActivity : Activity(), AppKodeinAware { fun getActivityBigDecimal() = BigDecimal("16.34") private val activityBigDecimal: BigDecimal by kodein.on(context = this).instance() } class CategoriesPresenter : AppKodeinAware { fun getPresenterBigDecimal(initialValue: BigDecimal) = initialValue * BigDecimal.TEN private val presenterBigDecimal: BigDecimal by kodein.on(context = this).instance(arg = BigDecimal("31.74")) } 
Ein Beispiel wird natürlich über die Ohren gezogen, und in der Praxis ist es unwahrscheinlich, dass Sie auf eine solche Situation stoßen, aber dieses Beispiel zeigt, wie der Kontext funktioniert.Um eine Abhängigkeit zu deklarieren, geben Sie nicht an with provider(), sondern with contexted<OurContextClass>().providerwo OurContextClasssich der Klassentyp befindet, dessen Instanz als Kontext fungiert. contextedkann nur Anbieter oder Fabrik sein.Der Zugriff auf diesen Kontext in der Funktion, die die Abhängigkeit zurückgibt, erfolgt über eine Variable mit dem Namen context.Um eine Abhängigkeit an einen Kontext anzuhängen, müssen Sie zuerst den Kontext für das Objekt Kodeinüber die Funktion angeben on()und dann die Abhängigkeit anfordern.In ähnlicher Weise wird der Kontext im Fall von verwendet injection. private val productApi: IProductApi by kodein.on(context = someContext).newInstance { ProductApi(instance(), instance()) } } 
Android-Erweiterungen
Zu Beginn des Artikels habe ich versprochen, Erweiterungsoptionen für in Betracht zu ziehen Android.Nichts hindert Sie daran, Kodeines wie oben beschrieben zu verwenden, aber Sie können alles um eine Größenordnung bequemer gestalten.Eingebautes Kodein für Android
Eine sehr nützliche Sache ist ein Modul, das für Android vorbereitet ist. Um eine Verbindung herzustellen, muss die Klasse die Eigenschaft träge Applicationimplementieren KodeinAwareund initialisieren Kodein(um auf die Instanz zugreifen zu können Application). Im Gegenzug erhalten Sie eine große Anzahl deklarierter Abhängigkeiten, die Sie von der Klasse erhalten können Application, einschließlich allem, was Sie benötigen Context. So verbinden Sie sich - sehen Sie sich ein Beispiel an. class MyApplication : Application(), KodeinAware { override val kodein = Kodein.lazy { import(androidModule(this@MyApplication))  
Wie Sie sehen können - können Sie zum Beispiel bekommen LayoutInflater. Eine vollständige Liste der im Modul deklarierten Abhängigkeiten finden Sie hier .Wenn Sie diese Abhängigkeiten außerhalb von Android-Klassen erhalten möchten, deren Kontext bekannt ist, geben Sie den Kontext explizit an. val inflater: LayoutInflater by kodein.on(context = getActivity()).instance() 
Holen Sie sich schnell Eltern Kodein durch nächstgelegene Kodein ()
Es ist einfach, in Android hängen einige Objekte von anderen ab. Auf der obersten Ebene gibt es Anwendung, unter der Aktivität und dann Fragment steht. Sie können die Aktivität implementieren KodeinAwaresowie Initialisierungsaufruf closestKodein()und somit eine Kopie erhalten Kodeinvon Application. class MyActivity : Activity(), KodeinAware { override val kodein by closestKodein() val ds: DataSource by instance() } 
closestKodeinSie können es auch außerhalb von Android-Klassen herunterladen, benötigen jedoch einen Android-Kontext, über den Sie die Funktion aufrufen können. Wenn Sie es verwenden KodeinAware, geben Sie auch den Kontext dafür an (überschreiben Sie die entsprechende Eigenschaft und übergeben Sie den Android-Kontext an die Funktion kcontext()). class MyController(androidContext: Context) : KodeinAware { override val kodein by androidContext.closestKodein() override val kodeinContext = kcontext(androidContext) val inflater: LayoutInflater by instance() } 
Erstellen Sie ein separates Kodein in Aktivität
Es kann durchaus notwendig sein, in der Aktivität vom übergeordneten Kodein zu erben und es zu erweitern. Die Lösung ist ganz einfach. class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by Kodein.lazy { extend(parentKodein)  } } 
Kodein, das eine Konfigurationsänderung durchläuft
Ja, das kannst du. Hierfür gibt es eine Funktion retainedKodein. Bei Verwendung wird das Objekt Kodeinnach einer Konfigurationsänderung nicht neu erstellt. class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by retainedKodein { extend(parentKodein) } } 
Was steht im Artikel nicht?
Ich gab nicht vor, vollständig zu sein, und ich selbst verstehe einige Dinge nicht gut genug, um zu versuchen, sie zu formulieren. Hier ist eine Liste dessen, was Sie selbst lernen können, wenn Sie die Grundprinzipien kennen:- Geltungsbereich
- Instanzbindung
- Mehrfachbindung
- Bereits Rückrufe
- Externe Quelle
- Fallstricke der gelöschten Version
- Konfigurierbares Kodein
- JSR-330-Kompatibilität
Gut und Links zur Dokumentation:Vielen Dank fürs Lesen, ich hoffe, der Artikel wird Ihnen nützlich sein!