如何在Activity中重置ViewModel并切换Retrofit请求BaseUrl
现有代码实现
ViewModel中的请求方法
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) } } } }
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) } } } } }
MainRepository代码
class MainRepository constructor(private val retrofitService: RetrofitService) { suspend fun getUserFiles(requestBody: RequestBody) = retrofitService.getUserFiles(requestBody) suspend fun getStrings(jsonObject: JsonObject) = retrofitService.getStrings(jsonObject) }
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!! } }
问题现象
在Activity中修改RetrofitService.getInstance(false)的参数为true/false时,BaseUrl并未切换。尝试过调用viewModelStoreOwner.viewModelStore.clear()清空ViewModel、修改BaseUrl判断逻辑、取消ViewModel协程等方法,均无效。目前只能通过新建独立ViewModel及对应Repository解决,想知道是否有在同一个ViewModelStoreOwner下重置ViewModel所有状态的方案。
解决方案
1. 修复Retrofit单例的逻辑错误
原单例代码中var isMint = true是冗余代码,且单例只会保留最后一次创建的实例,无法同时维护两个BaseUrl对应的Service。修改为分别存储两个实例:
companion object { private var mintRetrofitService: RetrofitService? = null private var regularRetrofitService: RetrofitService? = null fun getInstance(mint: Boolean): RetrofitService { return if (mint) { mintRetrofitService ?: createRetrofitInstance(true).also { mintRetrofitService = it } } else { regularRetrofitService ?: createRetrofitInstance(false).also { regularRetrofitService = it } } } private fun createRetrofitInstance(mint: Boolean): RetrofitService { 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() val retrofit = Retrofit.Builder() .baseUrl(if (mint) BuildConfig.MINT_URL else BuildConfig.API_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() return retrofit.create(RetrofitService::class.java) } }
2. 让ViewModel支持动态更新Repository
ViewModel构造时注入的Repository是固定的,需要提供方法允许替换内部的Repository:
class RequestsCore(private var mainRepository: MainRepository) : ViewModel() { // 新增方法:更新Repository fun updateRepository(newRepository: MainRepository) { this.mainRepository = newRepository } // 原有的fetchStrings、getUserFiles方法保持不变 }
在Activity中切换BaseUrl时:
// 创建新的RetrofitService和Repository val newRetrofit = RetrofitService.getInstance(true) val newRepo = MainRepository(newRetrofit) // 更新ViewModel的Repository viewVM.updateRepository(newRepo)
3. 完全重置ViewModel(同ViewModelStoreOwner下)
如果需要彻底重置ViewModel状态,先清空ViewModelStore再重新创建ViewModel:
// 清空当前ViewModelStoreOwner下的所有ViewModel viewModelStore.clear() // 用新的RetrofitService创建ViewModel val newRetrofit = RetrofitService.getInstance(true) val newRepo = MainRepository(newRetrofit) val viewVM = ViewModelProvider( this, AppVMFactory(newRepo) )[RequestsCore::class.java]
注意:viewModelStore.clear()会销毁所有关联的ViewModel,需确保其他ViewModel状态无需保留。之前调用无效可能是因为未重新创建ViewModel,或者ViewModel实例仍被其他引用持有。
4. 进阶方案:使用依赖注入框架
如果项目引入Hilt等依赖注入框架,可以通过绑定不同的RetrofitService实例,根据参数动态提供,实现更优雅的切换逻辑,无需手动管理实例和ViewModel更新。
内容的提问来源于stack exchange,提问作者Andrew

