コデイン。 基本

Kodein



が初めて目にする人向けのわかりやすいガイドは見つかりませんでした。また、ドキュメントはすべての場所で透過的で一貫性がないため、ライブラリの主な機能を共有したいと思います。 一部のライブラリ機能がリリースされますが、これは基本的に高度な部分です。 ここにすべてがありますので、記事を読んで、通常通りに起動して、 Kodein



依存関係の実装を開始できます。 Kodein 6.0.0



Support Library 28



またはAndroidX



を必要とし、多くのサードパーティライブラリがまだ互換性のあるバージョンを提供していないため、誰も決してそれらに切り替えないため、この記事はKodein 5.3.0



に基づいています。





Kodein



は、依存性注入(DI)を実装するためのライブラリです。 この概念に慣れていない場合は、 Dagger2に関する記事の冒頭を読んでください。著者がDIの理論的な側面を簡単に説明しています。



この記事では、Androidの例を使用してすべてを検討しますが、開発者によると、KodeinはKotlinがサポートするすべてのプラットフォーム(JVM、Android、JS、Native)で同じように動作します。



設置



Javaにはtype erasure



があるという事実により、問題が発生します-コンパイラーはジェネリック型を消去します。 バイトコードレベルでは、 List<String>



およびList<Date>



は単なるList



です。 それでも、ジェネリック型についての情報を取得する方法は残っていますが、コストがかかり、JVMとAndroidでのみ機能します。 この点に関して、 Kodein



開発者は2つの依存関係のいずれかを使用することをお勧めします。1つは作業中にジェネリック型に関する情報を受け取り( kodein-generic



)、もう1つは受け取らない( kodein-erased



) たとえば、 kodein-erased



List<String>



およびList<Date



>を使用する場合はList<*>



として保存され、 kodein-generic



を使用する場合はすべてが指定されたタイプとともに、つまりList<String>



およびList<Date>



として保存されますList<Date>



それぞれ。



選択方法



JVMの下ではなく書き込みkodein-erased



使用します。そうでない場合は不可能です。

JVMの下で書くとパフォーマンスの問題はあなたにとって非常に重要ですkodein-erased



使用できますが、注意してください、この経験はこれらの言葉の悪い意味で予期しないかもしれません。 特別なパフォーマンス要件なしで通常のアプリケーションを作成する場合は、 kodein-generic



使用します。



最終的に、パフォーマンスへのDIの影響について考える場合、ほとんどの場合、依存関係の大部分は一度作成されるか、依存関係は繰り返し再利用するために作成されますが、そのようなアクションを使用すると、アプリケーションのパフォーマンスに大きな影響を与える可能性は低くなります。



したがって、インストールします:



まず-リポジトリ内のbuild.gradleにjcenter()があるはずです(存在しない場合)。



 buildscript { repositories { jcenter() } }
      
      





次に、依存関係ブロックで、上記の基本的な依存関係のいずれかを追加します。



 implementation "org.kodein.di:kodein-di-generic-jvm:$version"
      
      





 implementation "org.kodein.di:kodein-di-erased-jvm:$version"
      
      





Androidについて説明しているため、依存関係が増えます。 もちろん、それなしでも実行できます。Kodeinは正常に機能しますが、Androidに役立つ追加機能を拒否する理由は何ですか(記事の最後で説明します)。 選択はあなた次第ですが、追加することを提案します。



ここにもオプションがあります。



まず、 SupportLibrary



を使用していません



 implementation "org.kodein.di:kodein-di-framework-android-core:$version"
      
      





2番目-使用



 implementation "org.kodein.di:kodein-di-framework-android-support:$version"
      
      





第三-あなたはAndroidXを使用しています



 implementation "org.kodein.di:kodein-di-framework-android-x:$version"
      
      





依存関係の作成を開始します



Dagger2



を使用して、アプリケーションの起動時に、Applicationクラスで依存関係を作成および初期化することに慣れています。



Kodeinでは、これは次のように行われます。



 class MyApp : Application() { val kodein = Kodein { /*  */ } }
      
      





依存関係宣言は常に次で始まります



 bind<TYPE>() with
      
      





タグ



Kodein依存関係タグ付けは、機能がDagger2



Qualifier



似た機能Dagger2



Dagger2



、個別のQualifier



を実行するか、実際にはQualifier



でもある@Named("someTag")



使用する必要があります。 一番下の行は単純です-このようにして、同じタイプの2つの依存関係を区別します。 たとえば、状況に応じてアプリケーションまたは特定のActivity



ontext



を取得する必要があるため、依存関係を宣言するときにこのタグを指定する必要があります。 Kodein



使用Kodein



と、タグなしで1つの依存関係を宣言できKodein



。これはベース依存関係であり、依存関係の受信時にタグを指定しない場合は取得します。他のタグはタグ付けする必要があり、タグは依存関係の受信時に指定する必要があります。



 val kodein = Kodein { bind<Context>() with ... bind<Context>(tag = "main_activity") with ... bind<Context>(tag = "sale_activity") with ... }
      
      





tag



パラメータの型はAny



であるため、単なる文字列以上のものを使用できます。 ただし、タグとして使用されるクラスは、 equals



hashCode



hashCode



メソッドを実装する必要equals



hashCode



。 依存関係を作成するか受信するかにかかわらず、タグを名前付き引数として関数に渡す必要が常にあります。



依存性注入のタイプ



Kodein



に依存関係を提供する方法はいくつかあります。基本的なことKodein



ます- Kodein



を作成します。 シングルトンは、作成されたKodein



インスタンスのフレームワーク内にKodein



ます。



シングルトンの紹介



例から始めましょう:



 val kodein = Kodein { bind<IMyDatabase>() with singleton { RoomDb() } }
      
      





したがって、 IMyDatabase



を提供(提供)し、その背後にRoomDb



インスタンスが隠されます。 RoomDb



のインスタンスは、依存関係の最初のリクエストでKodein



れ、新しいKodein



インスタンスがKodein



れるまでKodein



されません。 シングルトンは同期化されて作成されますが、必要に応じて非同期化することもできます。 これにより生産性が向上しますが、それに伴うリスクを理解する必要があります。



 val kodein = Kodein { bind<IMyDatabase>() with singleton(sync = false) { RoomDb() } }
      
      





依存関係のインスタンスを最初の呼び出しではなく、 Kodein



インスタンスの作成直後に作成するKodein



場合は、別の関数を使用します。



 val kodein = Kodein { bind<IMyDatabase>() with eagerSingleton { RoomDb() } }
      
      





常に依存関係の新しいインスタンスを作成する



シングルトーンではなく、依存関係にアクセスしてその新しいインスタンスを取得するときに常に作成することができます。 これには、 provider



関数が使用されます。



 val kodein = Kodein { bind<IMainPresenter>() with provider { QuantityPresenter() } }
      
      





この場合、 IMainPresenter



依存関係を要求するIMainPresenter



QuantityPresenter



新しいインスタンスが作成されます。



常に依存関係の新しいインスタンスを作成し、パラメーターを依存関係のコンストラクターに渡します



前の例のように、依存関係のリクエストごとに新しいインスタンスを取得できますが、同時に依存関係を作成するためのパラメーターを指定します。 パラメーターは最大5にすることができます。 この動作には、 factory



メソッドを使用します。



 val kodein = Kodein { bind<IColorPicker>() with factory { r: Int, g: Int, b: Int, a: Int -> RgbColorPicker(r, g, b, a) } }
      
      





パラメータに応じてキャッシュされたインスタンスを作成するたびに



前の段落を読んで、渡されるパラメーターに従って毎回新しいインスタンスを受け取るのではなく、同じパラメーターの依存関係の同じインスタンスを受け取るのが良いと思うかもしれません。



 val kodein = Kodein { bind<IRandomIntGenerator>() with multiton { from: Int, to: Int -> IntRandom(from, to) } }
      
      





上記の例では、最初にパラメーター5



10



依存関係を取得するときに、 IntRandom(5, 10)



新しいインスタンスを作成します。同じパラメーターで依存関係を再度呼び出すと、以前に作成したインスタンスを取得します。 したがって、遅延初期化を使用したシングルトーンからのmap



が判明します。 factory



の場合のように、引数factory



最大5です。



シングルトーンと同様に、ここで同期をオフにできます。



 val kodein = Kodein { bind<IRandomIntGenerator>() with multiton(sync = false) { from: Int, to: Int -> IntRandom(from, to) } }
      
      





Kodeinでソフトリンクと弱いリンクを使用する



singleton



またはmultiton



を使用して依存関係を提供する場合、格納されたインスタンスへの参照のタイプを指定できます。 上記で検討した通常の場合-これは通常のstrong



リンクになります。 ただし、 soft



リンクとweak



リンクを使用することは可能です。 これらの概念に慣れていない場合は、 こちらチェックしてください



したがって、アプリケーションライフサイクルの一部としてシングルトーンが再作成される場合とそうでない場合があります。



 val kodein = Kodein { bind<IMyMap>() with singleton(ref = softReference) { WorldMap() } bind<IClient>() with singleton(ref = weakReference) { id -> clientFromDB(id) } }
      
      





ストリームごとに個別のシングルトン



これは同じシングルトンですが、依存関係を要求する各スレッドに対して、シングルトンが作成されます。 これを行うには、使い慣れたパラメーターref



使用します。



 val kodein = Kodein { bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) } }
      
      





埋め込み可能な依存関係としての定数



定数を依存関係として提供できます。 このドキュメントでは、 Kodein



を使用すると、プリミティブやデータクラスなど、継承やインターフェイスのない単純型の定数をKodein



必要があるという事実に注目しています。



 val kodein = Kodein { constant(tag = "maxThread") with 8 constant(tag = "serverURL") with "https://my.server.url"
      
      





タイプを変更せずに依存関係を作成する



たとえば、依存関係をシングルトンとして提供したいが、インターフェイスの背後に隠さないでください。 bind



を呼び出してwith



代わりにfrom



を使用bind



場合、単にタイプを指定することはできません。



 val kodein = Kodein { bind() from singleton { Gson() }
      
      





上記の例の依存関係には、関数の戻り値の型があります。つまり、 Gson



型の依存関係がGson



ます。



スーパークラスまたはインターフェースのサブタイプごとに依存関係を作成します



Kodein



使用Kodein



と、単一のインターフェースを実装する特定のクラス(複数Kodein



の子孫にさまざまな方法で依存関係を提供できKodein







 val kodein = Kodein { bind<Animal>().subTypes() with { animalType -> when (animalType.jvmType) { Dog::class.java -> eagerSingleton { Dog() } else -> provider { WildAnimal(animalType) } } }
      
      





Animal



クラスは、スーパークラスまたはインターフェイスのいずれかです.subtypes



を使用して、 TypeToken<*>



.subtypes



を取得します。 animalType



からJavaクラスを取得し、それに応じて、さまざまな方法で依存関係を提供します。 この機能は、 TypeToken



またはその派生物を多くの場合のコンストラクターパラメーターとして使用する場合に役立ちます。 また、この方法では、異なるタイプに対して同じ依存関係を作成して、不要なコードを回避できます。



パラメーターとして他の依存関係を必要とする依存関係を作成する



ほとんどの場合、依存関係としてパラメーターなしのクラスを作成するだけでなく、コンストラクターにパラメーターを渡す必要があるクラスを作成します。



 class ProductGateway(private val api: IProductApi, private val dispatchers: IDispatchersContainer) : IProductGateway
      
      





Kodein



以前に作成された依存関係を持つクラスを作成するKodein



、instance()関数呼び出しをパラメーターとして渡すだけで十分です。 この場合、作成の順序は重要ではありません。



 bind<IDispatchersContainer>() with singleton { DispatchersContainer() } bind<IProductGateway>() with singleton { ProductGateway(instance(), instance()) } bind<IProductApi>() with singleton { ProductApi() }
      
      





instance()



代わりに、 provider()



またはfactory()



呼び出しがある場合があります。これらのメソッドについては、依存関係の取得と実装に関するセクションで詳しく見ていきます。



以前に作成した依存関係メソッドを呼び出して依存関係を作成します



あまり良く聞こえませんが、 instance<TYPE>



を呼び出して、すでに提供されているクラスを取得し、このクラスのメソッドを呼び出して新しい依存関係を取得できます。



 bind<DataSource>() with singleton { MySQLDataSource() } bind<Connection>() with provider { instance<DataSource>().openConnection() }
      
      





モジュール



Dagger2



を使用しDagger2



依存関係をDagger2



慣れてDagger2



Kodein



では、一見、すべてがあまり良く見えません。 Application



クラスで多くの依存関係を作成する必要がありますが、個人的にはあまり好きではありません。 しかし、解決策がありますKodein



では、モジュールを作成し、必要に応じてそれらの場所に接続することもできます。



  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() } //   }
      
      





ただし、モジュールは依存関係を取得するためのメソッドを宣言する単なるコンテナであり、クラス自体は作成しないことに注意してください。 したがって、依存関係の受信をモジュールでシングルトンとして宣言し、このモジュールをKodein



2つの異なるインスタンスにKodein



すると、 Kodein



インスタンスごとに1つずつ、2つの異なるシングレットが取得されます。



また、各モジュールの名前は一意である必要があります。 ただし、モジュールを別のプロジェクトからインポートする必要がある場合、名前の一意性を保証するのは困難です;このため、モジュールの名前を変更するか、名前にプレフィックスを追加できます。



 import(apiModule.copy(name = "firstAPI")) import(secondApiModule.copy(prefix = "secondAPI-"))
      
      





私は、モジュールが互いに依存し、ある種の階層を構成しているときに作業に慣れています。 各モジュールを一度Kodein



インポートできるため、同じ依存モジュールを持つ2つのモジュールを1つのKodein



にインポートしようとすると、アプリケーションがクラッシュします。 解決策は簡単ですimportOnce(someModule)



呼び出しを使用してインポートする必要があります。これは、同じ名前のモジュールが以前にインポートされたかどうかを確認し、必要に応じてインポートします。



たとえば、そのような場合、アプリケーションはクラッシュします。



  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) }
      
      





ただし、 importOnce



呼び出しが2回目の接続試行である場合、すべてが機能します。 注意してください。



  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) }
      
      





継承



同じモジュールを2回使用すると、異なる依存関係が作成されますが、継承とDagger2



似た動作の実装についてはDagger2



でしょうか? すべてが簡単ですKodein



インスタンスから継承するだけで、相続人の親のすべての依存関係にアクセスできます。



 val kodein: Kodein = Kodein { bind<ISchedulersContainer>() with singleton { SchedulersContainer() } //   } val subKodein = Kodein { extend(kodein) //   }
      
      





再定義



デフォルトでは、依存関係をオーバーライドすることはできません。そうしないと、ユーザーが夢中になり、アプリケーションが正しく動作しない理由を探します。 ただし、 bind



関数の追加パラメーターを使用してこれを行うことは可能です。 この機能は、たとえばテストの整理に役立ちます。



 val kodein = Kodein { bind<Api>() with singleton { ApiImpl() } /* ... */ bind<Api>(overrides = true) with singleton { OtherApiImpl() } }
      
      





既定では、モジュールとその依存関係は、 Kodein



オブジェクトで既に宣言されている依存関係をオーバーライドできませんが、モジュールをインポートするときに、その依存関係を既存のものによってオーバーライドできるように指定できます。このモジュール内では、他のユーザーがオーバーライドできる依存関係を既に指定できます。



あまり明確に聞こえませんが、例を使用しましょう。 これらの場合、アプリケーションはクラッシュします。



  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) }
      
      





そして、これでは、モジュールの依存関係がKodein



オブジェクトで宣言された依存関係を上書きします。



  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) }
      
      





しかし、本当にやりたいことを理解している場合は、 Kodein



オブジェクトと同じ依存関係がある場合にそれらを再定義し、アプリケーションがクラッシュしないモジュールを作成できます。 モジュールにはallowSilentOverride



パラメーターを使用します。



 val testModule = Kodein.Module(name = "test", allowSilentOverride = true) { bind<EmailClient>() with singleton { MockEmailClient() } }
      
      





ドキュメントでは、依存関係の継承と再定義、および継承者の依存関係のコピーに関するより複雑な状況について説明していますが、これらの状況はここでは考慮しません。



依存関係の取得と注入



最後に、多くの方法で依存関係を宣言する方法を見つけました。今度は、クラスで依存関係を取得する方法を見つけます。



Kodein



開発者は、依存関係を取得する2つの方法- injection



retieval



ます。 要するに、 injection



は、クラスが作成されたとき、つまりコンストラクターですべての依存関係を受け取るときであり、 retrieval



は、クラス自体がその依存関係を取得する責任があるときです。



injection



を使用injection



、クラスはKodein



について何も認識せず、クラス内のコードはクリーンですが、 Kodein



を使用すると、依存関係をより柔軟に管理する機会があります。 retrieval



の場合、依存関係への最初のアピールの時点でのみretrieval



すべての依存関係が遅延的に取得されます。



依存関係を使用するためのKodein



メソッド



Kodein



クラスのインスタンスには、依存関係、依存関係ファクトリ、または依存関係プロバイダーを返す3つのメソッドがありprovider()



。それぞれ、 instance()



factory()



およびprovider()



です。 したがって、 factory



またはprovider



を使用して依存関係を提供すると、関数実行の結果だけでなく、関数自体も受け取ることができます。 すべてのバリエーションでタグを使用できることに注意してください。



  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()
      
      





コンストラクターによる依存性注入



すでに理解しているように、それはinjection



に関するものです。 実装するには、まずコンストラクターですべてのクラスの依存関係を取り出してから、 kodein.newInstance



を呼び出してクラスのインスタンスを作成する必要があります



 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()) } }
      
      





ヌル可能プロパティへの依存性注入



依存関係が宣言されているかどうかわからない可能性があります。 依存関係がKodein



インスタンスで宣言されていない場合、上記の例のコードはKodein.NotFoundException



ます。 結果としてnull



関数を取得したい場合、依存関係がない場合、 instanceOrNull()



factoryOrNull()



およびproviderOrNull()



3つの補助関数があり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()) } }
      
      





クラス内の依存関係を取得します。



前述のように、 retrieval



を使用する場合、すべての依存関係の初期化はデフォルトで遅延します。 これにより、必要な場合にのみ依存関係を取得し、システムが作成するクラスの依存関係を取得できます。



Activity



Fragment



および独自のライフサイクルを持つ他のクラスはすべてそれらについてです。



Activity



依存関係を実装するには、Kodeinのインスタンスへのリンクのみが必要です。その後、既知のメソッドを使用できます。 実際、上記のretrieval



例を見てきました。プロパティを宣言し、それを関数instance()



factory()



または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()
      
      





工場にパラメーターを渡す



すでに上記のように、パラメーターをファクトリーに渡すには、arg



関数parameterを使用するだけで十分instance



です。しかし、いくつかのパラメーターがある場合はどうでしょうか(以前に工場に最大5つのパラメーターが存在する可能性があると言いました)?オーバーロードされたコンストラクターを持ち、2〜5個の引数を取ることができるパラメーターにarg



クラスを渡すだけですM







 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))
      
      





依存関係の初期化を強制する



彼らが言ったように-デフォルトでは、初期化は怠zyですが、トリガーを作成し、プロパティ、複数のプロパティ、またはインスタンス全体にバインドすることKodein



ができます。このトリガーをプルすると、依存関係が初期化されます。



 val myTrigger = KodeinTrigger() val gson: Gson by kodein.on(trigger = myTrigger).instance() /*...*/ myTrigger.trigger() //     Gson
      
      





 val myTrigger = KodeinTrigger() val kodeinWithTrigger = kodein.on(trigger = myTrigger) val gson: Gson by kodeinWithTrigger.instance() /*...*/ myTrigger.trigger() //        kodeinWithTrigger
      
      





レイジーコデインインスタンスの作成



その前に、常に明示的にインスタンスを作成しましたKodein



LazyKodein



、オブジェクトを返す必要のあるコンストラクター内の関数を受け入れるクラス使用して、このプロパティの初期化を延期することができますKodein







このアプローチは、たとえば、特定のKodeinインスタンスからの依存関係がまったく必要かどうかが不明な場合に役立ちます。



 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() //     kodein   
      
      





Kodein.lazyを呼び出すと、同様の結果が得られます。



  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   
      
      





コデイン遅延初期化



遅延初期化の場合Kodein



、オブジェクトが存在しLateInitKodein



ます。このオブジェクトを作成し、プロパティの作成をそのオブジェクトに委任し、オブジェクト自体を初期化した後、プロパティをそのオブジェクトに設定するとbaseKodein



、依存関係に既にアクセスできます。



 val kodein = LateInitKodein() val gson: Gson by kodein.instance() /*...*/ kodein.baseKodein = /*     Kodein */ /*...*/ gson.fromJson(someStr)
      
      





指定されたタイプのすべてのインスタンスを取得します



指定された型のインスタンスとそのすべての子孫をフォームでKodeinに要求できますList



すべては指定されたタグ内にのみあります。これを行うには、方法があります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()
      
      





ログに印刷すると、[32767、136.88、4562、12.46]が表示されます。タグとの依存関係はリストに含まれていませんでした。



KodeinAwareインターフェースを使用して依存関係の取得を簡素化



このインターフェイスでは、typeプロパティをオーバーライドする必要がKodein



あり、その代わりに、インスタンスで使用可能なすべての機能にアクセスできます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() }
      
      





ご覧のとおりby allInstances()



by kodein.allInstances()







以前は依存関係を受信するためのトリガーについて既に説明したように、以前の代わりに簡単に記述できますインターフェイスでKodeinAware



は、トリガーをオーバーライドし、このトリガーが呼び出されたときに宣言されたすべての依存関係を取得できます。



 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() } }
      
      





依存関係とインスタンスへのアクセスKodein



が遅延しているため、Kodein



Kotlinに組み込まれ関数のインスタンス初期化を委任できますlazy



そのようなアプローチは、コンテキストなどに応じてクラスで役立ちます(例:)Activity







 class CategoriesActivity : Activity(), KodeinAware { override val kodein: Kodein by lazy { (application as MyApplication).kodein } private val myFloat: Float by instance()
      
      





同じ理由で、修飾子を使用できます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 }
      
      





プロパティを委任せずに依存関係にアクセスする



何らかの理由でプロパティの委任を使用したくない場合は、DKodein



(直接から)直接アクセスを使用できます主な違いは、遅延初期化が消えてしまいます、依存性はコールの時にすぐに得られることがありinstance



provider



かつ同様の機能。DKodein



既存のKodeinインスタンスから取得するか、ゼロから構築できます。



 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はフレームワークKodeinAware



使用できDKodein



、フレームワークDKodeinAware



では実験できます。



任意のコンテキスト内で依存関係を取得します



1つのオブジェクトからKodein



同じタイプのいくつかの依存関係を取得するために、引数を持つタグとファクトリーを使用するオプションを既に検討しましたが、もう1つあります-コンテキストを使用する(これはAndroidのコンテキストではありません)。



タグとの依存関係との違い:





多くの場合、コンテキストの代わりに、引数付きのファクトリを使用できます。開発者Kodein



は、使用するものがわからない場合はこれを行うことをお勧めします。ただし、コンテキストは、たとえば、2つの引数を同じ型にキャストできない場合に役立ちます。



たとえば、とがActivity



ありPresenter



、1つのオブジェクトを使用しKodein



て、受け取ったクラスに応じて、さまざまな方法でさまざまなタイプのいくつかの依存関係を提供する必要があります。リードにActivity



し、Presenter



1種類に-あなたはオプションのインタフェースを必要とし、工場は結果の引数の型をチェックする必要があります。このスキームはあまり便利ではありません。したがって、コンテキストの使用方法を見てみましょう。



 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")) }
      
      





もちろん、例は耳の上に引っ張られ、実際には実際にそのような状況に遭遇することはほとんどありませんが、この例はコンテキストがどのように機能するかを示しています。



依存関係を宣言するには、次のように指定されていないwith provider()



、とwith contexted<OurContextClass>().provider



どこOurContextClass



-クラスのこのタイプのコピーは、コンテキストとして機能します。contexted



プロバイダーまたはファクトリーのみです。



依存関係を返す関数内のこのコンテキストへのアクセスは、名前の下の変数を介して行われますcontext







コンテキストにアタッチされた依存関係を取得するには、まずKodein



関数を使用してオブジェクトのコンテキストを指定し、on()



次に依存関係を要求する必要があります



同様に、コンテキストはの場合に使用されinjection



ます。



 private val productApi: IProductApi by kodein.on(context = someContext).newInstance { ProductApi(instance(), instance()) } }
      
      





Android拡張機能



記事の冒頭で、の拡張オプションを検討することを約束しましたAndroid



上で説明したように、それを

使用することを妨げるものは何もありませんKodein



が、すべてをより便利にすることができます。



Android向けの組み込みKodein



非常に便利なのは、Android用に準備されたモジュールです。接続するには、クラスがプロパティを遅延してApplication



実装KodeinAware



および初期化する必要がありますKodein



(インスタンスにアクセスするためApplication



)。その見返りとして、必要なApplication



ものすべてを含め、クラスから取得できる膨大な数の宣言された依存関係を取得しますContext



接続方法-例を見てください。



 class MyApplication : Application(), KodeinAware { override val kodein = Kodein.lazy { import(androidModule(this@MyApplication)) //  } val inflater: LayoutInflater by instance() }
      
      





あなたが見ることができるように-あなたは、たとえば、取得することができますLayoutInflater



モジュールで宣言されている依存関係の完全なリストについては、こちらをご覧ください



コンテキストを認識しているAndroidクラスの外部でこれらの依存関係を取得する場合は、コンテキストを明示的に指定します。



 val inflater: LayoutInflater by kodein.on(context = getActivity()).instance()
      
      





すぐに最も近いKodein()を介して親Kodeinを取得します



Androidでは、一部のオブジェクトは他のオブジェクトに依存しています。最上位にはアプリケーションがあり、その下にアクティビティがあり、次にフラグメントがあります。Activity KodeinAware



実装し、初期化としてを呼び出してからclosestKodein()



インスタンスを取得できますKodein



Application







 class MyActivity : Activity(), KodeinAware { override val kodein by closestKodein() val ds: DataSource by instance() }
      
      





closestKodein



Androidクラスの外部で取得することもできますが、関数を呼び出すことができるAndroidコンテキストが必要です。使用する場合はKodeinAware



、そのコンテキストも指定します(対応するプロパティをオーバーライドし、Androidコンテキストを関数に渡しますkcontext()



)。



 class MyController(androidContext: Context) : KodeinAware { override val kodein by androidContext.closestKodein() override val kodeinContext = kcontext(androidContext) val inflater: LayoutInflater by instance() }
      
      





アクティビティで別のKodeinを作成する



Activityの親Kodeinから継承して展開する必要があるかもしれません。解決策は非常に簡単です。



 class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by Kodein.lazy { extend(parentKodein) /*   */ } }
      
      





設定変更中のコデイン



はい、できます。これには機能がありretainedKodein



ます。使用する場合Kodein



、構成の変更後にオブジェクトは再作成されません。



 class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by retainedKodein { extend(parentKodein) } }
      
      





記事で何が言われていないのですか?



私は完全なふりをしたわけではなく、私自身はそれらを述べようとするほど十分に理解していない。基本的な原則を知って、自分で学ぶことができるもののリストは次のとおりです。





さて、ドキュメントへのリンク:





読んでくれてありがとう、この記事があなたの役に立つことを願っています!



All Articles