Slide 1

Slide 1 text

SIMPLE, FAST, AND BOILERPLATE-FREE DI FOR KOTLIN TP3 & KTP https://github.com/stephanenicolas/toothpick/ K d_lemures stephanenicolas # ktp-droidconsf

Slide 2

Slide 2 text

THE TALK ▸ KTP basics • Modules & Bindings • Singletons ▸ KTP vs Dagger vs Koin • Performance • Ease of use ▸ Advanced features • Scopes • Lifecycle integration • ViewModel integration # ktp-droidconsf

Slide 3

Slide 3 text

KTP BASICS

Slide 4

Slide 4 text

INJECTING class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .inject(this) } } KTP BASICS # ktp-droidconsf

Slide 5

Slide 5 text

INJECTING class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .inject(this) } } KTP BASICS # ktp-droidconsf

Slide 6

Slide 6 text

INJECTING class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .inject(this) } } KTP BASICS # ktp-droidconsf

Slide 7

Slide 7 text

MAKING DEPENDENCIES INJECTABLE KTP BASICS # ktp-droidconsf @InjectContructor class NotificationHelper() { fun showImportantNotification(message: String) { Snackbar.make(message, Snackbar.LENGHT_LONG) .show() } }

Slide 8

Slide 8 text

MAKING DEPENDENCIES INJECTABLE KTP BASICS # ktp-droidconsf @InjectContructor class NotificationHelper() { fun showImportantNotification(message: String) { Snackbar.make(message, Snackbar.LENGHT_LONG) .show() } }

Slide 9

Slide 9 text

LAZY INJECTING class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by lazy() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .inject(this) } } KTP BASICS # ktp-droidconsf

Slide 10

Slide 10 text

MODULES & BINDINGS KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .installModules(module { bind().toClass() }) .inject(this) } }

Slide 11

Slide 11 text

MODULES & BINDINGS KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .installModules(module { bind().toClass() }) .inject(this) } }

Slide 12

Slide 12 text

MODULES & BINDINGS KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .installModules(module { bind().toClass() }) .inject(this) } }

Slide 13

Slide 13 text

INJECTING SINGLETONS KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .inject(this) } }

Slide 14

Slide 14 text

KTP BASICS # ktp-droidconsf @Singleton class NotificationHelper() { fun showImportantNotification(message: String) { Snackbar.make(message, Snackbar.LENGHT_LONG) .show() } } SINGLETONS BY ANNOTATIONS

Slide 15

Slide 15 text

SINGLETONS BY ANNOTATIONS KTP BASICS # ktp-droidconsf @Singleton class NotificationHelper() { fun showImportantNotification(message: String) { Snackbar.make(message, Snackbar.LENGHT_LONG) .show() } }

Slide 16

Slide 16 text

SINGLETONS BY BINDINGS KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .installModules(module { bind().singleton() }) .inject(this) } }

Slide 17

Slide 17 text

KTP BASICS # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .installModules(module { bind().singleton() }) .inject(this) } } SINGLETONS BY BINDINGS

Slide 18

Slide 18 text

KTP VS DAGGER VS KOIN

Slide 19

Slide 19 text

DI LIBS COMPARISON Runtime
 Performance Daggers KTP K Koin Speed wise, all libs have the same performance level. DAGGER VS KOIN VS KTP # ktp-droidconsf

Slide 20

Slide 20 text

DI LIBS COMPARISON DAGGER VS KOIN VS KTP Boiler-plate Daggers KTP K KTP requires less boiler plate than
 Koin (providers) and 
 Dagger (components, modules & tests). # ktp-droidconsf Koin

Slide 21

Slide 21 text

DI LIBS COMPARISON Kotlin API Friendliness & Ease of use Daggers KTP K Koin KTP is easier to learn.
 Dagger has no Kotlin API & it’s hard! DAGGER VS KOIN VS KTP # ktp-droidconsf

Slide 22

Slide 22 text

DI LIBS COMPARISON With KTP, Annotation Processing is incremental
 and has a limited impact on build times. Build
 Performance Daggers KTP K DAGGER VS KOIN VS KTP # ktp-droidconsf Koin

Slide 23

Slide 23 text

DI LIBS COMPARISON DI GRAPH VALIDATION With KTP, it is possible to validate the DI Graph at test-time,
 when Dagger does it at compile-time. # ktp-droidconsf @Test fun testInjections() { // GIVEN val activity = SimpleBackpackItemsActivity() // WHEN activity.injectDependencies() // GIVEN // if injection did NOT crash, the graph is valid }

Slide 24

Slide 24 text

DI LIBS COMPARISON ERRORS AND STACK TRACES KTP stack traces are easy to read 
 and understand ! # ktp-droidconsf at com.example.toothpick.model.Backpack.(Backpack.kt:11) at com.example.toothpick.model.Backpack__Factory.createInstance(Backpack__Factory.java:10) at com.example.toothpick.model.Backpack__Factory.createInstance(Backpack__Factory.java:7) at toothpick.InternalProvider.get(InternalProvider.java:134) at toothpick.InternalScopedProvider.get(InternalScopedProvider.java:80) at toothpick.ScopeImpl.getInstance(ScopeImpl.java:93) at toothpick.ktp.delegate.EagerDelegate.onEntryPointInjected(InjectDelegate.kt:50) at toothpick.ktp.delegate.DelegateNotifier.notifyDelegates(DelegateNotifier.kt:49) at toothpick.ktp.KTP$1.inject(KTP.kt:37) at toothpick.Toothpick.inject(Toothpick.java:243) at toothpick.ScopeImpl.inject(ScopeImpl.java:140) at com.toothpick.SimpleBackpackItemsActivity.injectDependencies(SimpleBackpackItemsActivity.kt:73) at com.toothpick.SimpleBackpackItemsActivityTest.testInjections(SimpleBackpackItemsActivityTest.kt:18)

Slide 25

Slide 25 text

ADVANCED FEATURES

Slide 26

Slide 26 text

# ktp-droidconsf ADVANCED FEATURES SCOPES ROOT SCOPE
 is living Injection is performed inside scopes.
 Scopes contain bindings and singletons. KTP.openRootScope() .installModules(module { bind().toClass<…>()
 bind().singleton() })
 .inject(this)

Slide 27

Slide 27 text

MULTIPLE SCOPES APPLICATION # ktp-droidconsf ACTIVITY 1
 is living Each scope will have its own bindings & singletons. ACTIVITY 1 ADVANCED FEATURES

Slide 28

Slide 28 text

# ktp-droidconsf APPLICATION FLOW .installModules(module { bind() .toClass() }) ACTIVITY 1 ACTIVITY 2 ACTIVITY 3 @FlowScope @Singleton data class ShoppingCart SCOPE TREE ADVANCED FEATURES

Slide 29

Slide 29 text

OPENING A SUBSCOPE # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .inject(this) } } ROOT MY ACTIVITY ADVANCED FEATURES

Slide 30

Slide 30 text

# ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .inject(this) } } ROOT MY ACTIVITY OPENING A SUBSCOPE ADVANCED FEATURES

Slide 31

Slide 31 text

MODULES & BINDINGS IN SCOPES # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: INotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .installModules(module { bind() .toClass() }) .inject(this) } } ROOT MY ACTIVITY ADVANCED FEATURES

Slide 32

Slide 32 text

# ktp-droidconsf SCOPE LIFECYCLE APPLICATION
 is living ACTIVITY 1
 is living ACTIVITY 2
 is living A scope is a space in memory 
 that is associated to a duration, it has a lifecycle. ADVANCED FEATURES

Slide 33

Slide 33 text

CLOSING A SCOPE # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .inject(this) } override fun onDestroy() { super.onDestroy() KTP.closeScope(this) } } ADVANCED FEATURES

Slide 34

Slide 34 text

CLOSING A SCOPE # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .inject(this) } override fun onDestroy() { super.onDestroy() KTP.closeScope(this) } } ADVANCED FEATURES

Slide 35

Slide 35 text

LIFECYCLE INTEGRATION # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .closeOnDestroy(this) .inject(this) } } ADVANCED FEATURES

Slide 36

Slide 36 text

LIFECYCLE INTEGRATION # ktp-droidconsf class MyActivity : AppCompatActivity() { val notificationHelper: NotificationHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(this) .closeOnDestroy(this) .inject(this) } } dependencies { implementation ‘com.github.stephanenicolas.toothpick:smoothie-lifecycle-ktp:X.Y.Z’ } ADVANCED FEATURES

Slide 37

Slide 37 text

VIEWMODEL INTEGRATION # ktp-droidconsf ADVANCED FEATURES class MyActivity : AppCompatActivity() { val myViewModel: MyViewModel by viewModels() }

Slide 38

Slide 38 text

VIEWMODEL INTEGRATION # ktp-droidconsf ADVANCED FEATURES class MyActivity : AppCompatActivity() { val myViewModel: MyViewModel by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(MyViewModel::class) { it.installViewModelBinding(this) } .inject(this) } }

Slide 39

Slide 39 text

VIEWMODEL INTEGRATION # ktp-droidconsf ADVANCED FEATURES class MyActivity : AppCompatActivity() { val myViewModel: MyViewModel by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(MyViewModel::class) { it.installViewModelBinding(this) } .inject(this) } }

Slide 40

Slide 40 text

VIEWMODEL INTEGRATION # ktp-droidconsf ADVANCED FEATURES class MyViewModel : ViewModel() { val notificationHelper: NotificationHelper by inject() } ViewModel will get its dependencies injected.

Slide 41

Slide 41 text

# ktp-droidconsf ADVANCED FEATURES VIEWMODEL INTEGRATION class MyActivity : AppCompatActivity() { val myViewModel: MyViewModel by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(MyViewModel::class) { it.closeOnViewModelCleared(this) } .inject(this) } }

Slide 42

Slide 42 text

# ktp-droidconsf ADVANCED FEATURES VIEWMODEL INTEGRATION dependencies { implementation ‘com.github.stephanenicolas.toothpick:smoothie-lifecycle—viewmodel—ktp:X.Y.Z’ } class MyActivity : AppCompatActivity() { val myViewModel: MyViewModel by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) KTP.openRootScope() .openSubScope(MyViewModel::class) { it.closeOnViewModelCleared(this) } .inject(this) } }

Slide 43

Slide 43 text

SIMPLE, FAST, AND BOILERPLATE-FREE DI FOR KOTLIN TP3 & KTP https://github.com/stephanenicolas/toothpick/ K d_lemures stephanenicolas # ktp-droidconsf