Last active
June 14, 2022 16:52
-
-
Save seadowg/b684c11a2cc6e092c8ed4301181728f7 to your computer and use it in GitHub Desktop.
Tiny DI/service locator (whatever) framework for Kotlin that uses a shared singleton (like an Android Application object) as a host for dependencies. Includes Android extensions because reified is fun.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.Activity | |
import android.content.Context | |
import androidx.fragment.app.Fragment | |
import kotlin.reflect.KClass | |
import kotlin.reflect.KProperty | |
class ObjectProvider { | |
private val providers = mutableMapOf<Class<*>, () -> Any?>() | |
private val singletons = mutableMapOf<Class<*>, Any?>() | |
fun <T> setProvider(clazz: Class<T>, singleton: Boolean = true, provider: () -> T) { | |
if (singleton) { | |
providers[clazz] = { getSingleton(clazz, provider) } | |
} else { | |
providers[clazz] = provider | |
} | |
} | |
@Suppress("UNCHECKED_CAST") | |
fun <T> get(clazz: Class<T>): T { | |
return providers[clazz]!!.invoke() as T | |
} | |
private fun <T> getSingleton(clazz: Class<T>, provider: () -> T) { | |
singletons.getOrPut(clazz) { | |
provider() | |
} | |
} | |
} | |
interface ObjectProviderHost { | |
fun getObjectProvider(): ObjectProvider | |
} | |
fun objectProvider(block: ObjectProviderWrapper.() -> Unit): ObjectProvider { | |
val newObjectProvider = ObjectProvider() | |
block(ObjectProviderWrapper(newObjectProvider)) | |
return newObjectProvider | |
} | |
fun ObjectProvider.override(block: ObjectProviderWrapper.() -> Unit) { | |
block(ObjectProviderWrapper(this)) | |
} | |
class ObjectProviderWrapper(private val objectProvider: ObjectProvider) { | |
fun <T> provider(clazz: Class<T>, block: () -> T) { | |
objectProvider.setProvider(clazz, false, block) | |
} | |
fun <T : Any> provider(clazz: KClass<T>, block: () -> T) { | |
objectProvider.setProvider(clazz.java, false, block) | |
} | |
fun <T> singleton(clazz: Class<T>, block: () -> T) { | |
objectProvider.setProvider(clazz, true, block) | |
} | |
fun <T : Any> singleton(clazz: KClass<T>, block: () -> T) { | |
objectProvider.setProvider(clazz.java, true, block) | |
} | |
} | |
inline fun <reified T> Activity.inject(): InjectDelegate<T> { | |
return InjectDelegate(T::class.java) { getObjectProvider() } | |
} | |
inline fun <reified T> Fragment.inject(): InjectDelegate<T> { | |
return InjectDelegate(T::class.java) { requireContext().getObjectProvider() } | |
} | |
fun Context.getObjectProvider(): ObjectProvider { | |
return (applicationContext as ObjectProviderHost).getObjectProvider() | |
} | |
class InjectDelegate<T>(private val clazz: Class<T>, private val objectProvider: () -> ObjectProvider) { | |
private var value: T? = null | |
operator fun getValue(thisRef: Any?, property: KProperty<*>): T { | |
return value.let { | |
it ?: objectProvider().get(clazz).apply { value = this } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So in an Android Activity we end up with:
And then the Application looks like:
Could probably be extended with a module concept pretty easily. Something like: