0

An application uses singletons. One of them contains an instance of Retrofit client. Another one contains constants.

I can change a server URL in app preferences (in activity), so that after the app restart I expect to reinitialize Retrofit client with the new base URL.

After first start all singletons and classes are initialized. Then I change the URL in the activity and restart the application. Now singletons don't initialize and contain old constants. If I forcely stop the app or attach a debugger these singletons restart as expected.

object ApiClass {

    private val gsonConverter: GsonConverter = GsonConverter()
    var gson: Gson
        private set
    var retrofit: Retrofit
        private set

    init {
        Timber.i("*** start ApiClass")

        val okHttpClient = OkHttpClient().newBuilder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build()

        gson = GsonBuilder().setLenient().create()

        val url = "..." // Get a server URL from preferences.
        Timber.i("*** " + url)

        retrofit = Retrofit.Builder()
            .baseUrl(url)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
        ...
    }
}

/////////////

object ApiConst {

    init {
        Timber.i("*** start ApiConst")
    }

    const val SOME_TEXT = "text"
}

How to reinitialize singletons without Force stop the app?

CoolMind
  • 26,736
  • 15
  • 188
  • 224
  • 2
    I suspect Android might keep some state of your app, the truth is that stateful globals are often problematic, the easy solution is to add a call to reinitialize your 'ApiClass'. A better solution is to stop using singletons and have objects ties to the app lifecylce – al3c Jul 20 '20 at 14:10
  • @al3c, thanks! Maybe changing `ApiClass` to https://stackoverflow.com/questions/41672427/how-to-make-a-singleton-for-retrofit-2 will help (famous `getInstance()`). I will test. – CoolMind Jul 20 '20 at 14:16

1 Answers1

0

Thanks to @al3c I found that it was useless to reinitialize singletons. I tried to change one form of singleton to another (like in How to make a singleton for retrofit 2?):

class ApiClass {

    var gson: Gson private set
    lateinit var retrofit: Retrofit private set
    private val gsonConverter: GsonConverter = GsonConverter()
    private val okHttpClient: OkHttpClient

    init {
        Timber.i("*** start ApiClass")

        okHttpClient = OkHttpClient().newBuilder()
        ...
   }

    companion object {
        private var instance: ApiClass? = null

        fun getInstance(): ApiClass {
            if (instance == null) instance = ApiClass()
            return instance!!
        }
    }
}

But calling ApiClass.getInstance().retrofit didn't help me to recreate ApiClass after restart. Then I tried to change a base URL in Retrofit client before destroying the app (so that after restart it will have a new URL). After reading Retrofit - Change BaseUrl I wrote:

fun setBaseUrl(url: String) {
    retrofit = Retrofit.Builder()
        .baseUrl(url)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
}

Then I was able to call setBaseUrl from preferences activity in onDestroy to change Retrofit client. Also it helped me to continue working with the app with new URL for requests, so that I didn't have to restart the app.

CoolMind
  • 26,736
  • 15
  • 188
  • 224