Search code examples
androidmvvmandroid-viewmodel

How to clear Viewmodel state in activity


I have in my MVVM app two requests:

fun fetchStrings(lang: String, callBack: (res: JsonObject?) -> Unit) {
        job = CoroutineScope(Dispatchers.IO).launch {
            val array = JsonArray().apply { strings.forEach { item -> add(item) } }
            val jsonObject = JsonObject().apply {
                addProperty("lang", lang)
                add("ids", array)
            }
            val response = mainRepository.getStrings(jsonObject)
            withContext(Dispatchers.Main) {
                if (response.isSuccessful) {
                    callBack.invoke(response.body())
                } else {
                    callBack.invoke(null)
                }
            }
        }
    }

and:

   fun getUserFiles(requestBody: RequestBody? = null, callBack: (res: Any) -> Unit) {
        job = CoroutineScope(Dispatchers.IO).launch {
            val response = requestBody?.let { mainRepository.getUserFiles(it) }
            withContext(Dispatchers.Main) {
                if (response?.isSuccessful == true) {
                    callBack.invoke(response.body()!!)
                } else {
                    response?.message()?.let { callBack.invoke(it) }
                }
            }
        }
    }

both of them are in one Viewmodel. This viewmodel use mainRepository with these methods:

class MainRepository constructor(private val retrofitService: RetrofitService) {
    suspend fun getUserFiles(requestBody: RequestBody) = retrofitService.getUserFiles(requestBody)
    suspend fun getStrings(jsonObject: JsonObject) = retrofitService.getStrings(jsonObject)
}

with retrofitService:

 companion object {
        var retrofitService: RetrofitService? = null
        fun getInstance(mint: Boolean): RetrofitService {
            var isMint = true
            val client = OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.MINUTES)
                .callTimeout(10, TimeUnit.MINUTES)
                .writeTimeout(10, TimeUnit.MINUTES)
                .readTimeout(10, TimeUnit.MINUTES)
                .connectionPool(ConnectionPool(0, 5, TimeUnit.SECONDS))
                .dispatcher(Dispatcher().apply {
                    maxRequests = 1
                })
                .protocols(listOf(Protocol.HTTP_1_1))
                .build()


            client.dispatcher.cancelAll()

            val retrofit = Retrofit.Builder()
                .baseUrl(if (mint) BuildConfig.MINT_URL else BuildConfig.API_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
            retrofitService = retrofit.create(RetrofitService::class.java)

            return retrofitService!!
        }

    }

the problem is that these methods have two different URLs for requests which is managed by this condition if (isMint) BuildConfig.MINT_URL else BuildConfig.API_URL and when I make in activity:

val retrofitService = RetrofitService.getInstance(false)
val mainRepository = MainRepository(retrofitService)
val viewVM =  ViewModelProvider(
this,
AppVMFactory(mainRepository)
)[RequestsCore::class.java]

with changing here RetrofitService.getInstance(false) true/false I see that this value does not change. I tried to clear viewmodels:

viewModelStoreOwner.viewModelStore.clear()

but it did not help. I tried to add direct condition in:

.baseUrl(if (mint) BuildConfig.MINT_URL else BuildConfig.API_URL)

which will depend on request url, but it also did not help. Also tried such way:

viewModelStoreOwner.viewModelStore.keys().forEach {
            viewModelStoreOwner.viewModelStore[it]?.viewModelScope?.coroutineContext?.cancel()
        }

The only way which I can see is to create separate Viewmodel with usage similar repository. Maybe we have some way to reset viewmodel all values in context of one one viewmodelstoreowner?


Solution

  • Your view model is fine. The problem is located in RetrofitService conception.

    If there are some calls with different urls for the same environment, use Dynamic URL instead of recreating RetrofitService.

    public interface RetrofitService {
        @GET
        getUserFiles(@Url url: String, requestBody: RequestBody): YourResponse1
    
        @GET
        getStrings(@Url url: String, jsonObject: JsonObject): YourResponse2
    }
    
    class MainRepository constructor(private val retrofitService: RetrofitService) {
        suspend fun getUserFiles(requestBody: RequestBody) : YourResponse1 {
          val url = BuildConfig.API_URL + "path" // Simplified explanation
          return retrofitService.getUserFiles(url, requestBody)
        }
    
        suspend fun getStrings(jsonObject: JsonObject) : YourResponse2 {
          val url = BuildConfig.MINT_URL + "path" // Simplified explanation
          return retrofitService.getStrings(url, jsonObject)
        }
    
    }
    

    This answer might be helpful for the path generation.