在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):evant/kotlin-inject开源软件地址(OpenSource Url):https://github.com/evant/kotlin-inject开源编程语言(OpenSource Language):Kotlin 99.8%开源软件介绍(OpenSource Introduction):kotlin-injectA compile-time dependency injection library for kotlin. @Component
abstract class AppComponent {
abstract val repo: Repository
@Provides
protected fun jsonParser(): JsonParser = JsonParser()
protected val RealHttp.bind: Http
@Provides get() = this
}
interface Http
@Inject
class RealHttp
@Inject
class Api(private val http: Http, private val jsonParser: JsonParser)
@Inject
class Repository(private val api: Api) val appComponent = AppComponent::class.create()
val repo = appComponent.repo Downloadplugins {
id("org.jetbrains.kotlin.jvm") version "1.5.31"
id("org.jetbrains.kotlin.kapt") version "1.5.31"
}
dependencies {
kapt("me.tatarka.inject:kotlin-inject-compiler-kapt:0.4.1")
implementation("me.tatarka.inject:kotlin-inject-runtime:0.4.1")
} or with KSPYou can use ksp instead of kapt.
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
plugins {
id("org.jetbrains.kotlin.jvm") version "1.5.31"
id("com.google.devtools.ksp") version "1.5.31-1.0.1"
}
repositories {
mavenCentral()
google()
}
dependencies {
ksp("me.tatarka.inject:kotlin-inject-compiler-ksp:0.4.1")
implementation("me.tatarka.inject:kotlin-inject-runtime:0.4.1")
} UsageLet's go through the above example line-by line and see what it's doing. @Component
abstract class AppComponent { The building block of kotlin-inject is a component which you declare with an abstract val repo: Repository In your component you can declare abstract read-only properties or functions to return an instance of a given type. This is where the magic happens. kotlin-inject will figure out how to construct that type for you in it's generated implementation. How does it know how to do this? There's a few ways: @Provides
protected fun jsonParser(): JsonParser = JsonParser() For external dependencies, you can declare a function or read-only property in the component to create an instance for a certain type. kotlin-inject will use the return type to provide this instance where it is requested. Note: It is good practice to always explicitly declare the return type, that way it's clear what type is being provided. It may not always be what you expect! protected val RealHttp.bind: Http
@Provides get() = this You can declare arguments to a providing function/property to help you construct your instance. Here we are taking in an
instance of @Provides
fun http(http: RealHttp): Http = http @Inject
class RealHttp
@Inject
class Api(private val http: Http, private val jsonParser: JsonParser)
@Inject
class Repository(private val api: Api) For your own dependencies you can simply annotate the class with val appComponent = AppComponent::class.create()
val repo = appComponent.repo Finally, you can create an instance of your component with the generated FeaturesComponent ArgumentsIf you need to pass any instances into your component you can declare them as constructor args. You can then pass them
into the generated create function. You can optionally annotate it with @Component
abstract class MyComponent(@get:Provides protected val foo: Foo) MyComponent::class.create(Foo()) If the argument is another component, you can annotate it with @Component
abstract class ParentComponent {
protected fun provideFoo(): Foo = ...
}
@Component
abstract class ChildComponent(@Component val parent: ParentComponent) {
abstract val foo: Foo
} val parent = ParentComponent::class.create()
val child = ChildComponent::class.create(parent) Type Alias SupportIf you have multiple instances of the same type you want to differentiate, you can use type aliases. They will be treated as separate types for the purposes of injection. typealias Dep1 = Dep
typealias Dep2 = Dep
@Component
abstract class MyComponent {
@Provides
fun dep1(): Dep1 = Dep("one")
@Provides
fun dep2(): Dep2 = Dep("two")
@Provides
fun provides(dep1: Dep1, dep2: Dep2): Thing = Thing(dep1, dep2)
}
@Inject
class InjectedClass(dep1: Dep1, dep2: Dep2) Function InjectionYou can also use type aliases to inject into top-level functions. Annotate your function with typealias myFunction = () -> Unit
@Inject
fun myFunction(dep: Dep) {
} You can then use the type alias anywhere and you will be provided with a function that calls the top-level one with the requested dependencies. @Inject
class MyClass(val myFunction: myFunction)
@Component
abstract class MyComponent {
abstract val myFunction: myFunction
} You can optionally pass explicit args as the last arguments of the function. typealias myFunction = (String) -> String
@Inject
fun myFunction(dep: Dep, arg: String): String = ... ScopesBy default kotlin-inject will create a new instance of a dependency each place it's injected. If you want to re-use an instance you can scope it to a component. The instance will live as long as that component does. First create your scope annotation. @Scope
@Target(CLASS, FUNCTION, PROPERTY_GETTER)
annotation class MyScope Then annotate your component with that scope annotation. @MyScope
@Component
abstract class MyComponent() Finally, annotate your provides and @MyScope
@Component
abstract class MyComponent {
@MyScope
@Provides
protected fun provideFoo(): Foo = ...
}
@MyScope
@Inject
class Bar() Component InheritanceYou can define @NetworkScope
abstract class NetworkComponent {
@NetworkScope
@Provides
abstract fun api(): Api
} Then you can have multiple implementations @Component
abstract class RealNetworkComponent : NetworkComponent() {
override fun api(): Api = RealApi()
}
@Component
abstract class TestNetworkComponent : NetworkComponent() {
override fun api(): Api = FakeApi()
} Then you can provide the abstract class to your app component @Component abtract class AppComponent(@Component val network: NetworkComponent) Then in your app you can do AppComponent::class.create(RealNetworkComponent::class.create()) an in tests you can do AppComponent::class.create(TestNetworkComponent::class.create()) Multi-bindingsYou can collect multiple bindings into a For a set, return the type you want to put into a set, then you can inject or provide a @Component
abstract class MyComponent {
abstract val allFoos: Set<Foo>
@IntoSet
@Provides
protected fun provideFoo1(): Foo = Foo("1")
@IntoSet
@Provdies
protected fun provideFoo2(): Foo = Foo("2")
} For a map, return a @Component
abstract class MyComponent {
abstract val fooMap: Map<String, Foo>
@IntoMap
@Provides
protected fun provideFoo1(): Pair<String, Foo> = "1" to Foo("1")
@IntoMap
@Provides
protected fun provideFoo2(): Pair<String, Foo> = "2" to Foo("2")
} Function Support & Assisted InjectionSometimes you want to delay the creation of a dependency or provide additional params manually. You can do this by injecting a function that returns the dependency instead of the dependency directly. The simplest case is you take no args, this gives you a function that can create the dep. @Inject
class Foo
@Inject
class MyClass(fooCreator: () -> Foo) {
init {
val foo = fooCreator()
}
} If you define args, you can use these to assist the creation of the dependency. These are passed in as the last arguments to the dependency. @Inject
class Foo(bar: Bar, arg1: String, arg2: String)
@Inject
class MyClass(fooCreator: (arg1: String, arg2: String) -> Foo) {
init {
val foo = fooCreator("1", "2")
}
} LazySimilarly, you can inject a @Inject
class Foo
@Inject
class MyClass(lazyFoo: Lazy<Foo>) {
val foo by lazyFoo
} Default ArgumentsYou can use default arguments for parameters you inject. If the type is present in the graph, it'll be injected, otherwise the default will be used. @Inject class MyClass(val dep: Dep = Dep("default"))
@Component abstract ComponentWithDep {
abstract val myClass: MyClass
@Provides fun dep(): Dep = Dep("injected")
}
@Component abstract ComponentWithoutDep {
abstract val myClass: MyClass
}
ComponentWithDep::class.create().myClass.dep // Dep("injected")
ComponentWithoutDep::class.create().myClass.dep // Dep("default") OptionsYou can provide some additional options to the processor.
Additional docsYou can find additional docs on specific use-cases in the docs folder. SamplesYou can find various samples here |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论