SlideShare une entreprise Scribd logo
1  sur  98
Télécharger pour lire hors ligne
Using Dagger in a
Clean Architecture project
Fabio Collini
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
Android programmazione avanzata
Android Developers Italia
Ego slide
entitiesentities
Layered

Architecture
Clean

Architecture
Dagger
Modularization
Modularization
https://www.youtube.com/watch?v=PZBg5DIzNww
https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
1. Speeds up builds
2. Enable on demand delivery
3. Simplify development
4. Reuse modules across apps
5. Experiment with new technologies
6. Scale development teams
7. Enables refactoring
8. Simplifies test automation
Modularization - Why you should care
https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
App
feature3feature1 feature2
dynamic
feature3
App
feature1 feature2
App
dynamic
feature3
feature1 feature2
Feature1App
Main app
BUILD SUCCESSFUL in 1m 12s

847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date

Feature app
BUILD SUCCESSFUL in 43s

504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date

Feature module
BUILD SUCCESSFUL in 30s

427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date

Core module
BUILD SUCCESSFUL in 22s

308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date

Utils
BUILD SUCCESSFUL in 6s

22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date
Poorman'sbenchmark
Main app
BUILD SUCCESSFUL in 1m 12s

847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date

Feature app
BUILD SUCCESSFUL in 43s

504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date

Feature module
BUILD SUCCESSFUL in 30s

427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date

Core module
BUILD SUCCESSFUL in 22s

308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date

Utils
BUILD SUCCESSFUL in 6s

22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date
Poorman'sbenchmark
Architectures
Activity
Repository
api1
UseCase
ViewModel
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
RepositoryImplRepositoryUseCase
domain repository
UseCase
RepositoryImpl
Repository
domain repository
Repository RepositoryImpl
UseCase
Inversion
Of Control
The “I” in S.O.L.I.D.
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
TDD
Pros
Framework
independence
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
https://www.youtube.com/watch?v=GlDsfq3xHvo&t=
Dagger
App
dynamic
feature3
feature1 feature2
Feature1App
App
dynamic
feature3
feature1 feature2
Feature1App
Dagger Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Component dependencies
Subcomponents
Dagger Android
Daggerpatterns
Component dependencies
https://medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7
https://proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc
DaggerTip#1
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.build()
}1
DaggerTip#1
Exception in thread "main" java.lang.IllegalStateException: Component1 must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at DaggerComponent2$Builder.build(DaggerComponent2.java:35)
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.build()
}1
DaggerTip#1
Exception in thread "main" java.lang.IllegalStateException: Component1 must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at DaggerComponent2$Builder.build(DaggerComponent2.java:35)
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.component1(DaggerComponent1.create())
.build()
}1
DaggerTip#1
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2 {
@Component.Factory
interface Factory {
fun create(component1: Component1): Component2
}2
}3
fun main() {
DaggerComponent2.factory()
.create(DaggerComponent1.create())
}1
DaggerTip#2
@Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
Module1
pattern
format
DaggerTip#2
@Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
@Component(modules = [Module1::class])
interface Component1 {
val format: DecimalFormat
}2
Module1
Component1
pattern
format
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
@Component(modules = [Module1::class])
interface Component1 {
val format: DecimalFormat
}2
@Module
class Module2 {
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
}4
Module2
Component2
Module1
Component1
pattern
format
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
pattern
format
error: [Dagger/MissingBinding] String cannot be provided without an @Inject constructor or
an @Provides-annotated method.
public abstract interface Component2 {
^
String is injected at
Module2.format(pattern)
DecimalFormat is provided at
Component2.getFormat()
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
patternpattern
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
patternpattern
format
DaggerTip#3
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
DaggerTip#3 interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
fun main() {
val component1 = object : Component1 {
override val pattern ="#,###.0000"
}3
val component2 = DaggerComponent2.factory().create(component1)
}4
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Lib
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Lib
feature2
Lib
feature1
feature2
Lib
feature1
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
feat1feat2Lib
feature2
Lib
feature1
@Singleton
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Singleton
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Libfeat1feat2
feature2
Lib
feature1
@Scope annotation class Feature1SingletonScope
@Feature1SingletonScope
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Scope annotation class Feature2SingletonScope
@Feature2SingletonScope
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Scope annotation class LibSingletonScope
@Module
class LibModule {
@LibSingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@LibSingletonScope
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Libfeat1feat2
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
fun main() {
val obj1 = DaggerLibComponent.create().myLibObject
val obj2 = DaggerLibComponent.create().myLibObject
println(obj1 === obj2)
//false :(
}2
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
fun main() {
val component = DaggerLibComponent.create()
val obj1 = component.myLibObject
val obj2 = component.myLibObject
println(obj1 === obj2)
//true :)
}2
interface LibComponentProvider {
val libComponent: LibComponent
}8
Lib
interface Feature1Provider {
val feature1Component: Feature1Component
}7
interface LibComponentProvider {
val libComponent: LibComponent
}8
feature1Lib
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
interface Feature1Provider {
val feature1Component: Feature1Component
}7
interface LibComponentProvider {
val libComponent: LibComponent
}8
feature1LibApp
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1App
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
interface ComponentHolder {
val map: MutableMap<KClass<*>, Any>
}1
open class ComponentHolderApp : Application(), ComponentHolder {
override val map = mutableMapOf<KClass<*>, Any>()
}2
inline fun <reified C : Any> ComponentHolder.getOrCreate(factory: () -> C): C =
map.getOrPut(C::class, factory) as C
A “real” implementation is available here:
https://github.com/fabioCollini/CleanWeather/blob/dagger/kotlinUtils/
src/main/java/it/codingjam/cleanweather/kotlinutils/ComponentHolder.kt
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() = getOrCreate {
}1
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() = getOrCreate {
}2
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() = getOrCreate {
DaggerFeature1Component.factory()
.create(libComponent())
}1
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() = getOrCreate {
DaggerLibComponent.create()
}2
Feature1App
App Libfeature1
class MyApp : ComponentHolderApp()
class MyFeature1App : ComponentHolderApp()
fun ComponentHolderApp.feature1Component() = getOrCreate {
DaggerFeature1Component.factory()
.create(libComponent())
}1
fun ComponentHolderApp.libComponent() = getOrCreate {
DaggerLibComponent.create()
}2
Libfeature1
Feature1App
App
@Feature1SingletonScope
@Component(dependencies = [Lib1Component::class, Lib2Component::class])
interface Feature1Component {
//...
}
DaggerTip#5
error: @Feature1SingletonScope Feature1Component depends on more than one scoped component:
@dagger.Component(dependencies = {Lib1Component.class, Lib2Component.class})
^
@Lib1SingletonScope Lib1Component
@Lib2SingletonScope Lib2Component
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1Component {
val myLibObject: MyLibObject
}2
DaggerTip#5
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1ComponentImpl : Lib1Component
DaggerTip#5
@Scope
annotation class Lib1SingletonScope
@Module
class Lib1Module {
@Lib1SingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}1
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1ComponentImpl : Lib1Component
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
DaggerTip#5
@Scope
internal annotation class Lib1SingletonScope
@Module
internal class Lib1Module {
@Lib1SingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}1
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
internal interface Lib1ComponentImpl : Lib1Component
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
DaggerTip#5
DaggerTip#5
interface Lib1Component {
val myLibObject: MyLibObject
}2
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
NODaggerTip
interface Lib1Component {
val myLibObject: MyLibObject
}2
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
NODaggerTip
interface Lib1Component {
val myLibObject: MyLibObject
}2
private class Lib1ComponentImpl : Lib1Component {
override val myLibObject by lazy {
MyLibObject()
}3
}4
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { Lib1ComponentImpl() }
Clean
Architecture
domain repository
Repository RepositoryImpl
UseCase
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
domain
domain repository
Repository RepositoryImpl
UseCase
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
@Component
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
@Module
class DomainModule {
@Provides
fun provideRepository(): Repository =
RepositoryImpl()
}1
@Component(modules = [DomainModule::class])
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
@Component
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
interface DomainDependencies {
val repository: Repository
}3
@Component(
dependencies = [DomainDependencies::class]
)9
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}3
@Component(
dependencies = [DomainDependencies::class]
)9
interface DomainComponent {
val useCase: UseCase
}2
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domain
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
map[DomainDependencies::class]
as DomainDependencies
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domain
class MyApp : ComponentHolderApp() {
override fun onCreate() {
map[DomainDependencies::class] =
DaggerRepositoryComponent.create()
}1
}21
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
map[DomainDependencies::class]
as DomainDependencies
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domainApp
interface DomainDependencies {
val repository: Repository
}2
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
@InversionProvider
fun ComponentHolderApp.provideDomainDependencies():
DomainDependencies = getOrCreate {
DaggerRepositoryComponent.create()
}6
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
@InversionProvider
fun ComponentHolderApp.provideDomainDependencies():
DomainDependencies = getOrCreate {
DaggerRepositoryComponent.create()
}6
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
Testing
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
}2
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
@Test
fun launchActivity() {
whenever(useCaseMock.retrieve()) doReturn "ABCDEF"
rule.launchActivity(null)
onView(withId(R.id.text))
.check(matches(withText("ABCDEF")))
}3
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
@Test
fun launchActivity() {
whenever(useCaseMock.retrieve()) doReturn "ABCDEF"
rule.launchActivity(null)
onView(withId(R.id.text))
.check(matches(withText("ABCDEF")))
}3
}4
Wrappingup
Component dependencies
Each module exposes:
a Component interface
a method to create the Component
A map in the App can be useful to:
manage singletons
replace real objects with fakes/mocks
Links
Demo Project
github.com/fabioCollini/CleanWeather/tree/dagger
Droidcon Italy talk - SOLID principles in practice: the Clean Architecture
youtube.com/watch?v=GlDsfq3xHvo&t=
Implementing Dependency Inversion using Dagger components
medium.com/google-developer-experts/implementing-dependency-inversion-using-dagger-components-d6b0fb3b6b5e
Inversion library
github.com/fabioCollini/Inversion
Yigit Boyar, Florina Muntenescu - Build a Modular Android App Architecture (Google I/O'19)
youtube.com/watch?v=PZBg5DIzNww
Jeroen Mols - Modularization - Why you should care
jeroenmols.com/blog/2019/03/06/modularizationwhy/
Ben Weiss - Dependency injection in a multi module project
medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7
Marcos Holgado - Using Dagger in a multi-module project
proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc
THANKS
FOR YOUR
ATTENTION
QUESTIONS?
@fabioCollini

Contenu connexe

Tendances

Reflection in java
Reflection in javaReflection in java
Reflection in java
upen.rockin
 

Tendances (20)

Observer pattern
Observer patternObserver pattern
Observer pattern
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Android MVVM
Android MVVMAndroid MVVM
Android MVVM
 
Reflection in java
Reflection in javaReflection in java
Reflection in java
 
Angular Dependency Injection
Angular Dependency InjectionAngular Dependency Injection
Angular Dependency Injection
 
Java Inheritance and Polymorphism
Java Inheritance and PolymorphismJava Inheritance and Polymorphism
Java Inheritance and Polymorphism
 
Date and Time Module in Python | Edureka
Date and Time Module in Python | EdurekaDate and Time Module in Python | Edureka
Date and Time Module in Python | Edureka
 
Introduction to flutter
Introduction to flutter Introduction to flutter
Introduction to flutter
 
Java Programming for Designers
Java Programming for DesignersJava Programming for Designers
Java Programming for Designers
 
Java beans
Java beansJava beans
Java beans
 
Flutter Intro
Flutter IntroFlutter Intro
Flutter Intro
 
Flutter presentation.pptx
Flutter presentation.pptxFlutter presentation.pptx
Flutter presentation.pptx
 
Java reflection
Java reflectionJava reflection
Java reflection
 
Firebase Auth Tutorial
Firebase Auth TutorialFirebase Auth Tutorial
Firebase Auth Tutorial
 
Introduction to design patterns
Introduction to design patternsIntroduction to design patterns
Introduction to design patterns
 
Android adapters
Android adaptersAndroid adapters
Android adapters
 
Angular - Chapter 3 - Components
Angular - Chapter 3 - ComponentsAngular - Chapter 3 - Components
Angular - Chapter 3 - Components
 
Cross platform app development with flutter
Cross platform app development with flutterCross platform app development with flutter
Cross platform app development with flutter
 
Introduction to Android and Android Studio
Introduction to Android and Android StudioIntroduction to Android and Android Studio
Introduction to Android and Android Studio
 
Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 

Similaire à Using Dagger in a Clean Architecture project

Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2
Alessandro Molina
 
Use Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDEUse Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDE
Benjamin Cabé
 

Similaire à Using Dagger in a Clean Architecture project (20)

Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
 
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveDataAndroid MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
 
Dark side of Android apps modularization
Dark side of Android apps modularizationDark side of Android apps modularization
Dark side of Android apps modularization
 
Spring boot
Spring bootSpring boot
Spring boot
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
 
Angular2 + rxjs
Angular2 + rxjsAngular2 + rxjs
Angular2 + rxjs
 
Flutter technology Based on Web Development
Flutter technology Based on Web Development Flutter technology Based on Web Development
Flutter technology Based on Web Development
 
Extending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native ModulesExtending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native Modules
 
OpenDaylight Developer Experience 2.0
 OpenDaylight Developer Experience 2.0 OpenDaylight Developer Experience 2.0
OpenDaylight Developer Experience 2.0
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code less
 
IRJET- Polymer Javascript
IRJET- Polymer JavascriptIRJET- Polymer Javascript
IRJET- Polymer Javascript
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
 
ScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency InjectionScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency Injection
 
Angular2 with type script
Angular2 with type scriptAngular2 with type script
Angular2 with type script
 
Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2
 
Modern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design PatternsModern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design Patterns
 
Evolution of Patterns
Evolution of PatternsEvolution of Patterns
Evolution of Patterns
 
Use Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDEUse Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDE
 
Lunch and learn as3_frameworks
Lunch and learn as3_frameworksLunch and learn as3_frameworks
Lunch and learn as3_frameworks
 

Plus de Fabio Collini

Plus de Fabio Collini (20)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized project
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 
Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUK
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
 

Dernier

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 

Dernier (20)

%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 

Using Dagger in a Clean Architecture project

  • 1. Using Dagger in a Clean Architecture project Fabio Collini
  • 7. 1. Speeds up builds 2. Enable on demand delivery 3. Simplify development 4. Reuse modules across apps 5. Experiment with new technologies 6. Scale development teams 7. Enables refactoring 8. Simplifies test automation Modularization - Why you should care https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
  • 11. Main app BUILD SUCCESSFUL in 1m 12s 847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date Feature app BUILD SUCCESSFUL in 43s 504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date Feature module BUILD SUCCESSFUL in 30s 427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date Core module BUILD SUCCESSFUL in 22s 308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date Utils BUILD SUCCESSFUL in 6s 22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date Poorman'sbenchmark
  • 12. Main app BUILD SUCCESSFUL in 1m 12s 847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date Feature app BUILD SUCCESSFUL in 43s 504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date Feature module BUILD SUCCESSFUL in 30s 427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date Core module BUILD SUCCESSFUL in 22s 308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date Utils BUILD SUCCESSFUL in 6s 22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date Poorman'sbenchmark
  • 30. DaggerTip#1 @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .build() }1
  • 31. DaggerTip#1 Exception in thread "main" java.lang.IllegalStateException: Component1 must be set at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95) at DaggerComponent2$Builder.build(DaggerComponent2.java:35) @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .build() }1
  • 32. DaggerTip#1 Exception in thread "main" java.lang.IllegalStateException: Component1 must be set at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95) at DaggerComponent2$Builder.build(DaggerComponent2.java:35) @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .component1(DaggerComponent1.create()) .build() }1
  • 33. DaggerTip#1 @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 { @Component.Factory interface Factory { fun create(component1: Component1): Component2 }2 }3 fun main() { DaggerComponent2.factory() .create(DaggerComponent1.create()) }1
  • 34. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 Module1 pattern format
  • 35. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 @Component(modules = [Module1::class]) interface Component1 { val format: DecimalFormat }2 Module1 Component1 pattern format format
  • 36. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 @Component(modules = [Module1::class]) interface Component1 { val format: DecimalFormat }2 @Module class Module2 { }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { }4 Module2 Component2 Module1 Component1 pattern format format
  • 37. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 pattern format error: [Dagger/MissingBinding] String cannot be provided without an @Inject constructor or an @Provides-annotated method. public abstract interface Component2 { ^ String is injected at Module2.format(pattern) DecimalFormat is provided at Component2.getFormat()
  • 38. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 patternpattern format
  • 39. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 patternpattern format
  • 40. DaggerTip#3 interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4
  • 41. DaggerTip#3 interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 fun main() { val component1 = object : Component1 { override val pattern ="#,###.0000" }3 val component2 = DaggerComponent2.factory().create(component1) }4
  • 42. @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Lib
  • 43. @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Lib feature2 Lib feature1
  • 44. feature2 Lib feature1 @Component(dependencies = [LibComponent::class]) interface Feature1Component @Component(dependencies = [LibComponent::class]) interface Feature2Component @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 feat1feat2Lib
  • 45. feature2 Lib feature1 @Singleton @Component(dependencies = [LibComponent::class]) interface Feature1Component @Singleton @Component(dependencies = [LibComponent::class]) interface Feature2Component @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Libfeat1feat2
  • 46. feature2 Lib feature1 @Scope annotation class Feature1SingletonScope @Feature1SingletonScope @Component(dependencies = [LibComponent::class]) interface Feature1Component @Scope annotation class Feature2SingletonScope @Feature2SingletonScope @Component(dependencies = [LibComponent::class]) interface Feature2Component @Scope annotation class LibSingletonScope @Module class LibModule { @LibSingletonScope @Provides fun provideMyLibObject() = MyLibObject() }3 @LibSingletonScope @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Libfeat1feat2
  • 47. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1
  • 48. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 fun main() { val obj1 = DaggerLibComponent.create().myLibObject val obj2 = DaggerLibComponent.create().myLibObject println(obj1 === obj2) //false :( }2
  • 49. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 fun main() { val component = DaggerLibComponent.create() val obj1 = component.myLibObject val obj2 = component.myLibObject println(obj1 === obj2) //true :) }2
  • 50. interface LibComponentProvider { val libComponent: LibComponent }8 Lib
  • 51. interface Feature1Provider { val feature1Component: Feature1Component }7 interface LibComponentProvider { val libComponent: LibComponent }8 feature1Lib
  • 52. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 interface Feature1Provider { val feature1Component: Feature1Component }7 interface LibComponentProvider { val libComponent: LibComponent }8 feature1LibApp
  • 53. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1App
  • 54. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 55. interface ComponentHolder { val map: MutableMap<KClass<*>, Any> }1 open class ComponentHolderApp : Application(), ComponentHolder { override val map = mutableMapOf<KClass<*>, Any>() }2 inline fun <reified C : Any> ComponentHolder.getOrCreate(factory: () -> C): C = map.getOrPut(C::class, factory) as C A “real” implementation is available here: https://github.com/fabioCollini/CleanWeather/blob/dagger/kotlinUtils/ src/main/java/it/codingjam/cleanweather/kotlinutils/ComponentHolder.kt
  • 56. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 57. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 58. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = getOrCreate { }1 interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = getOrCreate { }2 Libfeature1Feature1AppApp
  • 59. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = getOrCreate { DaggerFeature1Component.factory() .create(libComponent()) }1 interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = getOrCreate { DaggerLibComponent.create() }2 Feature1App App Libfeature1
  • 60. class MyApp : ComponentHolderApp() class MyFeature1App : ComponentHolderApp() fun ComponentHolderApp.feature1Component() = getOrCreate { DaggerFeature1Component.factory() .create(libComponent()) }1 fun ComponentHolderApp.libComponent() = getOrCreate { DaggerLibComponent.create() }2 Libfeature1 Feature1App App
  • 61. @Feature1SingletonScope @Component(dependencies = [Lib1Component::class, Lib2Component::class]) interface Feature1Component { //... } DaggerTip#5 error: @Feature1SingletonScope Feature1Component depends on more than one scoped component: @dagger.Component(dependencies = {Lib1Component.class, Lib2Component.class}) ^ @Lib1SingletonScope Lib1Component @Lib2SingletonScope Lib2Component
  • 62. @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1Component { val myLibObject: MyLibObject }2 DaggerTip#5
  • 63. interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1ComponentImpl : Lib1Component DaggerTip#5
  • 64. @Scope annotation class Lib1SingletonScope @Module class Lib1Module { @Lib1SingletonScope @Provides fun provideMyLibObject() = MyLibObject() }1 interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1ComponentImpl : Lib1Component fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() } DaggerTip#5
  • 65. @Scope internal annotation class Lib1SingletonScope @Module internal class Lib1Module { @Lib1SingletonScope @Provides fun provideMyLibObject() = MyLibObject() }1 interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) internal interface Lib1ComponentImpl : Lib1Component fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() } DaggerTip#5
  • 66. DaggerTip#5 interface Lib1Component { val myLibObject: MyLibObject }2 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() }
  • 67. NODaggerTip interface Lib1Component { val myLibObject: MyLibObject }2 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() }
  • 68. NODaggerTip interface Lib1Component { val myLibObject: MyLibObject }2 private class Lib1ComponentImpl : Lib1Component { override val myLibObject by lazy { MyLibObject() }3 }4 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { Lib1ComponentImpl() }
  • 71. class UseCase @Inject constructor( val repository: Repository) { //... }9 domain domain repository Repository RepositoryImpl UseCase
  • 72. class UseCase @Inject constructor( val repository: Repository) { //... }9 @Component interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 73. class UseCase @Inject constructor( val repository: Repository) { //... }9 @Module class DomainModule { @Provides fun provideRepository(): Repository = RepositoryImpl() }1 @Component(modules = [DomainModule::class]) interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 74. @Component interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 75. interface DomainDependencies { val repository: Repository }3 @Component( dependencies = [DomainDependencies::class] )9 interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Dependencies Domain Component
  • 76. interface DomainDependencies { val repository: Repository }3 @Component( dependencies = [DomainDependencies::class] )9 interface DomainComponent { val useCase: UseCase }2 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 77. fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domain
  • 78. fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( map[DomainDependencies::class] as DomainDependencies )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domain
  • 79. class MyApp : ComponentHolderApp() { override fun onCreate() { map[DomainDependencies::class] = DaggerRepositoryComponent.create() }1 }21 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( map[DomainDependencies::class] as DomainDependencies )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domainApp
  • 80.
  • 81. interface DomainDependencies { val repository: Repository }2 @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 82. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 83. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 84. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies @InversionProvider fun ComponentHolderApp.provideDomainDependencies(): DomainDependencies = getOrCreate { DaggerRepositoryComponent.create() }6 domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 85. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies @InversionProvider fun ComponentHolderApp.provideDomainDependencies(): DomainDependencies = getOrCreate { DaggerRepositoryComponent.create() }6 domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 87. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 88. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 89. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 90. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() }4
  • 91. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { }2 }4
  • 92. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 }4
  • 93. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 @Test fun launchActivity() { whenever(useCaseMock.retrieve()) doReturn "ABCDEF" rule.launchActivity(null) onView(withId(R.id.text)) .check(matches(withText("ABCDEF"))) }3 }4
  • 94. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 @Test fun launchActivity() { whenever(useCaseMock.retrieve()) doReturn "ABCDEF" rule.launchActivity(null) onView(withId(R.id.text)) .check(matches(withText("ABCDEF"))) }3 }4
  • 95. Wrappingup Component dependencies Each module exposes: a Component interface a method to create the Component A map in the App can be useful to: manage singletons replace real objects with fakes/mocks
  • 96. Links Demo Project github.com/fabioCollini/CleanWeather/tree/dagger Droidcon Italy talk - SOLID principles in practice: the Clean Architecture youtube.com/watch?v=GlDsfq3xHvo&t= Implementing Dependency Inversion using Dagger components medium.com/google-developer-experts/implementing-dependency-inversion-using-dagger-components-d6b0fb3b6b5e Inversion library github.com/fabioCollini/Inversion Yigit Boyar, Florina Muntenescu - Build a Modular Android App Architecture (Google I/O'19) youtube.com/watch?v=PZBg5DIzNww Jeroen Mols - Modularization - Why you should care jeroenmols.com/blog/2019/03/06/modularizationwhy/ Ben Weiss - Dependency injection in a multi module project medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7 Marcos Holgado - Using Dagger in a multi-module project proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc