{"id":317,"date":"2018-04-27T17:35:00","date_gmt":"2018-04-27T16:35:00","guid":{"rendered":"https:\/\/codizon.com\/?p=317"},"modified":"2021-03-15T22:43:49","modified_gmt":"2021-03-15T21:43:49","slug":"how-to-reproduce-dagger-functions-or-develop-your-own-di-in-kotlin","status":"publish","type":"post","link":"https:\/\/codizon.com\/2018\/04\/how-to-reproduce-dagger-functions-or-develop-your-own-di-in-kotlin\/","title":{"rendered":"How to reproduce Dagger functions in Kotlin?"},"content":{"rendered":"\n
As an Android developer, you must have heard of Dagger. Moreover, you\u2019ve probably faced some difficulties with setting it up in Kotlin projects. What are the benefits of this library? Do we still need those Dagger\u2019s annotation processors and code generators? Being inspired by Antonio Leiva<\/a> great article on how to use Dagger 2 on Android With Kotlin<\/a> and Elye<\/a>\u2019s Dagger 2 for Dummies in Kotlin<\/a>, as well as relying on my own experience, I\u2019ll try to convince you that actually, we don\u2019t need them In this article, I\u2019ll show you how basic<\/span> Dagger features can be easily replaced.<\/p>\n\n\n\n The concept of skipping Dagger in Kotlin deserves its own name. Double \u201cg\u201d for the similarity with \u201cDagger\u201d and \u201cK\u201d as in \u201cKotlin\u201d. I called it Kagger, and <\/em>surprisingly,<\/em> in Danish kagger<\/em> means cakes <\/em>\ud83c\udf70. It\u2019s not a library, so assuming you\u2019ve already defined Kotlin dependencies, you have nothing to configure at all. Our solution will be pure-kotlin and therefore a piece of cake \ud83d\ude42<\/p>\n\n\n\n Just like in Dagger, we are using \u201ccomponents\u201d and \u201cmodules\u201d terms. The main difference is we split them to their interfaces<\/em><\/strong> and implementations<\/em><\/strong>. The following comparison should be self-explanatory.<\/p>\n\n\n\n In Dagger you would probably define module like:<\/p>\n\n\n\n When it comes to a component, you have to define a list of contained modules and specify which classes will be able to use that component:<\/p>\n\n\n\n Then you can build your component:<\/p>\n\n\n\n And finally you are able to inject your dependencies:<\/p>\n\n\n\n Note: In the newest version of Dagger, it\u2019s not necessary to access components by casting application property. I used it to make the example clear.<\/em><\/p><\/blockquote>\n\n\n\n How to achieve the same only with Kotlin?<\/p>\n\n\n\n Firstly, define module interface<\/strong>:<\/p>\n\n\n\n Please notice I\u2019ve replaced singleton declaration with a field. That\u2019s the way we emphasize the difference between singletons and non-singletons to avoid misleadings in the future. It\u2019s up to you if you follow this pattern or not. When the definition is done, let\u2019s make an module implementation<\/strong>:<\/p>\n\n\n\n What is more, for singleton field, lazy property<\/a> was used. It\u2019s worth to know it\u2019s thread-safe and we can be sure that object will be instantiated only once.<\/p>\n\n\n\n Let\u2019s move on to component interface:<\/p>\n\n\n\n It may seem familiar to Dagger component declaration. Again, apart from its name, we are declaring a list of modules that component should contain. It\u2019s not necessary to specify which classes will be able to \u201cinject\u201d those objects.<\/p>\n\n\n\n For similarity with above Dagger example, let\u2019s implement a component as an object expression:<\/p>\n\n\n\n Here we are using the true power of Kotlin \u2014 delegation. You may create separate class for component implementation as well:<\/p>\n\n\n\n So If you do so, the access to items looks like this:<\/p>\n\n\n\n As you can see above, we are \u201cinjecting\u201d items without annotation or lateinit keyword. Vals remains vals. In case you want to parameterize non-singleton objects, just add a parameter to a module function:<\/p>\n\n\n\n For testing purposes, you can create a mocked version of To help you with better understanding of this matter, I\u2019ve put a compilable project to GitHub repository<\/a>.<\/p>\n\n\n\n With the mechanism described above, we were able to remove simple Dagger dependencies in projects created for Indoorway<\/a>. What\u2019s more, Kagger may be used outside of Android projects. I\u2019m looking forward for your opinions and thoughts on this.<\/p>\n\n\n\n Featured in Android Weekly #307<\/a>. Originally posted on Medium<\/a>.<\/em><\/p>\n\n\n\nat all<\/s> sometimes. (Well, Dagger is a pretty powerful tool). <\/p>\n\n\n\nSetup<\/h2>\n\n\n\n
Components and modules<\/h2>\n\n\n\n
Dagger<\/h3>\n\n\n\n
@Module class AxModule(private val application: Application) {\n @Provides \n @Singleton \n fun provideSomeSingleton() = application\n .getSystemService(Context.POWER_SERVICE) as PowerManager\n \n @Provides \n fun provideSomeBean() = application\n .getSystemService(Context.LOCATION_SERVICE) as LocationManager\n}<\/code><\/pre>\n\n\n\n
@Singleton<\/code> annotation is used to mark single-instance beans, so function
provideSomeSingleton()<\/code> will be called only once. The second one,
provideSomeBean()<\/code> is not annotated as a singleton and will be invoked every time it\u2019s being injected. Let\u2019s imagine that we have another few modules, such as
AxModule<\/code>,
BxModule<\/code> and
CxModule<\/code>.<\/p>\n\n\n\n
@Singleton\n@Component(modules = arrayOf(AxModule::class, BxModule::class, CxModule::class))\ninterface MyComponent {\n fun inject(dummyActivity: DummyActivity)\n}<\/code><\/pre>\n\n\n\n
class MyApplication : Application() {\n \n val myComponent: MyComponent by lazy {\n DaggerMyComponent\n .builder()\n .axModule(AxModule(this))\n .bxModule(BxModule()) \n .cxModule(CxModule())\n .build()\n }\n \n}<\/code><\/pre>\n\n\n\n
class DummyActivity : AppCompatActivity() {\n\n @Inject lateinit var powerManager: PowerManager \/\/ singleton\n @Inject lateinit var locationManager: LocationManager \/\/ non-singleton\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_dummy)\n \n \/\/ there may be another ways to access myComponent\n (application as MyApplication).myComponent.inject(this)\n }\n \n}<\/code><\/pre>\n\n\n\n
PowerManager<\/code> and
LocationManager<\/code> will be accessible after
OnCreate<\/code> call.<\/p>\n\n\n\n
Kagger \/ Pure Kotlin<\/h3>\n\n\n\n
interface AxModule {\n val someSingleton: PowerManager\n fun someBean(): LocationManager\n}<\/code><\/pre>\n\n\n\n
class AxModuleImpl(private val application: Application) : AxModule {\n override val someSingleton by lazy {\n application.getSystemService(Context.POWER_SERVICE) as PowerManager\n }\n\n override fun someBean() = application\n .getSystemService(Context.LOCATION_SERVICE) as LocationManager\n}<\/code><\/pre>\n\n\n\n
interface MyComponent: AxModule, BxModule, CxModule<\/code><\/pre>\n\n\n\n
class MyApplication : Application() {\n\n val myComponent: MyComponent by lazy {\n object : MyComponent,\n AxModule by AxModuleImpl(this),\n BxModule by BxModuleImpl(),\n CxModule by CxModuleImpl() {}\n }\n \n}<\/code><\/pre>\n\n\n\n
MyComponent<\/code>interface extends
AxModule<\/code>,
BxModule<\/code> and
CxModule<\/code>. Thanks to the implementation by delegation<\/a> we are keeping those modules apart and we don\u2019t have to implement all methods and fields in one place. Each module can contain its own dependencies, as
AxModule<\/code> which uses
application<\/code>context.<\/p>\n\n\n\n
class MyComponentImpl(application: Application): MyComponent,\n AxModule by AxModuleImpl(application),\n BxModule by BxModuleImpl(),\n CxModule by CxModuleImpl()<\/code><\/pre>\n\n\n\n
MyApplication<\/code> class simplifies to:<\/p>\n\n\n\n
class MyApplication : Application() {\n val myComponent: MyComponent by lazy { MyComponentImpl(this) }\n}<\/code><\/pre>\n\n\n\n
class DummyActivity : AppCompatActivity() {\n private val component by lazy {\n \/\/ there might be a better way of accessing myComponent\n (application as MyApplication).myComponent\n }\n private val someSingleton by lazy { component.someSingleton }\n private val someBean by lazy { component.someBean() }\n \n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_dummy)\n \/\/ nothing to declare in onCreate\n }\n\n}<\/code><\/pre>\n\n\n\n
interface AxModule {\n val someSingleton: PowerManager\n fun someBeanWithParams(param: String): LocationManager\n}<\/code><\/pre>\n\n\n\n
MyComponent<\/code> and replace only necessary modules, methods or fields.<\/p>\n\n\n\n
Why does it matter?<\/h2>\n\n\n\n