You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将EditText输入值作为Retrofit的baseUrl使用?

问题原因分析

你的核心问题出在两点:

  1. NetworkLayer是单例object,myApi通过by lazy初始化,第一次调用时会直接用初始空字符串newUrl创建Retrofit实例,触发IllegalArgumentException;
  2. 即便后续修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:25:35