Доклад Антона Минашкина для Съесть собаку #15, 27/11/18
Тезисы:
- Почему DI – такой популярный design pattern в Android;
- Что особенного в DI для Kotlin;
- Практическая польза и опции DI.
3. Problem Statement
public class SomeController {
public final BusinessLogic logic;
public SomeController(BusinessLogic logic) {
this.logic = logic;
}
public void callMe() {
//some stuff
logic.doSomeStuff();
//some other stuff
}
}
4. Problem Statement
public class SomeController {
public final BusinessLogic logic;
public SomeController(BusinessLogic logic) {
this.logic = logic;
}
public void callMe() {
//some stuff
logic.doSomeStuff();
//some other stuff
}
}
6. Problem Statement
public class SomeController {
public final BusinessLogic logic;
@Inject
public SomeController(BusinessLogic logic) {
this.logic = logic;
}
public void callMe() {
//some stuff
logic.doSomeStuff();
//some other stuff
}
}
7. Problem Statement
public class SomeController {
public final BusinessLogic logic;
@Inject
public SomeController(BusinessLogic logic) {
this.logic = logic;
}
public void callMe() {
//some stuff
logic.doSomeStuff();
//some other stuff
}
}
17. Dagger 2 Summary
● Good for migration from Java
● Everybody know it
● The fastest DI ever (probably)
● Oldschool
18. Problem Statement
What if we will be able to initialize immutable variables not only in the constructor?
19. Problem Statement
What if we will be able to initialize immutable variables not only in the constructor?
How to do DI in a ideal Android world with Kotlin?
20. Problem Statement
public class SomeActivity: Activity {
val logic: BusinessLogic by inject()
public void callMe() {
//some code
logic.doSomeStuff();
//some other code
}
}
21. Problem Statement
public class SomeActivity: Activity {
val logic: BusinessLogic by inject()
public void callMe() {
//some code
logic.doSomeStuff();
//some other code
}
}
23. Kodein
“Kodein is a very useful dependency retrieval container, it is very easy to use and
configure.”
24. Kodein
val kodein = Kodein {
bind<MyDataSource>() with factory { url: MyDataSource -> MyDataSource(url) }
bind<MyDataSource>("InMemory") with provider { MyDataSource(inMemory: true) }
bind<MyDataSource>("Mock") with singleton { MockDataSource() }
constant("samsung") with "(╯°□°)╯︵ ┻━┻"
}
25. Kodein
val kodein = Kodein {
bind<MyDataSource>() with factory { url: MyDataSource -> MyDataSource(url) }
bind<MyDataSource>("InMemory") with provider { MyDataSource(inMemory: true) }
bind<MyDataSource>("Mock") with singleton { MockDataSource() }
constant("samsung") with "(╯°□°)╯︵ ┻━┻"
}
26. Kodein
val kodein = Kodein {
bind<MyDataSource>() with factory { url: MyDataSource -> MyDataSource(url) }
bind<MyDataSource>("InMemory") with provider { MyDataSource(inMemory: true) }
bind<MyDataSource>("Mock") with singleton { MockDataSource() }
constant("samsung") with "(╯°□°)╯︵ ┻━┻"
}
27. Kodein
val kodein = Kodein {
bind<MyDataSource>() with factory { url: MyDataSource -> MyDataSource(url) }
bind<MyDataSource>("InMemory") with provider { MyDataSource(inMemory: true) }
bind<MyDataSource>("Mock") with singleton { MockDataSource() }
constant("samsung") with "(╯°□°)╯︵ ┻━┻"
}
28. Kodein
val kodein = Kodein {
bind<MyDataSource>() with factory { url: MyDataSource -> MyDataSource(url) }
bind<MyDataSource>("InMemory") with provider { MyDataSource(inMemory: true) }
bind<MyDataSource>("Mock") with singleton { MockDataSource() }
constant("samsung") with "(╯°□°)╯︵ ┻━┻"
}
29. Kodein: Retrieval
val diceFactory: (Int) -> Dice = kodein.factory()
val dataSource: MyDataSource = kodein.instance()
val randomProvider: () -> Random = kodein.provider()
val someConstant: String = kodein.instance("samsung")
30. Kodein: Retrieval
val diceFactory: (Int) -> Dice = kodein.factoryOrNull()
val dataSource: MyDataSource = kodein.instanceOrNull()
val randomProvider: () -> Random = kodein.providerOrNull()
val someConstant: String = kodein.instanceOrNull("samsung")
31. Kodein: Being Kodein aware
class MyManager(override val kodein: Kodein) : KodeinAware {
val datasource: DataSource = instance()
val random: Random = instance()
}
32. Kodein: Via lazy properties
class Controller(private val kodein: Kodein) {
private val dataSource: MyDataSource by kodein.lazy.instance()
private val randomProvider: () -> Random by kodein.lazy.provider()
private val someConstant: String by kodein.lazy.instance("samsung")
}
33. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
34. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
35. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
36. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
37. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
38. Kodein: Via an injector
class Controller() {
private val injector = KodeinInjector()
private val dataSource: MyDataSource by injector.instance()
private val randomProvider: () -> Random by injector.provider()
private val someConstant: String by injector.instance("samsung")
private val kodein by injector.kodein()
fun whenReady(kodein: Kodein) = injector.inject(kodein)
}
KodeinInjector.UninjectedException
39. Kodein: Android
“Kodein does work on Android as-is. The kodein-android extension adds
multiple android-specific utilities to Kodein.
Using or not using this extension really depends on your needs.”
40. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
41. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
● Scopes
42. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
● Scopes
● Auto Scopes
43. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
● Scopes
● Auto Scopes
● Modules
44. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
● Scopes
● Auto Scopes
● Modules
45. Kodein: Other stuff
● Deep integration with Android if needed (interfaces, base classes)
● Scopes
● Auto Scopes
● Modules
50. Koin
“A small library for writing dependency injection in a concise and pragmatic way.
No proxy, no code generation, no introspection. Just DSL and functional Kotlin
magic!”
51. Koin: DSL
applicationContext - create a Koin Module
factory - provide a factory bean definition
bean - provide a bean definition
bind - additional Kotlin type binding for given bean definition
53. Koin: Provide keyword
Parameters:
● name (optional, default value is “”) - name for that bean definition (used for
name resolution)
● isSingleton (optional, default value is true) - define if the bean definition
will be singleton or factory
● definition - a lambda function, describing how to build the component - This
function generally call the constructor of the class component
54. Koin: Provide keyword
val myModule = applicationContext {
provide { MyComponent() } //just call the constructor of MyComponent
provide("MyCmp") { MyComponent() } //will be resolved with "MyCmp" name
provide(isSingleton = false) { MyComponent() } //default true
}
55. Koin: Binding additional types
val myModule = applicationContext {
// Define bean with type MyDataSource and additional type DataSource
provide { MyDataSource() } bind DataSource::class
}
OR
val myModule = applicationContext {
// Define bean with type DataSource
provide { MyDataSource() as DataSource }
}
58. Koin: The Very Start Function
class MyApplication : Application(){
override fun onCreate() {
super.onCreate()
// Start Koin
startKoin(this, listOf(myModule))
}
}
59. Koin: Properties
Koin will check if koin.properties is available, to load your properties. It has to be
in src/main/resources Kotlin folder or asset Android folder
OR/AND
startKoin(listOf(myModule),properties = HashMap(...properties))
60. Koin: Injecting
● Use a adapted Koin module, to help you inject into runtime components
(Activity, Fragments, Controller …)
● Tag a component as KoinComponent. In Android, the following classes have
already KoinComponent features: Application,Context, Activity,
Fragment, Service
61. Koin: Working with Contexts
A context is a logical subset of bean definitions inside a module:
val myModule = applicationContext {
context("myContext"){
bean { MyComponentA() }
bean { MyComponentB(get()) }
}
}
62. Koin: Working with Contexts
A context is a logical subset of bean definitions inside a module:
val myModule = applicationContext {
context("myContext"){
bean { MyComponentA() }
bean { MyComponentB(get()) }
}
}
63. Koin: Releasing Contexts
abstract class MyCustomActivity : AppCompatActivity() {
abstract val contextName: String
override fun onPause() {
releaseContext(contextName)
super.onPause()
}
}
65. Koin Android: DSL
Access all the Koin DSL, plus:
● androidApplication() - resolve Application type dependency
● viewModel - declare a ViewModel component (koin-android-architecture
only)
66. Koin Android: Injection
● get() get desired dependency
● by inject() lazy delagate to inject the desired dependency
● by property() lazy inject property value
● releaseContext() release the given context by its name
67. Koin Android: ViewModel
In Activity or Fragment:
● getViewModel() fetch given ViewModel dependency
● by viewModel() lazy delegate to inject the ViewModel
In Fragment:
● Fragment.getSharedViewModel() fetch given ViewModel dependency -
shared with Activity
● Fragment by sharedViewModel() lazy delegate to inject the ViewModel -
shared with Activity
68. Koin Android: ViewModel
class MyActivity : AppCompatActivity(){
override fun onCreate() {
super.onCreate()
val myModel = getViewModel<MyModel>()
}
}
OR
class MyActivity : AppCompatActivity(){
val myModel by viewModel<MyModel>()
}
69. Koin Android: ViewModel
class MyFragment : Fragment(){
val myModel by viewModel<MyModel>()
}
OR
class MyFragment : Fragment(){
// Create new instance
val myModel by viewModel<MyModel>(fromActivity = false)
}