{"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 at all<\/s> sometimes. (Well, Dagger is a pretty powerful tool). <\/p>\n\n\n\n

In this article, I\u2019ll show you how basic<\/span> Dagger features can be easily replaced.<\/p>\n\n\n\n

Setup<\/h2>\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

Components and modules<\/h2>\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

Dagger<\/h3>\n\n\n\n

In Dagger you would probably define module like:<\/p>\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

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

@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

Then you can build your component:<\/p>\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

And finally you are able to inject your dependencies:<\/p>\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

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

Kagger \/ Pure Kotlin<\/h3>\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

interface AxModule {\n   val someSingleton: PowerManager\n   fun someBean(): LocationManager\n}<\/code><\/pre>\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

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

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

interface MyComponent: AxModule, BxModule, CxModule<\/code><\/pre>\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

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

Here we are using the true power of Kotlin \u2014 delegation. 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

You may create separate class for component implementation as well:<\/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

So 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

If you do so, the access to items looks like this:<\/p>\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

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

interface AxModule {\n   val someSingleton: PowerManager\n   fun someBeanWithParams(param: String): LocationManager\n}<\/code><\/pre>\n\n\n\n

For testing purposes, you can create a mocked version of MyComponent<\/code> and replace only necessary modules, methods or fields.<\/p>\n\n\n\n

To help you with better understanding of this matter, I\u2019ve put a compilable project to GitHub repository<\/a>.<\/p>\n\n\n\n

Why does it matter?<\/h2>\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\n

\n
\n
<\/div>\n\n\n\n
\"\"<\/figure><\/div>\n\n\n\n
<\/div>\n<\/div><\/div>\n\n\n\n\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"

How basic Dagger features can be easily replaced in pure kotlin?<\/p>\n","protected":false},"author":1,"featured_media":386,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":""},"categories":[79,78,82],"tags":[],"_links":{"self":[{"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/posts\/317"}],"collection":[{"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/comments?post=317"}],"version-history":[{"count":36,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/posts\/317\/revisions"}],"predecessor-version":[{"id":1121,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/posts\/317\/revisions\/1121"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/media\/386"}],"wp:attachment":[{"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/media?parent=317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/categories?post=317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codizon.com\/wp-json\/wp\/v2\/tags?post=317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}