light-weight KOtlin Dependency Injection (KODI)

Overview

KODI

KOtlin Dependency Injection (KODI)

Kotlin 1.5.30 Awesome Kotlin Badge Codacy Badge

This is simple and useful dependency injection framework for work with your regular projects. It use standart Kotlin language construction like literal function with recieve, infix function, hight-order function, ets. to bind and inject dependencies into your objects. It has two packages:

  1. Kodi library

It's a dependency injection library writen in pure Kotlin. This library has intuitive sintax like:

bind<SomeClassInterface>() at "Any_String_Scope_Name" with singleton { SomeClassImplementation() }

No annotation processing, and reflection in core module.

You can bind three type of instances:

  • singleton: only one instance will be used. It's lazy instantiating
  • provider: it's create instance every time it's called. Lazy also!
  • constant: constant value by tag. It's create when it binded

In order to get a value or add a new one to the dependency graph, you need to call the kodi {} function from any part of your program. Recomendation to call it for binding at MainApplication.kt class. You can use val myModule = kodiModule { bind<ISomeInstance> with ...} for separate and organize independent component of your programm. Just call kodi {import(myModule) } to bind all dependencies from it. Keywords:

  • bind: bind string tag or generic type with given provided instance
  • bindType: bind inherit type with given provided instance
  • at: add instance at scope with string name
  • kodiModule { }: instantiate module for dependency separation
  • withScope: can used only with kodiModule for bind all instances at selected scope. If there is any scope binding inside this module, it will be used as main scope for this bindings
fun main(args: Array<String>) {
    // module with custom scope
    val kodiModule = kodiModule {
        bind<ISingleInterface>() with single { SingleClass(UUID.randomUUID().toString()) }
        // this will be use a `MY_PROVIDER_SCOPE_NAME` scope (since version 1.3.+)
        bind<IProviderInterface>() at MY_PROVIDER_SCOPE_NAME with provider { ProviderClass(UUID.randomUUID().toString()) }
	// since version 1.2.7
	bindType<ISingleInterface, AnotherSingleClass>() with single { AnotherSingleClass(UUID.randomUUID().toString()) }
        bindType<ISingleInterface, OneMoreSingleClass>() with single { OneMoreSingleClass(UUID.randomUUID().toString()) }
    } withScope MY_SINGLE_SCOPE_NAME.asScope()

    kodi {
        // Import module
        import(kodiModule)
        // bind constant value
        bind<String>(SOME_CONSTANT_TAG) with constant { "Hello" } at scope(CONSTANTS_SCOPE)
        // bind singleton value with lazy receiver properties
        bind<IInjectable>() with single { InjectableClass(instance(), instance()) } at MY_SINGLE_SCOPE_NAME.asScope()
        
	// Multi type instance from inherits (since 1.2.7)
        val anotherInstance: ISingleInterface = instance<AnotherSingleClass>()
        anotherInstance.printName()
        // Call Instance of the same interface but another implementation
        val oneMoreInstance: ISingleInterface = instance<OneMoreSingleClass>()
        oneMoreInstance.printName()
	
	// get value
        val singleInstance = instance<ISingleInterface>()
        singleInstance.printName()
        // immutable variable
        val providerImmutableLazyInstance by immutableInstance<IProviderInterface>()
        // mutable variable
        var providerMutableLazyInstance by mutableInstance<IProviderInterface>()
        val injectableSinge = instance<IInjectable>()
        // another instance call
        instance<IProviderInterface>().printName()
        // call immutable variable
        providerImmutableLazyInstance.printName()
        // call mutable variable
        providerMutableLazyInstance.className()
        providerMutableLazyInstance = ProviderClass("Another_id", "another name")
        //call singleton class as lazy 
        injectableSinge.providerInstance.printName()
        injectableSinge.singleInstance.printName()
        // call of mutable instance
        providerMutableLazyInstance.printName()

        // unbind instance
        unbind<IProviderInterface>()

        //change instance scope
        holder<IInjectable>() at MY_PROVIDER_SCOPE_NAME.asScope()

        // unbind all scope of instances and removed it from dependency graph
        unbindScope(MY_SINGLE_SCOPE_NAME.asScope())
    }
}

interface IPrintable {
    val id: String
    val name: String
    fun printName() {
        println("-----> Class name: ${this.name} withScope id: $id")
    }
}

interface ISingleInterface : IPrintable
data class SingleClass(override val id: String, override val name: String = "Single") : ISingleInterface
data class AnotherSingleClass(override val id: String, override val name: String = "Another") : ISingleInterface
data class OneMoreSingleClass(override val id: String, override val name: String = "OneMore") : ISingleInterface

interface IProviderInterface : IPrintable, Cloneable
data class ProviderClass(override val id: String, override val name: String = "Provider") : IProviderInterface

interface IInjectable {
    val singleInstance: ISingleInterface
    val providerInstance: IProviderInterface
}

data class InjectableClass(
        override val providerInstance: IProviderInterface,
        override val singleInstance: ISingleInterface
) : IInjectable

See com.mincor.kodiexample.KodiTest.kt for full example.

  1. KodiReflect library

It's use a kotlin reflect library to create instances and inject parameters into constructor. For start using this you need to implement IKodi interface to take all features of injection, like lazy property initialization or direct access to instances of given generic classe. It contains some functionality like:

  1. single() - create the one and only one instace of given generic class and save it in instanceMap
  2. instance() - simple create an instance of given generic class
  3. instanceByTag() - create an instance of given generic class and save it to instanceMap
  4. provider() - it's create or give an holder class for your functions and save it into instanceMap to call later
  5. providerCall() - call the providing function with tag, function and params
  6. providerCallByTag() - call already saved provider by tag and params
  7. bind<Interface, Implementation>(... params) - you can use binding function to map your interfaces to instance in just one line of code (Sinse version 0.1.5)
  8. constant(TAG, VALUE) - map constants to tag (Sinse version 0.1.5)
  9. singleProvider(block: () -> T): T - to initilize instance by yourself (since version 0.1.9)

You can initialize you instances, single, providers, binding and constants in just one place: val kodi = kodi { }

in every function you can pass params for injection and put a tag to get it later. You can use lazy instantiating by extension functions

  • singleLazy
  • providerLazy
data class UserData(val id:String, val name:String, val email:String)

data class Post(val id:String = "", val user:UserData? = null, val title:String = "", val desc:String = "")

interface IUserData {
        fun getData():String
    }
    
class UserDataInstance : IUserData {
        override fun getData():String {
            return "HELLO WORLD"
        }
    }

class MainActivity : AppCompatActivity(), IKodi {

 companion object {
        const val TAG_FUN_WITH_PARAMS = "fun_with_params"
        const val TAG_FUN_WITHOUT_PARAMS = "fun_without_params"
        const val TAG_FUN_FOR_INIT = "fun_for_init"

        private const val INSTANCE_TAG = "simple_tag"

        const val MY_GLOBAL_CONST = "global_const"
    }
    
    // now you can initialize Kodi and all the injected dependencies in one place
    val kodi = initKODI {
        single<UserData>(UUID.randomUUID().toString(), "Aleksandr", "[email protected]")

        bind<IUserData, UserDataInstance>()

        constant(MY_GLOBAL_CONST, "https://myamazingaddress.com")

        provider(TAG_FUN_FOR_INIT, ::checkInstanceWithTag)
    }
    
   ///-------- LAZY VAL SECTION ----////
    private val singleInstance: UserData by singleLazy(UUID.randomUUID().toString(), "Aleksandr", "[email protected]")
    private val providerWithReturnAndParams by providerLazy("", ::funcForProviderLazy, UUID.randomUUID().toString())
    private val userDataInstance:IUserData by singleLazy()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        println("-----> SINGLE INSTANCE '${singleInstance.name}'")
        val posts = mutableListOf<Post>()
        (0..25).forEach {
            // Only instances with 'var' constructor params can be ovveriden by input params if another case check
            posts.add(instance(UUID.randomUUID().toString(), single<UserData>(listOf("1", "2", "3", "4", "5", "6")), "Title $it", "Desc $it"))
        }

        println("-----> INTERFACE BINDING TEST DATA ${userDataInstance.getData()}")
        println("-----> CONSTANTS TEST  ${constant<String>(MY_GLOBAL_CONST)}")

        val providerData = providerWithReturnAndParams.call()
        println("-----> PROVIDER LAZY DATA '${providerData.name}' and email = '${providerData.email}'")

       // val instanceWithTag = instanceByTag<Post>(INSTANCE_TAG, UUID.randomUUID().toString(), singleInstance, "Title with tag", "Desc with tag")
       // println("-----> INSTANCE WITH TAG TITLE '${instanceWithTag.title}'")

        // call the function without params
        providerCall(TAG_FUN_WITHOUT_PARAMS, ::funcWithoutParams)
        // call the function with params
        providerCall(TAG_FUN_WITH_PARAMS, ::funcWithParams, posts)

        /**
         * If you do not set the incoming function parameters it throw with RuntimeException
         */
        // check for singleInstance user
        val singleUser = single<UserData>()
        val providerWithReturns = providerCall("fun_with_return", ::funcWithReturnAndParams, singleUser)
        println("-----> RETURN FROM FUNC '$providerWithReturns'")

        /**
         * Call the function that we bind early in initKODI section
         */
        providerCall<Unit>(TAG_FUN_FOR_INIT)

        /**
         * Call already bounded function with new params
         */
        providerCallByTag<String>(TAG_FUN_WITH_PARAMS, single<UserData>())

        //call provider with RuntimeException, cause there is no providing function gives
        //providerCallByTag<String>("provider_call_without nothing", "HEHE")
        println("-----> END OF PRINTING")
    }
    
    private fun checkInstanceWithTag() {
        println("-----> HELLO instance with tag and desc = '${instanceByTag<Post>(INSTANCE_TAG).desc}'")
    }

    fun funcForProviderLazy(id:String):UserData {
        return instance(id, "Vasya", "[email protected]")
    }

    fun funcWithoutParams() {
        println("-----> IM WITHOUT PARAMS")
    }

    fun funcWithParams(posts:Any) {
        println("-----> IM WITH PARAMS '$posts'")
    }

    fun funcWithReturnAndParams(user:UserData):String = user.id

    override fun onDestroy() {
        // clear all saved instances
        removeAll()
        super.onDestroy()
    }
}

Gradle:

build.gradle {
maven { url = "https://jitpack.io" }
}

// Standart Library
implementation 'com.github.Rasalexman.KODI:kodi:x.y.z'

// AndroidX Module
implementation 'com.github.Rasalexman.KODI:kodiandroidx:x.y.z'

// Annotation processing
kapt 'com.github.Rasalexman.KODI:kodigen:x.y.z'

// Old Reflection Library. It's a final version and i don't have any plans to support it in the future.
implementation 'com.github.Rasalexman.KODI:kodireflect:x.y.z'

License

MIT License

Copyright (c) 2021 Aleksandr Minkin ([email protected])

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

You might also like...
A light Sina Weibo client for Android
A light Sina Weibo client for Android

BlackLight 新浪微博客户端 请注意,当前BlackLight的开发已经被冻结,直到2016年高考后重启。 The development progress has been FROZEN till June 2016. 详细介绍以及Changelog请见Play商店。 https://pl

The Free & Open Source Android Application for reading (Light) Novels
The Free & Open Source Android Application for reading (Light) Novels

Shosetsu The Free & Open Source Android Application for reading (Light) Novels! We appreciate any help in improving the app, so let us know by opening

🏛 ThinkRchive Light Dark An app showing all details for various Lenovo Thinkpad models.
🏛 ThinkRchive Light Dark An app showing all details for various Lenovo Thinkpad models.

🏛 ThinkRchive Light Dark An app showing all details for various Lenovo Thinkpad models. Made to try out Jepack Compose for Android. This repo is a Mu

Flym News Reader is a light Android feed reader (RSS/Atom)

BEWARE: The original developer stopped the development of the app so I'm starting to work on it to improve it and maintain it. Flym News Reader Light

An app that allows you to search for Github profiles of users and their repositories [d.light Android Engineer Test], for the Android Engineer Role
An app that allows you to search for Github profiles of users and their repositories [d.light Android Engineer Test], for the Android Engineer Role

An app that allows you to search for Github profiles of users and their repositories [d.light Android Engineer Test], for the Android Engineer Role

FDPClient-EDITED - A free mixin-based injection hacked-client for Minecraft using Minecraft Forge based on LiquidBounce

FDPClient A free mixin-based injection hacked-client for Minecraft using Minecra

This is the first goland plugin for SCA of Go. It focuses on the dependency security of the Go project.
This is the first goland plugin for SCA of Go. It focuses on the dependency security of the Go project.

This is the first goland plugin for SCA of Go. It focuses on the dependency security of the Go project. It will generate the SCA report for the dependencies with vulnerabilities. For the detailed introduction of this plugin, please refer to this article.

A full-stack application showing the power 💪 of KOTLIN. Entire android app + backend Apis written in Kotlin 🔥
A full-stack application showing the power 💪 of KOTLIN. Entire android app + backend Apis written in Kotlin 🔥

Gamebaaz 🎮 A full-stack application showing the power 💪 of KOTLIN. Entire android app + backend Apis written in Kotlin 🔥 Android Backend Jetpack Co

An simple image gallery app utilizing Unsplash API to showcase modern Android development architecture (MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit)
An simple image gallery app utilizing Unsplash API to showcase modern Android development architecture (MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit)

Imagine App An simple image gallery app utilizing Unsplash API. Built with ❤︎ by Wajahat Karim and contributors Features Popular photos with paginatio

Releases(1.6.6)
  • 1.6.6(Aug 4, 2022)

  • 1.6.4(Jun 10, 2022)

  • 1.6.3(Apr 16, 2022)

    release version 1.6.3

    • update kotlin version to 1.6.20
    • update libs versions.gradle.kts
    • change InstanceType checking in Kodi.instance to 'is'
    Source code(tar.gz)
    Source code(zip)
  • 1.6.2(Mar 14, 2022)

    release version 1.6.2

    • fix some bugs with many annotations in line
    • fix check annotations on compile time

    thanks to @iterus0 for contribute

    Source code(tar.gz)
    Source code(zip)
  • 1.6.1(Mar 2, 2022)

  • 1.5.24(Mar 1, 2022)

  • 1.5.23(Feb 24, 2022)

  • 1.5.22(Feb 1, 2022)

    up version to 1.5.22

    • immutableInstance and mutableInstance now public global high order functions
    • kodiproccessor now has type as isolating incremental processing
    Source code(tar.gz)
    Source code(zip)
  • 1.5.21(Jan 17, 2022)

  • 1.5.20(Nov 18, 2021)

  • 1.5.19(Oct 15, 2021)

  • 1.5.18(Sep 18, 2021)

  • 1.5.17(Aug 22, 2021)

  • 1.5.16(Aug 20, 2021)

  • 1.5.15(Jul 26, 2021)

    up version to 1.5.15

    • up kotlin ver to 1.5.21
    • choose processor type to org.gradle.annotation.processing.isolating
    • refactor core processor code
    • now processor calling only once in build process
    Source code(tar.gz)
    Source code(zip)
  • 1.5.14(May 8, 2021)

  • 1.5.13(May 8, 2021)

  • 1.5.12(May 8, 2021)

  • 1.5.11(May 8, 2021)

  • 1.5.1(May 8, 2021)

  • 1.5.0(Apr 4, 2021)

    Refactoring of core data structures. Much more memory management and speed up. New functionalities for adding binding/unbinding listeners

    • addBindingListener / removeBindingListener
    • addUnbindingListener / removeUnbindingListener
    • hasBindingListener / hasUnbindingListener
    Source code(tar.gz)
    Source code(zip)
  • 1.4.93(Mar 21, 2021)

    Update kotlin version to 1.4.31 Reupload jars and aar to bintray

    Note: Just because bintray stops to submit libraries since 1 May. There will be another repository soon. Stay tuned

    Source code(tar.gz)
    Source code(zip)
  • 1.4.9(Sep 17, 2020)

  • 1.4.6(May 16, 2020)

  • 1.4.3(May 5, 2020)

  • 1.4.2(Apr 29, 2020)

    Added code generation for abstract and static classes and functions for BindSingle and BindProvider annotations

    Example:

    1. Create an instance with the provider for class reference
    interface IAnotherClass
    interface IMyClass : IAnotherClass
    
     @BindProvider(toClass = IAnotherClass::class)
     class MyClass() : IMyClass
    

    It is generate Providing method for binding like: bind<IAnotherClass>(tag = [toTag]) at [atScope] with provider { MyClass() }

    1. Binding for singleton object functions also for hight-order functions
     object CustomObject {
          @BindProvider(toClass = IAnotherClass::class)
          fun providingMethod(input: IMyClass): IAnotherClass {
             return input
          }
     }
    

    It is generate Providing method for binding like:

    bind<IAnotherClass>(tag = [toTag]) at [atScope] with provider { CustomObject.providingMethod(input = instance<IMyClass>()) }
    
    1. for interfaces and abstract classes it always will generate instance<T>():
    @BindProvider(toClass = IMyClass::class)
    interface IAbstractInterface : IMyClass
    

    bind<IMyClass>(tag = [toTag]) at [atScope] with provider { instance<IAbstractInterface>() }

    1. for abstract functions processor always generate instance().funName() like:
    interface IAbstractInterface {
         @BindProvider(toClass = IAnotherClass::class)
         fun getMyAbstractInstance(input: IAbstractInterface): IMyClass
    }
    

    bind<IAnotherClass>(tag = [toTag]) at [atScope] with provider { instance<IAbstractInterface>().getMyAbstractInstance( input = instance<IAbstractInterface>() ) }

    1. Added new annotation called WithInstance Tell the annotation processor to bind field or property with corresponding kodi instance
    @BindSingle(toClass = IMyClass::class)"
     class MyClass(
                   @WithInstance(
                       tag = "myAwesomeTag",
                       scope = "MyAwesomeScope",
                       with = "IDefaultClass::class"
                       )
                  myDefaultValue: IDefaultClass
              ) : IMyClass
    

    It's generate single binding function with default instance paramater

    bind<IMyClass>() with single {
              MyClass(
                    myDefaultValue = instance<IDefaultClass>(
                                                            tag = "myAwesomeTag", 
                                                            scope = "MyAwesomeScope"
                                                   )
               )
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 1.4.1(Apr 28, 2020)

    Major release with big new functionality. Was added Annotation processor in the new module - kodigen. New annotation in the base package - kodi.annotations

    1. BindSingle and BindProvider annotations can be used with classes and static functions only. It generates a kodiModule file that contains all necessary bindings.
    2. IgnoreInstance - ignore some constructor and methods parameters but be you must specify it by default
    3. WithInstance - tell the processor to get an instance of the parameter with tag or scope

    This is a first release of the annotation processor and it's will be improved later.

    1. add maven { url = "https://dl.bintray.com/sphc/KodiGen" } in your project.gradle file in repository section
    2. to use annotation processor need to include kapt plugin: apply plugin: 'kotlin-kapt' or with GradleDSL - plugin { kotlin("kapt")}
    3. app.gradle dependency block: kapt("com.rasalexman.kodi:kodigen::x.y.z")
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Mar 29, 2020)

  • 1.3.0(Mar 15, 2020)

    RELEASE 1.3.0

    • Kotlin 1.3.70
    • Removed unused and deprecated methods
    • Added full SCOPE support.
    • Kotlin Gradle DSL
    • KodiAndroidX module with the latest Kodi version 1.3.0
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0-rc4(Feb 5, 2020)

Owner
Alexandr Minkin
Lead Software Developer - Kotlin - Swift - Java - C#
Alexandr Minkin
HiltTest - Simple hilt dependency injection android kotlin

Using Hilt in your Android app This folder contains the source code for the "Usi

Anbarasu Chinna 1 Jun 3, 2022
Kotlin-Hilt - Hilt ile Dependency Injection

Kotlin-Hilt Hilt ile Dependency Injection Dependency Injection(DI), programlamad

null 2 Jan 1, 2023
MovieStreaming - Movie Streaming is a streaming app and developed with Kotlin and Koin Dependency injection

MovieStreaming Movie Streaming is a streaming app and developed with Kotlin and

Amir Ali 3 Nov 19, 2022
ToDo list is a sample project for save task and complete they. developed with Kotlin , Coroutins and Dagger-Hilt Dependency injection.

ToDo list is a sample project for save task and complete they. developed with Kotlin , Coroutins and Dagger-Hilt Dependency injection.

Seyyed Ali Tabatabaei 8 Dec 28, 2022
The News App has been carried out within the framework of the MVVM architecture, information about news is obtained by consulting an API, it is built usisng Jetpack Copose, Coroutines, Dependency Injection with Hilt and Retrofit

Journalist The News App consists of an application that displays the latest news from EEUU from an API that provides official and updated information.

null 0 Nov 3, 2021
An app which displays questions from Stack Exchange from it's api. Can search questions with tags as well. Uses MVVM architecture, dependency injection, coroutines, retrofit2 for network calls

Stack Exchange app What the app does? Shows a list of trending questions from stack exchange api Can search for the desires question. Can add tags to

null 0 Apr 27, 2022
All-money-converter-app - A currency converter App built a free Currency API Dependency injection

This a currency converter App built a free Currency API Dependency injection . T

Espérant GADA 0 Feb 3, 2022
An android application to make students life easier by reducing their backpack weight.

Smart-Schooling An android application to make students life easier by reducing their backpack weight. Dont forget to ⭐ the repo Overview Almost every

Thamizh Kaniyan 3 Jan 31, 2022
Flym News Reader is a light Android feed reader (RSS/Atom)

BEWARE: Google added some restrictions to news app and I don't see how Flym (and other RSS aggregators) could comply to that. For instance, Flym canno

Frédéric Julian 938 Jan 1, 2023
Flym News Reader is a light Android feed reader (RSS/Atom)

BEWARE: Google added some restrictions to news app and I don't see how Flym (and other RSS aggregators) could comply to that. For instance, Flym canno

Frédéric Julian 938 Jan 1, 2023