I'm using Koin 3.0.0-alpha-4 version and when I'm trying to use injected class by koin, then it throws exception for iOS side:
KotlinException=kotlin.IllegalStateException: Must be main thread, KotlinExceptionOrigin
I have a singleton class where I'm initializing objects using koin like that:
#ThreadLocal
object ObjectFactory : KoinComponent{
val appStateRepository: AppStateRepository by inject()
val appStateRepositoryDemo = AppStateRepository()
}
if I use appStateRepository inside background thread (Dispatchers.Default), which is injected by koin, then it throws exception IllegalStateException: Must be main thread, but if I use appStateRepositoryDemo than it works fine
Here is a method which I'm invoking from iOS to inject modules
fun initKoinIos(
userDefaults: NSUserDefaults,
doOnStartup: () -> Unit
): KoinApplication = initKoin {
module {
...
single { doOnStartup }
}
}
fun initKoin(appDeclaration: KoinAppDeclaration = {}) = startKoin {
appDeclaration()
modules(
platformModule,
networkModule,
useCaseModules,
repositoryModule,
commonUtils,
)
}
Here is the usage:
fun testRepoAccess() {
ObjFactory.appStateRepository.test() // Ok, here we are in main thread
CoroutineScope(Dispatchers.Default).launch {
ObjFactory.appStateRepositoryDemo.test() // Ok
ObjFactory.appStateRepository.test() // Not Ok, throws exception (Must be main thread)
}
}
Expected behavior
It should work for iOS like it works Android
Koin 3.0.0-alpha-4
Additional moduleDefinition
Coroutines 1.4.2-native-mt
UPDATE
I was using wrong Koin library name, now I'm using :
io.insert-koin:koin-core:3.0.1
and now I have another exception:
kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared org.koin.core.context.GlobalContext.KoinInstanceHolder#1c02ca8 from other thread
Koin on Kotlin/Native requires injection on the main thread, to avoid freezing Koin state. As a result, you can't actually inject directly from a background thread.
Adding a special inject method that would allow you to inject by switching to the main thread was intended to be added to Koin, but I'm pretty sure that never wound up in the code. Primarily because it's very rare that anybody has needed it.
So, anyway, you can't do that with Koin. You could try Kodein, but I wrote Koin's implementation to throw precisely because touching Koin from another thread will freeze everything inside it, and that may not be what you intend to do.
I know nobody likes non-answers, but why is ObjectFactory #ThreadLocal? I assume to keep it mutable, but if appStateRepository is a single, it would be frozen anyway.
Related
I am calling a function in a class S3FileOperationsAdapter from the main function in my kotlin code. I am injecting the class S3FileOperationsAdapter in the main function file. So it looks like
class Runner {
#set:Inject
lateinit var s3FileOperationsAdapter: S3FileOperationsAdapter
fun main(args: Array<String>) {
s3FileOperationsAdapter.readFunction()
}
}
Now the issue is:
When I try to run the above code, I get the error Error: Main method is not static in class com.amazon.bram.sim.BatchJobRunner, please define the main method as:. This is understandable.
And we can only make a static function within an object in kotlin. So upon doing that, I cannot Inject the dependency, because Dagger does not support injection into Kotlin objects. So it feels like a deadlock.
My question is, I want to inject the dependency in this file anyhow so that I can call the respective function. And I am calling this function from the "fun main()" in kotlin. How can I achieve this? Has anyone ever faced this before?
In order to inject anything in Dagger, you must first create an instance of your component. Since no code at all will run before fun main(), this needs to be done during main itself (or in a field initializer).
After creating an instance of the component, you can ask it for an instance of S3FileOperationsAdapter directly.
fun main(args: Array<String>) {
// Create the component.
val component = DaggerMyComponent.create()
// or use the Component.Builder or Component.Factory you defined for MyComponent.
// Get an object from the component.
// This method should be defined in your component interface.
val adapter = component.s3FileOperationsAdapter()
// Use the object.
adapter.readFunction()
}
If your actual code is more complicated, with multiple injected objects and a longer main() function, this may be a bit unwieldy. In that case, you can extract your current main() into its own class and let Dagger provide that class in main().
I've been having a shot at kotlin multiplatform and it's brilliant, but threading stumps me. The freezing of state between threads makes sense conceptually, and works fine in simple examples where small objects or primitives are passed back and forth, but in real world applications I can't get around InvalidMutabilityException.
Take the following common code snippet from an android app
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
private var coreroutineSupervisor = SupervisorJob()
private var coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main + coreroutineSupervisor)
private fun loadResults() {
// Here: Show loading
coroutineScope.launch {
try {
val result = withContext(Dispatchers.Default) { objectWhichContainsNetworking.fetchData() }
// Here: Hide loading and show results
} catch (e: Exception) {
// Here: Hide loading and show error
}
}
}
Nothing very complex, but if used in common code and run from Kotlin/Native then pow InvalidMutabilityException on MainViewModel.
It seems the reason for this is that anything passed in withContext is frozen recursively so because objectWhichContainsNetworking is a property of MainViewModel and is used in withContext then MainViewModel gets caught in the freeze.
So my question is, is this just a limitation of the current Kotlin/Native memory model? Or perhaps the current version of coroutines? And are there any ways round this?
Note: coroutines version: 1.3.9-native-mt. kotlin version 1.4.0.
Edit 1:
So it appears that the above slimmed down code actually works fine. It turns out the incriminating code was an updateable var in the view model (used to keep a reference to the last view state) which becomes frozen and then throws the exception when it tries to be mutated. I'm going to make an attempt of using Flow/Channels to ensure there's no var reference needed and see if this fixes the overall problem.
Note: if there is a way to avoid MainViewModel being frozen in the first place it would still be fantastic!
Edit 2:
Replaced the var with Flow. I couldn't get standard flow collecting in iOS until using the helpers here: https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.
MainViewModel still gets frozen, but as all it's state is immutable it's no longer a problem. Hope it helps someone!
In your original code, you are referencing a field of the parent object, which causes you to capture the whole parent and freeze it. It is not an issue with coroutines. Coroutines follows the same rules as all the other concurrency libraries in Kotlin/Native. It freezes the lambda when you cross threads.
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
//yada yada
private fun loadResults() {
coroutineScope.launch {
try {
val result = withContext(Dispatchers.Default) {
//The reference to objectWhichContainsNetworking is a field ref and captures the whole view model
objectWhichContainsNetworking.fetchData()
}
} catch (e: Exception) {}
}
}
To prevent this from happening:
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
init{
ensureNeverFrozen()
}
//Etc
The most complicated thing with the memory model is this. Getting used to what's being captured and avoiding it. It's not that hard when you get used to it, but you need to learn the basics.
I've talked about this at length:
Practical Kotlin/Native Concurrency
Kotlin Native Concurrency Hands On
KotlinConf KN Concurrency
The memory model is changing, but it'll be quite a while before that lands. Once you get used to the memory model, the immutable issues are generally straightforward to diagnose.
com.soywiz.korinject.AsyncInjector$NotMappedException: Class 'class ChooseCampaign (Kotlin reflection is not available)' doesn't have constructors RequestContext(initialClazz=class ChooseCampaign (Kotlin reflection is not available))
Above exception was threw when I compiled current code. And I dont know how fix it and what does it means.
My code:
textButton {
position(0, 128)
text = "Play"
onClick {
println("Play")
launchImmediately {
sceneContainer.changeTo<ChooseCampaign>()
}
}
}
How it fix?
When using Scenes in KorGE, you are using the korinject dependency injector indirectly.
And that injector requires manual mapping. If you are using Modules, you can configure those mappings at the Module.init method.
Check this sample: https://github.com/korlibs/korge-samples/blob/1771b7ca7f4440e1a368ff4b441e97bf62e08b8d/sample-scenes/src/commonMain/kotlin/main.kt#L15-L23
In your case, once you get the Injector instance, you can map a scene like this:
mapPrototype { ChooseCampaign(get()) }
You have to put as many get() as parameters your ChooseCampaign constructor has.
In the case you are not using modules, the place to put the mapping is different, and you need to get the Injector instance.
In your suspend fun main() = Korge { block, you have the Stage singleton injected. This is the root view that has a reference to the Views singleton.
So there you can just access the injector like this: this.views.injector
You can then map your scenes whenever you like, though I suggest you do to it at the beginning of the application.
I'm trying to intercept the System.out print statements, and in a multithreaded program, I'm planning on adding these to a map using a CoroutineContext.Key as the map key, so I know which coroutine the output belongs to.
My child methods being executed don't have access to the CoroutineScope as this was kicked off on a parent method.
I was hoping for a static method along the lines of CoroutineContext.currentKey but this doesn't look like it exists.
I've achieved a similar thing in C#, using their Task.CurrentId
Is there any way for me to achieve this?
Thanks
You can create your own thread-local variable to keep your own identifier of the coroutine or even directly its saved output and use ThreadLocal.asContextElement() extension function to convert it to the coroutine context element. Now, if you start your coroutine with this element, then the specified value of this thread-local variable will be automatically installed into the corresponding thread-local variable as the this coroutine hops from thread to thread. See the following example code:
import kotlinx.coroutines.*
val myId = ThreadLocal<String>()
// I'm not a suspending function, yet I know what coroutine I work in
fun whereAmI() {
println("I'm in coroutine '${myId.get()}'")
}
fun main() = runBlocking<Unit> {
launch(myId.asContextElement("First")) {
whereAmI()
}
launch(myId.asContextElement("Second")) {
whereAmI()
}
}
I have a PropertiesModule that extends AbstractModule and contains application constants that I use throughout the project:
class PropertiesModule: AbstractModule(), Serializable {
companion object {
const val APP_NAME = "MyAppName"
...
}
override fun configure() {
...
}
}
Then I use the PropertiesModule to create the injector:
...
val injector = Guice.createInjector(
PropertiesModule(),
...
)
And later when I use the injector to get the property value, I have multiple choices. I could do:
val appName = injector.getInstance(
Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
)
or
val appName = injector.getExistingBinding(
Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
).provider.get()
or
val appName = injector.getProvider(
Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
).get()
I read that in general getInstance should be avoided. But I'm not sure what is the difference among them.
If you've just created your Injector, use getInstance. Otherwise, try to get your object from a narrower injection.
getBinding and getExistingBinding are effectively both for reflective purposes:
[Binding is] a mapping from a key (type and optional annotation) to the strategy for getting instances of the type. This interface is part of the introspection API and is intended primarily for use by tools.
Though it would probably work to some degree, I'd recommend against getExistingBinding, if only because it has the counterintuitive behavior of not creating Just-In-Time bindings if they do not already exist, as described in the getExistingBinding docs:
Unlike getBinding(Key), this does not attempt to create just-in-time bindings for keys that aren't bound.
This method is part of the Guice SPI and is intended for use by tools and extensions.
getProvider(Class<T>) will procure a Provider<T> from the graph, which is like a single-key Injector that can only create or retrieve instances of a single key from the Injector. Think of it as creating a lambda that calls getInstance for a specific Key, and nothing more.
getInstance is the most important method on Injector, and I expect all Guice applications to call it at least once. If you've just created your Injector, you'll presumably want to either get an instance from it, or inject the #Inject fields of an object. The former happens with getInstance, and the latter with injectMembers. In my opinion, that's the best way to get the first object out of your graph.
However, and this is where the advice you heard comes in: You should probably not use the Injector after that first getInstance or injectMembers call. This is because the goal in Guice is to make independent components that express their dependencies narrowly so you can replace those dependencies easily. This is the major benefit of dependency injection. Though you have the options of stashing the Injector somewhere static, or injecting the Injector itself into your dependencies, that prevents you from knowing exactly what your class's dependencies are: with an Injector injection, you can inject anything in the world.
To demonstrate (please forgive my Kotlin inexperience):
class BadIdea #Inject constructor(injector: Injector) {
fun doSomething() {
// Bad idea: you don't express this dependency anywhere, and this code
// is nearly unusable without a real Guice Injector
val appName = injector.getInstance(
Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
)
}
}
class WorseIdea {
fun doSomething() {
// Worse idea: same as the above, but now you rely on a static!
val appName = StaticInjectorHolder.getInjector().getInstance(
Key.get(String::class.java, Names.named(PropertiesModule.APP_NAME))
)
}
}
class GoodIdea #Inject constructor(#Named(PropertiesModule.APP_NAME) appName: String) {
fun doSomething() {
// Good idea: you express your dep, and anyone can call the constructor
// manually, including you in your unit tests.
}
}