Retrofit 2 — How to Change API Base Url at Runtime

In this tutorial we'll show you a very handy tool, which can make your debugging life so much easier. If you're an Android app developer and you've to deal with multiple versions of an API (for example develop, staging and production) you're probably tired of building three (or more) versions of the app, if you quickly want to see if your app works against all server deployments.

In the next few minutes, you'll learn how you can change the API base url at runtime! This means, you can check with one compilation and installation how your app behaves with all API versions.

In the last few months, we've published a variety of Retrofit tutorials:

Retrofit Series Overview

The Core: ServiceGenerator

Long time readers will know most of our Retrofit tutorials utilize our ServiceGenerator class from the creating a sustainable android client tutorial. Since we'll mainly work in the ServiceGenerator code, make sure you're familiar with this class.

In the current version the ServiceGenerator works with multiple static fields and a String constant API_BASE_URL, which holds the API base url:

public class ServiceGenerator {  
    public static final String API_BASE_URL = "http://futurestud.io/api";
    private static Retrofit retrofit;

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(API_BASE_URL);

    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();

    // No need to instantiate this class.
    private ServiceGenerator() {

    }

    public static <S> S createService(Class<S> serviceClass, AccessToken token) {
        String authToken = token.getTokenType().concat(token.getAccessToken());
        return createService(serviceClass, authToken);
    }

    // more methods
    // ...
}

Adjusting the ServiceGenerator

With this setup you don't have a chance to change the API_BASE_URL constant at runtime. You've change it in the source code, compile a new .apk and test it again. Since this is very inconvenient if you're working with multiple API deployments, we'll make minor changes to the ServiceGenerator class:

public class ServiceGenerator {  
    public static String apiBaseUrl = "http://futurestud.io/api";
    private static Retrofit retrofit;

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(apiBaseUrl);

    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();

    // No need to instantiate this class.
    private ServiceGenerator() {
    }

    public static void changeApiBaseUrl(String newApiBaseUrl) {
        apiBaseUrl = newApiBaseUrl;

        builder = new Retrofit.Builder()
                        .addConverterFactory(GsonConverterFactory.create())
                        .baseUrl(apiBaseUrl);
    }

    public static <S> S createService(Class<S> serviceClass, AccessToken token) {
        String authToken = token.getTokenType().concat(token.getAccessToken());
        return createService(serviceClass, authToken);
    }

    // more methods
    // ...
}

Let's examine our changes. We've renamed the constant API_BASE_URL to a non-final field apiBaseUrl. We also added a new static method changeApiBaseUrl(String newApiBaseUrl), which will change that particlar apiBaseUrl variable. It also creates a new version of the Retrofit.Builder instance builder. This is important because we're re-using the builder for requests. If we don't create a new instance all requests still would have gone against the original apiBaseUrl value.

Example Usage

We've made the necessary changes to the ServiceGenerator. Let's move on to actually utilize our new functioanlity:

public class DynamicBaseUrlActivity extends AppCompatActivity {

    public static final String TAG = "CallInstances";
    private Callback<ResponseBody> downloadCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_file_upload);

        downloadCallback = new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                Log.d(TAG, "server contacted at: " + call.request().url());
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d(TAG, "call failed against the url: " + call.request().url());
            }
        };

        // first request
        FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
        Call<ResponseBody> originalCall = downloadService.downloadFileWithFixedUrl();
        originalCall.enqueue(downloadCallback);

        // change base url
        ServiceGenerator.changeApiBaseUrl("http://development.futurestud.io/api");

        // new request against new base url
        FileDownloadService newDownloadService = ServiceGenerator.create(FileDownloadService.class);
        Call<ResponseBody> newCall = newDownloadService.downloadFileWithFixedUrl();
        newCall.enqueue(downloadCallback);
    }
}

In this activity we're showing you two requests to download a file from the server. If you're interested in the details on how to download files with Retrofit, head over to the relevant tutorial. After the first request is executed, we change the base url to our development environment with the new ServiceGenerator.changeApiBaseUrl() method. Lastly, we'll make the same download request again. When we start the app, we're getting the following logs:

D/CallInstances: server contacted at: http://futurestud.io/resource/example.zip  
D/CallInstances: server contacted at: http://development.futurestud.io/resource/example.zip  

This is exactly as we wanted Retrofit to behave. The first request still goes against the production server. The second request, after changing the base url, goes against our development server. Awesome!

When To Change the Base Url at Runtime

The demo code above simplifies things a little. We usually implement a button in the debug version of the app, where the tester can select the wished server environment. Thus, depending on your situation, you probably have to write a bit more code to decide when and to which base url Retrofit should change to.

Also, we only recommend doing this for debugging purposes. We don't think this is a good way of making your app work with various servers at the same time. If your app needs to deal with more than one API, look for a different version. The dynamic url tutorial might be a good start.

Lastly, please test if you can simply switch environments with your app. For example, if you store user and authentication information on the server side, switching the environment might cause problems. Your production database most likely does not contain the same users as your development database, correct? In our apps, we delete all relevant user data and force a new, fresh login after the tester changed the environment via a new base url.

Summary

In this tutorial, you've learned how to change the API base url at runtime. This can be incredibly useful if you're dealing with multiple API deployments. We've shown you the necessary enhancement to the ServiceGenerator class and how to make the necessary requests. You also have to be aware of the possible consequences when switching API deployments during runtime. Nevertheless, if you spent the hour to implement this for your app, you'll save days of compiling time!

If you think is useful or if you've any questions, let us know in the comments!


Explore the Library

Find interesting tutorials and solutions for your problems.