You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 3, 2023. It is now read-only.
Copy file name to clipboardExpand all lines: README.md
+55-66
Original file line number
Diff line number
Diff line change
@@ -9,15 +9,10 @@
9
9
> remain unknown to the Vikings' rivals for centuries, the Ulfberht was a revolutionary high-tech
10
10
> tool as well as a work of art.
11
11
12
-
*A little more bad-ass than a Dagger, huh?*
13
-
14
-
Dependency injection is a technique in which an application supplies dependencies of an object.
15
-
"Dependencies" in this context are not dependencies like in a Gradle file. Dependencies are services
16
-
(i.e. APIs, classes) that are needed in certain parts of your code.
17
-
18
-
Dependency injection enables you to pass around services without manual construction - it keeps
19
-
track of everything for you and injects things where they are needed. The need for this is more
20
-
apparent when you deal with very large applications.
12
+
*A little more bad-ass than a Dagger.* Dependency injection is a technique in which an application
13
+
supplies dependencies of an object. "Dependencies" in this context are not dependencies like in a
14
+
Gradle file. Dependencies are services (i.e. APIs, classes) that are needed in certain parts of
15
+
your code.
21
16
22
17
---
23
18
@@ -51,9 +46,9 @@ based rather than reflection-based, while still being written in Kotlin. And I w
51
46
scoping support, especially on Android. _This is the result._
52
47
53
48
You may be wondering what makes this library different than KOIN or other Kotlin "DI" libraries?
54
-
Ulfberht uses annotation processing to do actual dependency injection vs. plain service location.
55
-
A big example of this difference is that you don't need to manually tell this library how to fill
56
-
constructor parameters, they are filled for you by generated code.
49
+
Libraries like KOIN are just service locators - you need to build the dependency graph manually,
50
+
filling in constructors, etc. Annotation processor based DI libraries handle this for you with
51
+
code generation, so you *don't* need to write boilerplate and you *don't* need to use reflection.
57
52
58
53
---
59
54
@@ -387,77 +382,66 @@ This code assumes that one of the modules going up the graph from `Component5` c
387
382
388
383
# Qualifiers
389
384
390
-
Qualifiers are simple identifiers that associate a type that may be broad, like a string, with a
391
-
very specific bound or provided instance. There are a few places where you can specify a qualifier.
392
-
393
-
First, the `@Binds` and `@Provides` annotations take a qualifier:
385
+
Qualifiers are simple identifiers that associate a type with a very specific bound or provided
386
+
instance. A qualifier is a special type of annotation, which is defined like this:
394
387
395
388
```kotlin
396
-
constvalQUALIFIER_ONE="one"
397
-
constvalQUALIFIER_TWO="two"
389
+
@Qualifier
390
+
annotationclassDemoQualifier1
398
391
392
+
@Qualifier
393
+
annotationclassDemoQualifier2
394
+
```
395
+
396
+
You use it to mark `@Binds` and `@Provides` functions:
397
+
398
+
```kotlin
399
399
@Module
400
400
interfaceDemoModule1 {
401
-
@Binds(QUALIFIER_ONE)
402
-
fundemoClass1(impl:Demo1Impl): Demo1
403
-
404
-
@Binds(QUALIFIER_ONE)
405
-
fundemoClass2(impl:Demo2Impl): Demo2
401
+
@Binds @Singleton @DemoQualifier1
402
+
fundemoClass(impl:Demo1Impl): Demo1
406
403
}
407
404
408
405
@Module
409
406
abstractclassDemoModule2 {
410
-
@Provides(QUALIFIER_TWO)
411
-
fundemoClass1(): Demo1 {
407
+
@Provides @Singleton @DemoQualifier2
408
+
fundemoClass(): Demo1 {
412
409
returnDemo1Impl()
413
410
}
414
-
415
-
@Provides(QUALIFIER_TWO)
416
-
fundemoClass2(): Demo2 {
417
-
returnDemo2Impl()
418
-
}
419
411
}
420
412
```
421
413
422
-
Second, constructor parameters take a qualifier via a `@Param` annotation:
414
+
Then, you can mark constructor parameters with it...
423
415
424
416
```kotlin
425
417
classSomeInjectedClass(
426
-
@Param(QUALIFIER_ONE)valsomeDependency:Demo1,
427
-
@Param(QUALIFIER_TWO)valanotherDependency:Demo1
418
+
@DemoQualifier1valsomeDependency:Demo1,
419
+
@DemoQualifier2valanotherDependency:Demo1
428
420
) {
429
421
...
430
422
}
431
423
```
432
424
433
-
Third, `@Provides` method parameters take a qualifier also via the `@Param` annotation:
434
-
435
-
```kotlin
436
-
@Module
437
-
abstractclassDemoModule2 {
438
-
@Provides
439
-
fundemoClass1(
440
-
@Param(QUALIFIER_ONE) neededDependency:Demo2
441
-
): Demo1 {
442
-
returnDemo1Impl()
443
-
}
444
-
}
445
-
```
446
-
447
-
Finally, the `@Inject` annotation takes a qualifier as well:
425
+
...along with `@Inject` targets (the `field:` prefix on the annotation name is important in Kotlin):
448
426
449
427
```kotlin
450
428
classSomeClass {
451
-
@Inject(QUALIFIER_ONE)
452
-
lateinitvarsomeDependency:Demo1
453
-
@Inject(QUALIFIER_TWO)
454
-
lateinitvaranotherDependency:Demo1
429
+
@Inject @field:DemoQualifier1
430
+
lateinitvarsomeDependency1:Demo1
431
+
@Inject @field:DemoQualifier2
432
+
lateinitvarsomeDependency2:Demo1
455
433
456
434
init {
457
435
component<SomeComponent>().inject(this)
458
436
}
459
437
}
460
-
```
438
+
```
439
+
440
+
You will get two completely separate instances of `Demo1`, since two different qualifiers are being
441
+
used. `@Singleton` was applied for demo purposes to show that it'll store two different instances.
442
+
But even without that, you're providing two different things. This could be useful if you were
443
+
providing primitives, like strings, or an interface for something like preferences. There's a lot of
444
+
possibilities.
461
445
462
446
---
463
447
@@ -467,43 +451,48 @@ Sometimes your app may need to be able to inject something that is defined at ru
467
451
that cannot be constructed in a module. A good example of when this would be necessary is in
468
452
an Android application, like if you needed to inject the Application context.
469
453
470
-
First, you tag constructor parameters that need to be provided at runtime with the `@Param`
471
-
annotation, which is discussed in [Qualifiers](#qualifiers) above.
454
+
First, you tag constructor parameters or fields that need to be provided at runtime with a
455
+
qualifier annotation, which is discussed in [Qualifiers](#qualifiers) above.
472
456
473
457
```kotlin
474
-
constvalAPP_CONTEXT:String="app_context"
458
+
@Qualifier
459
+
annotationclassAppContext
460
+
461
+
@Qualifier
462
+
annotationclassApiKey
475
463
476
464
classStringRetriever(
477
-
@Param(APP_CONTEXT) valappContext:Context
465
+
@AppContext valappContext:Context,
466
+
@ApiKey valapiKey:String
478
467
) {
479
468
fungetString(@IdRes res:Int): String {
480
469
return appContext.resources.getString(res)
481
470
}
482
471
}
483
472
```
484
473
485
-
At injection time, you pass mapped runtime dependencies into the `component` method. They are available
486
-
for injection until the component is destroyed, or its parents destroy it.
474
+
At injection time, you pass mapped runtime dependencies into the `component<>()` method. They are
475
+
available for injection until the component is destroyed, or its parents destroy it.
487
476
488
477
```kotlin
489
-
// Should ideally be the same constant above, rather than being defined twice
490
-
constvalAPP_CONTEXT:String="app_context"
491
-
492
478
classLoginActivity : AppCompatActivity() {
493
479
@Inject
494
-
lateinitvar stringRetriever:StringRetriever
480
+
lateinitvar stringRetriever:StringRetriever
495
481
496
482
overridefunonCreate(savedInstanceState:Bundle?) {
497
483
super.onCreate(savedInstanceState)
498
484
499
485
component<LoginComponent>(
500
-
APP_CONTEXT to applicationContext
486
+
AppContext::classto applicationContext,
487
+
ApiKey::classto "hello, world!"
501
488
).inject(this)
502
489
}
503
490
}
504
491
```
505
492
506
-
Runtime dependencies in a component are made available to all of the component's children too. In an Android application, providing the application context at the `Application` level will make it available to all Activities and Fragments that use child components.
493
+
Runtime dependencies in a component are made available to all of the component's children too.
494
+
In an Android application, providing the application context at the `Application` level will make
495
+
it available to all Activities and Fragments that use child components.
507
496
508
497
---
509
498
@@ -579,4 +568,4 @@ just inject the `ViewModel` as you would inject anything else.
579
568
However, you can only inject a `ViewModel` into an `androidx.fragment.app.Fragment` or
580
569
`androidx.fragment.app.FragmentActivity` (includes `AppCompatActivity` and descendants). Why?
581
570
Internally, Ulfberht's generated code delegates through `ViewModelProviders` which must attach to
0 commit comments