question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is it possible to change Singleton (or any parameter) values after the initial Kodein instantiation?

See original GitHub issue

I’d really like to be able to change the value of my HTTP clients baseUrl value, because it can be set dynamically while using the application. I’m having trouble finding a way to change parameter values dynamically.

I’ve looked into Multiton binding as it seemed like a type of Singleton-Factory that would allow the arguments to be changed. But I had no luck with that.

Here is my Api singleton:

interface LoginApi {

    @POST("Account/Login")
    @FormUrlEncoded
    fun login(
        @Field("username") username: String,
        @Field("password") password: String,
        @Field("sessionType") sessionType: String
    ): Call<BaseApiResponse<Login>>

    @GET("Account/Logout")
    fun logout(
        @Query("token") token: String
    ): Call<BaseApiResponse<Any>>

    companion object {
        operator fun invoke(
            baseUrl: String,
            cache: Cache
        ): LoginApi {

            val okHttpClient = OkHttpClient.Builder()
                .cache(cache)
                .readTimeout(60, TimeUnit.SECONDS)
                .connectTimeout(60, TimeUnit.SECONDS)
                .build()

            return Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(baseUrl)
                .addConverterFactory(JacksonConverterFactory.create())
                .build()
                .create(LoginApi::class.java)
        }
    }
}

Dependency Declaration with muliton (doesn’t work):

     import(androidXModule(this@MyApplication))

        bind<Any>() with multiton { baseUrl: String, applicationContext: Context ->
            createLoginApi(baseUrl, applicationContext)
            LoginApi(
                BuildConfig.BASE_URL,
                Cache(applicationContext.cacheDir, 10 * 1024 * 102)
            )
        }

        bind<LoginDataSource>() with singleton { LoginService(instance()) }
        bind<LoginRepository>() with singleton { LoginRepositoryImpl(instance()) }
        bind() from singleton { LoginViewModelFactory(instance()) }
        bind() from singleton { LoginViewModel(instance()) }
    }

Dependency Declaration with no multiton (works):

    import(androidXModule(this@MyApplication))

        bind() from singleton {
            LoginApi(
                BuildConfig.BASE_URL,
                Cache(applicationContext.cacheDir, 10 * 1024 * 102)
            )
        }

        bind<LoginDataSource>() with singleton { LoginService(instance()) }
        bind<LoginRepository>() with singleton { LoginRepositoryImpl(instance()) }
        bind() from singleton { LoginViewModelFactory(instance()) }
        bind() from singleton { LoginViewModel(instance()) }
    }

Version

    implementation 'org.kodein.di:kodein-di-generic-jvm:6.3.2'
    implementation "org.kodein.di:kodein-di-framework-android-x:6.3.2”

Let me know if you need any other information. This is pretty very simple example – I’d just like to be able to change parameter values dynamically. If that isn’t possible, then is there any way that would allow me to do such a thing

Thank you in advance!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
romainbslcommented, Sep 24, 2019

that should work, but you’re code is not entirely decoupled.

bind<LoginApi>() with multiton { args: LoginArguments ->
    createLoginApi(args.baseUrl, applicationContext)
    LoginApi(
        args.baseUrl,
        Cache(applicationContext.cacheDir, 10 * 1024 * 102)
    )
}

bind<LoginDataSource>() with multiton { args: LoginArguments -> LoginService(instance(arg = args)) }

bind<LoginRepository>() with singleton { 
    LoginRepositoryImpl(instance(arg = LoginArguments(BuildConfig.BASE_URL)))
}

When retrieving the instance of LoginRepository Kodein will:

  1. Look for a binding for LoginRepository
    • that’s the singleton with LoginRepositoryImpl waiting for a LoginDataSource
  2. Look for LoginDataSource binding that need a parameter args: LoginArguments
    • that’s the multiton returning a LoginService with args: LoginArgumentsas parameter
      • this is the job of instance(arg = LoginArguments(BuildConfig.BASE_URL))

Hope this is clear enough.

2reactions
romainbslcommented, Sep 24, 2019

Well, I get what you’re trying to do. There is no magic in Kodein, if you bind a multiton with some arguments, like:

bind<LoginApi>() with multiton { args: LoginArguments -> ... }

You can’t expect from Kodein to retrieve it with some dynamic values by applying the instance function, as you can have multiple instances to inject.

What you could do is to use another multiton, like:

bind<LoginDataSource>() with multiton { args: LoginArguments -> LoginService(instance(arg = args)) }

thus, the usage of LoginDataSource could be something like:

val dataSource: LoginDataSource by kodein.instance(arg = LoginArguments("/path", context))
Read more comments on GitHub >

github_iconTop Results From Across the Web

Is it possible to change Singleton (or any parameter) values ...
Hello,. We don't recommend to change the kodein instance at runtime. In fact, it's not possible out of the box. You are on...
Read more >
Singleton with parameter in Kotlin - Stack Overflow
I used a companion object for the singletons without constructor parameters. There is another singleton that takes a constructor parameter.
Read more >
Thread safe singleton that take parameters for instantiation
Hi, I understand the following class is an example of thread safe implementation of Singleton. This class initializes the instance lazily ...
Read more >
Singleton class / Object class / Companion object – In Kotlin
In the following implementation, the Storage class has a private constructor. So it can not be instantiated by any client.
Read more >
Kotlin singletons with argument. object has its limits
Contrary to a class , an object can't have any constructor, but init blocks are allowed if some initialization code is needed. object...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found