Change Retrofit Base URL on Runtime.

Narayan Panthi
Nerd For Tech
Published in
3 min readSep 5, 2021

--

Switch between multiple base URLs like prod | dev in retrofit easily.

Hey there! Great to have you on board. In this article, we’ll walk through a quick and user-friendly technique to switch up the base URL in Retrofit.

Retrofit is a type-safe HTTP client for Android, Java, and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp.

We instantiate the Retrofit instance by providing the default base URL.

return new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BuildConfig.PRODUCTION_BASE_URL)
.addConverterFactory(-)
.addCallAdapterFactory(-)
.build();

Problem Report

Switching between URLs at runtime used to be a bit of a headache. Developers had to resort to using two separate Retrofit instances in their apps to deal with this challenge. To avoid the hassle of having multiple Retrofit instances, (we) developers came up with some cool tricks, like…

  1. With Retrofit Dynamic URL — Tedious
  2. Interceptor to change host URL with the help of Shared Pref ✅
  3. Storing BASE_URL directly in Shared Pref — Ok
  4. Reinit OkHttp /Retrofit— Nice

And more...

Here, Let’s implement approach no 2.

Interceptor to change host URL

Let’s dive in by setting up some dependencies.

ext {
okHttpVersion = "4.8.1"
retrofitVersion = "2.9.0"
hiltVersion = "2.38.1"
}
// Implementation
// OkHttp
implementation "com.squareup.okhttp3:okhttp:$rootProject.okHttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okHttpVersion"

// Retrofit2
implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"
implementation "com.github.akarnokd:rxjava3-retrofit-adapter:3.0.0"
implementation "com.squareup.retrofit2:converter-protobuf:$rootProject.retrofitVersion"

// Hilt
implementation "com.google.dagger:hilt-android:$rootProject.hiltVersion"
annotationProcessor "com.google.dagger:hilt-android-compiler:$rootProject.hiltVersion"
annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0'

Now, create a PreferenceHelper (SharedPrefManager) with two helper functions to read/write the status of the current environment selection mode.

 // Store & Get Prod or Dev Environment from Shared Pref
void setProdEnvironmentStatus(boolean status);
boolean isProdEnvironment();

Then, create an Interceptor to handle the switching environment work. 😮

Here, Retrofit is injected with DEVELOPMENT_BASE_URL assuming the default selection for the application.

@Provides
@Singleton
public Retrofit provideRetrofitClient(ProtoConverterFactory protoConverterFactory, RxJava3CallAdapterFactory rxJava3CallAdapterFactory, OkHttpClient okHttpClient) {return new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BuildConfig.DEVELOPMENT_BASE_URL)
.addConverterFactory(protoConverterFactory)
.addCallAdapterFactory(rxJava3CallAdapterFactory)
.build();
}

NOW provide HostSelectionInterceptor dependency to the hilt in NetworkModule.java as

@Provides
@Singleton
public HostSelectionInterceptor provideHostSelectionInterceptor(
PreferenceHelper preferenceHelper) {
return new HostSelectionInterceptor(preferenceHelper);
}

Then, provide this interceptor to our OkHttpClient Builder like below.

@Provides
@Singleton
public OkHttpClient provideOkHttpClient(@ApplicationContext Context context, HttpLoggingInterceptor httpLoggingInterceptor, HostSelectionInterceptor hostSelectionInterceptor) {

long cacheSize = 5 * 1024 * 1024;
Cache mCache = new Cache(context.getCacheDir(), cacheSize);
if (BuildConfig.DEBUG) {
return new OkHttpClient().newBuilder()
.cache(mCache)
.retryOnConnectionFailure(true)
.connectTimeout(200, TimeUnit.SECONDS)
.readTimeout(200, TimeUnit.SECONDS)
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(hostSelectionInterceptor)
.followRedirects(true)
.followSslRedirects(true)
.build();

} else {
return new OkHttpClient().newBuilder()
.connectTimeout(200, TimeUnit.SECONDS)
.readTimeout(200, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor(hostSelectionInterceptor)
.build();
}
}

Now, We can INJECT HostSelectionInterceptor in our ViewModel or activity by simply providing @Inject annotation. Use it where it fits best within your project’s context.

@Inject
HostSelectionInterceptor hostSelectionInterceptor;

And… Finally, we can switch between these environments just by changing the selection mode status on shared Preference and recalling our setHostBaseUrl function in HostSelectionInterceptor.

I prefer to do the work from ViewModel. Just leaving here a sample.

Example of Spinner Switch for “DEV” & “PROD” environment.

private void doSetupHostSelection() {

ArrayAdapter<String> arrayAdapter = new CustomSpinnerAdapter(this).spinnerAdapter();
//Setting the ArrayAdapter data on the Spinner
arrayAdapter.add("DEV");
arrayAdapter.add("PROD");
getViewDataBinding().spinnerHostSelection.setAdapter(arrayAdapter);


// selector
getViewDataBinding().spinnerHostSelection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

if (position == 0) {
preferenceHelper.setProdEnvironmentStatus(false);
hostSelectionInterceptor.setHostBaseUrl();

} else {
preferenceHelper.setProdEnvironmentStatus(true);
hostSelectionInterceptor.setHostBaseUrl();
}
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
});

}

This function setHostBaseUrl will do the rest of the work.

public void setHostBaseUrl() {
if (preferenceHelper.isProdEnvironment()) {
this.host = HttpUrl.parse(BuildConfig.PRODUCTION_BASE_URL);
} else {
this.host = HttpUrl.parse(BuildConfig.DEVELOPMENT_BASE_URL);
}
}

Grab the idea & Implement it in the best way!

Note!!

I suggest limiting its usage to the development/debug build. This is primarily suitable for the development phase because all requests pass through this interceptor, potentially leading to slow your request.

Moreover, when debugging the request, you may come across the same URL being requested, as the interceptor alters it midway.

Thank you for reading…

For any confusion or suggestion fill in the comment section 🐧

--

--