Search code examples
androidkotlinretrofitretrofit2base-url

Use EditText value as baseUrl in Retrofit


I'm creating an app where the user has to insert a serverurl in an EditText field, and that url should be the baseUrl of the retrofit-request. So, my code works as it should when i use a hardcoded baseurl, but the app crashes when I try to pass the value from the Edittext to the baseUrl.

Thats how I tried to pass the value:

object NetworkLayer {

        var newUrl: String = ""

        val retrofit: Retrofit
            get() = Retrofit.Builder()
                .baseUrl(newUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build()

        val myApi: MyApi by lazy {
            retrofit.create(MyApi::class.java)
        }
        val apiClient = ApiClient(myApi)
    }

and in my MainActivity:

 var serverUrl = binding.et1.text.toString()
        
        button.setOnClickListener {
            NetworkLayer.newUrl = serverUrl
            viewModel.getServerInformation(headerValue)
        }

I get this error message: Error message: Caused by: java.lang.IllegalArgumentException: Expected URL scheme 'http' or 'https' but no scheme was found for.

So probably retrofit uses the empty "" string for the request. Somehow I should send the information to retrofit that when clicking the button the url from the Edittext (et1) is the baseUrl. When I use a seperate class (f.e. class Constants, with a companion object with a const val baseUrl = "hardcoded url") it works also. Can I create a function to inform the retrofit client to use the Edittext as baseUrl and declare it in the onClickListener? or could it be a way to create the retrofit client in a class instead of an object? (using url: String as parameter in the class and adding the edittext as argument in the MainActivity?) Sadly the @Url annotation for Retrofit doesn't work as I have to use also @Header and @Query in the different requests.

Or is there a compeletey different way for doing this?

Hopefully there is someone who can help me.


Solution

  • I managed to solve it, the only thing I had to change was: val url = binding.etServerUrl.text instead of val url = binding.etServerUrl.text.toString()

    and when calling the function on button click I added the toString() to the url argument. When I try to add the toString() to the val url as I always did before it doesn't work, anyone can tell me why?

    Here is an example how I use it (I changed the Retrofit client a bit to my first version in the question). So finally I can go ahead with my app, as I was blocked now for a few weeks with this.. :-)

    object RetrofitClient{
    
        var retrofitService: MyApi? = null
        
        fun getInstance(url: String): MyApi{
    
            if (retrofitService == null) {
                val retrofit = Retrofit.Builder()
                    .baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                retrofitService = retrofit.create(MyApi::class.java)
            }
            return retrofitService!!
        }
    }
    

    I changed the retrofitclient a bit, but it wors Then in the repository:

    class MainRepository (){
    
        suspend fun getToken(cookie: String, url: String): TokenResponse? {
            val request = RetrofitClient.getInstance(url).getToken(cookie)
    
            if (request?.isSuccessful!!) {
                return request.body()!!
            }
    
            return null
        }
    }
    

    Viewmodel:

    class SharedViewModel() : ViewModel() {
    
        private val repository = MainRepository()
    
    
        private val _getTokenLiveData = MutableLiveData<TokenResponse>()
        val getTokenLiveData: LiveData<TokenResponse> = _getTokenLiveData
    
    
        fun getToken(cookie: String, url: String) {
            viewModelScope.launch {
                val response = repository.getToken(cookie, url)
                _getTokenLiveData.postValue(response)
            }
        }
    }
    

    And finally the MainActivity:

    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        val viewModel: SharedViewModel by lazy {
            ViewModelProvider(this)[SharedViewModel::class.java]
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater) //initializing the binding class
            setContentView(binding.root)
    
            val url = binding.etServerUrl.text
            val headerValue = binding.etMac.text.toString()
            val button = binding.button
            val textView = binding.textView
    
            button.setOnClickListener {
                viewModel.getToken(headerValue, url = url.toString())
            }
    
    
            viewModel.getTokenLiveData.observe(this) { response ->
                if (response == null) {
                    Toast.makeText(this@MainActivity, "Fehlerhaft", Toast.LENGTH_SHORT).show()
                    return@observe
                }
                textView.text = response.js.token
    
            }
    
        }
    }