Part 1- How to change base url on runtime in an Android Project? Part 2- How to use OkHttp interceptor for other usecases?
- Bunyamin Basoglu
- Mar 9, 2022
- 5 min read
Updated: Mar 26, 2024

Part 1- Change base url on runtime
I will implement a short and simple solution to change base url. It is all related to your okHttpClient. I will use retrofit in my example but that does not matter, you can also use it on the other http clients such as Ktor and so on. You just need to add a base url interceptor to your OkHttpClient.Builder.
1. Create an interceptor and a base url holder which will contain base url. Interceptor will intercept your api calls and change your old base url with new the base url that assigned in BaseUrlHolder. That is all you need to add for your project.
class BaseUrlInterceptor (var urlConfigHolder: BaseUrlConfigHolder) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val builder = request.newBuilder()
urlConfigHolder.baseUrl?.let {
adjustBaseUrl(request.url)?.let {
request = builder.url(it).build()
}
}
return chain.proceed(request)
}
private fun adjustBaseUrl(url: HttpUrl): HttpUrl? {
return urlConfigHolder.baseUrl?.let {
url.newBuilder().host(it.toHttpUrl().host).build()
}
}
}
data class BaseUrlConfigHolder(
var baseUrl : String?= null
)
2. If you are using any dependency injection library, create a singleton BaseUrlHolder to adjust base url. If you are not using any then you can keep it static or you can implement manual dependency injection. You need to reach this BaseUrlHolder from anywhere that you want to change your base url. Then you are done.
Then add interceptor to your OkHttpClient.Builder.
...
val yourHttpClientBuilder= OkHttpClient.Builder()
yourHttpClientBuilder.addInterceptor(
BaseUrlInterceptor({$yourSingletonBaseUrlHolder})
)
...
I am using an existing project as an example. I used hilt as dependency injection. So I will create a singleton BaseUrlConfigHolder and pass it as an argument to okHttpBuilder.
@Provides
@Singleton
fun provideBaseUrlConfigHolder() : BaseUrlConfigHolder = BaseUrlConfigHolder()
@Provides
@Singleton
fun provideOkHttpClient(baseUrlConfigHolder: BaseUrlConfigHolder) : OkHttpClient {
return OkHttpClient
.Builder()
.callTimeout(Constants.NetworkConstants.TimeoutSecForRequest, TimeUnit.SECONDS)
.connectTimeout(Constants.NetworkConstants.TimeoutSecForRequest, TimeUnit.SECONDS)
.readTimeout(Constants.NetworkConstants.TimeoutSecForRequest, TimeUnit.SECONDS)
.writeTimeout(Constants.NetworkConstants.TimeoutSecForRequest, TimeUnit.SECONDS)
.addInterceptor(BaseUrlInterceptor(baseUrlConfigHolder))
.build()
}
3. At this point when you change {$yourSingletonBaseUrlHolder}.baseUrl you will change base url of your api calls for that http client.
{$yourSingletonBaseUrlHolder}.baseUrl = {myNewBaseUrl}
In my example I just need to reach BaseUrlConfigHolder and change its baseUrl property. Let's change when user open WebViewFragment. As can be seen from the example code sample below, WebViewFragment will inject BaseUrlConfigHolder and when onViewCreated method called it will change baseUrl of the calls with "https://news.google.com/", from that point on for intercepted http client.
@AndroidEntryPoint
class WebViewFragment : BaseFragment(){
@Inject
lateinit var baseUrlConfigHolder: BaseUrlConfigHolder
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
baseUrlConfigHolder.baseUrl = "https://news.google.com/"
...
4. If you have multiple http clients and multiple base urls, just create multiple BaseUrlHolder and multiple interceptors for each and intercept with their interceptor and BaseUrlHolder.
5. If you have multiple http clients and multiple base urls, just create multiple BaseUrlHolder and multiple interceptors for each and intercept with their interceptors and BaseUrlHolder.
You can also check implementation from this project branch on Github https://github.com/b-basoglu/NewsApp/tree/example/base-url-interceptor. You can compare with the master to see how small are changes.
Part 2- Let’s go through more modifications step by step:
I simply showed the approach you’ve outlined for dynamically changing the base URL using an interceptor and a base URL holder is quite effective and versatile. However, to extend its functionality to include modifying other parts of the URL such as the scheme, port, or headers, you can make some adjustments to the BaseUrlInterceptor and the base URL configuration.
1. Modify DynamicUrlInterceptor to handle more URL components and headers
You can extend the DynamicUrlInterceptor to include methods for adjusting other URL components and headers:
class UrlAdjustingInterceptor(private val urlConfigHolder: UrlConfigHolder) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val builder = request.newBuilder()
urlConfigHolder.url?.let { url ->
adjustUrl(request.url)?.let { adjustedUrl ->
request = builder.url(adjustedUrl).build()
}
}
return chain.proceed(request)
}
private fun adjustUrl(url: HttpUrl): HttpUrl? {
return urlConfigHolder.url?.let { configuredUrl ->
url.newBuilder()
.scheme(configuredUrl.toHttpUrl().scheme) // Adjust scheme
.port(configuredUrl.toHttpUrl().port) // Adjust port
.host(configuredUrl.toHttpUrl().host)
.build()
}
}
}
In this modification, we’re adjusting the scheme and port along with the host. You can add more methods to adjust other components like the path, query, and fragment if needed.
2. Extend UrlConfigHolder to include url and headers
You can extend the UrlConfigHolder to include the url along with headers that you want to add to the request:
data class UrlConfigHolder(
var url: String? = null,
var headers: Map<String, String> = emptyMap()
)
3. Modify DynamicUrlInterceptor to include url and headers
Now, adjust the UrlAdjustingInterceptor to use the url and add headers to the request:
class UrlAdjustingInterceptor(private val urlConfigHolder: UrlConfigHolder) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val builder = request.newBuilder()
urlConfigHolder.url?.let { configuredUrl ->
adjustUrl(request.url)?.let { adjustedUrl ->
request = builder.url(adjustedUrl).build()
}
}
// Add headers
urlConfigHolder.headers.forEach { (name, value) ->
builder.addHeader(name, value)
}
return chain.proceed(request)
}
private fun adjustUrl(url: HttpUrl): HttpUrl? {
return urlConfigHolder.url?.let { configuredUrl ->
url.newBuilder()
.scheme(configuredUrl.toHttpUrl().scheme) // Adjust scheme
.port(configuredUrl.toHttpUrl().port) // Adjust port
.host(configuredUrl.toHttpUrl().host)
.build()
}
}
}
With these modifications, the UrlConfigHolder now includes the url property, allowing you to dynamically adjust it along with headers for your API calls. The interceptor intercepts requests and adjusts the URL and headers accordingly before sending them out.
Overall, interceptors provide a powerful mechanism for customizing and extending the behavior of HTTP clients to meet the specific needs of an application. There are some example usages below:
Modify Requests: Interceptors can modify the outgoing HTTP request before it is sent. This includes adding or removing headers, adjusting the URL, or even altering the request body.
Handle Authentication: Interceptors can handle authentication by adding necessary authentication headers to the request. For example, adding OAuth tokens or API keys.
Log Requests and Responses: Interceptors can log details of the requests and responses for debugging purposes. This includes logging headers, request URLs, request bodies, response codes, and response bodies.
Handle Retries: Interceptors can implement retry logic in case of network failures or server errors. They can intercept failed requests, determine if a retry is needed, and modify the request accordingly.
Handle Redirects: Interceptors can handle HTTP redirects by intercepting redirect responses and modifying the request to follow the redirect or handle it differently.
Implement Caching: Interceptors can implement caching mechanisms by intercepting responses and storing them in a cache. Subsequent requests for the same resource can be served from the cache to improve performance and reduce network traffic.
Modify Responses: Interceptors can modify incoming HTTP responses before they are delivered to the client. This includes modifying response headers or response bodies.
Measure Performance: Interceptors can measure performance metrics such as request/response times, network latency, and throughput for monitoring and optimization purposes.
Implement Custom Logic: Interceptors can implement custom logic specific to the application’s requirements. This could include enforcing security policies, modifying requests based on certain conditions, or transforming data before it is sent or received.
Comments