如何将EditText输入值作为Retrofit的baseUrl使用?
问题原因分析
你的核心问题出在两点:
NetworkLayer是单例object,myApi通过by lazy初始化,第一次调用时会直接用初始空字符串newUrl创建Retrofit实例,触发IllegalArgumentException;- 即便后续修改
newUrl,lazy修饰的实例不会重新初始化,永远复用第一次创建的错误Retrofit对象。
可行解决方案
方案一:将NetworkLayer改为可传入URL的类(推荐)
把单例改成带构造参数的类,每次点击按钮时根据用户输入创建新的网络层实例,确保Retrofit用最新的baseUrl初始化:
class NetworkLayer(private val baseUrl: String) { private val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() } val myApi: MyApi by lazy { retrofit.create(MyApi::class.java) } val apiClient: ApiClient by lazy { ApiClient(myApi) } }
MainActivity调用逻辑:
button.setOnClickListener { val serverUrl = binding.et1.text.toString().trim() // 强制校验URL格式,必须包含http/https协议 if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) { Toast.makeText(this, "请输入带http/https前缀的服务器地址", Toast.LENGTH_SHORT).show() return@setOnClickListener } // 创建新的网络层实例并传入ViewModel val networkLayer = NetworkLayer(serverUrl) viewModel.setNetworkLayer(networkLayer) viewModel.getServerInformation(headerValue) }
ViewModel中持有实例:
class YourViewModel : ViewModel() { private lateinit var networkLayer: NetworkLayer fun setNetworkLayer(layer: NetworkLayer) { this.networkLayer = layer } fun getServerInformation(headerValue: String) { viewModelScope.launch { // 使用最新的网络层实例发起请求 val response = networkLayer.apiClient.getServerInfo(headerValue) // 处理请求结果 } } }
方案二:在单例中添加重置逻辑
如果需要保持单例模式,可通过重置Retrofit相关实例的方式,让下次调用时用新的baseUrl重新创建:
object NetworkLayer { private var currentBaseUrl: String = "" private var retrofit: Retrofit? = null private var myApi: MyApi? = null private var apiClient: ApiClient? = null // 提供设置baseUrl并重置实例的方法 fun updateBaseUrl(url: String) { currentBaseUrl = url // 清空所有缓存实例 retrofit = null myApi = null apiClient = null } private val retrofitInstance: Retrofit get() { if (retrofit == null) { retrofit = Retrofit.Builder() .baseUrl(currentBaseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() } return retrofit!! } val myApiInstance: MyApi get() { if (myApi == null) { myApi = retrofitInstance.create(MyApi::class.java) } return myApi!! } val apiClientInstance: ApiClient get() { if (apiClient == null) { apiClient = ApiClient(myApiInstance) } return apiClient!! } }
MainActivity调用逻辑:
button.setOnClickListener { val serverUrl = binding.et1.text.toString().trim() if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) { Toast.makeText(this, "请输入带http/https前缀的服务器地址", Toast.LENGTH_SHORT).show() return@setOnClickListener } NetworkLayer.updateBaseUrl(serverUrl) viewModel.getServerInformation(headerValue) }
ViewModel中使用:
fun getServerInformation(headerValue: String) { viewModelScope.launch { val response = NetworkLayer.apiClientInstance.getServerInfo(headerValue) // 处理请求结果 } }
关键注意事项
- 必须校验用户输入的URL格式,强制要求包含
http://或https://,否则Retrofit会直接抛出异常; - 处理空输入场景,避免传入空字符串触发错误;
- 方案一更适合需要频繁切换不同服务器地址的场景,方案二更适合全局单例管理的场景。
内容的提问来源于stack exchange,提问作者Alex Mutschl
相关产品推荐
相关产品推荐

