Using Retrofit for your Android app's networking can make your life so much easier. However, Retrofit's design requires a single Retrofit instance for each API with a different base URL. Consequently, if your app is talking to two or more APIs (under different URLs), you'll need to deal with at least two Retrofit instances.
If you've multiple Retrofit instances and don't pay attention, you'll lose app performance. In this tutorial, we'll show you how you can share OkHttp as the HTTP network layer between the Retrofit instances, and prevent this loss of performance.
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
Issue of Multiple Retrofit Instances
If your app uses multiple Retrofit instances, your declaration probably looks similar to this:
Retrofit retrofitApiV1 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit retrofitApiV2 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build();
The example code above uses the same domain, but it could also be two completely different domains, or just different subdomains. In either case, with the example code Retrofit would create a new OkHttp instance for each Retrofit instance. This default behavior leads to at least two separate OkHttp instances, which each keep their own request pooling, disk cache, routing logic, etc.. Because they don't share these, your overall app will unnecessarily lose performance.
Solution 1: Sharing Default OkHttp Instance
In order to make them share a single OkHttp instance, you can simply pass it explicitly on the builder:
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofitApiV1 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v1/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit retrofitApiV2 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v2/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
The small difference in the new code above causes a shared OkHttp instance and you win back the lost app performance.
If you need two different instances of the OkHttp client, the next section is for you.
Solution 2: Sharing Modified OkHttp Instances
Of course, our example code is simplified. You might already have custom OkHttp clients in your app. Are you stuck when the different APIs you need to talk to require each a custom OkHttp client, e.g., by having different interceptors?
No! You can still share the core (request pooling, disk cache, …) of the OkHttp client between all instances. In order to create custom OkHttp clients, but with a shared core, you can make shallow copies of an OkHttp client by calling .newBuilder()
:
// core will be shared across both clients
OkHttpClient baseOkHttpClient = new OkHttpClient();
// customize client for first Retrofit instance for API v1
OkHttpClient okHttpClientV1 = baseOkHttpClient
.newBuilder()
.followRedirects(false)
.build()
Retrofit retrofitApiV1 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v1/")
.client(okHttpClientV1)
.addConverterFactory(GsonConverterFactory.create())
.build();
// customize client for second Retrofit instance for API v2
OkHttpClient okHttpClientV2 = baseOkHttpClient
.newBuilder()
.addInterceptor(...)
.build()
Retrofit retrofitApiV2 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v2/")
.client(okHttpClientV2)
.addConverterFactory(GsonConverterFactory.create())
.build();
This way, you can customize each OkHttp client to the specific needs, including interceptors. okHttpClientV1
and okHttpClientV2
are customized to the needs of their respective API, but still use the same core. Sharing an OkHttp (core) instance across Retrofit clients can be particularly effective when the servers use HTTP/2, where additional advantages come into play when the APIs live physically on the same server.
Converters
Beware, the negative effect of not sharing resources is also happening (with a less dramatic effect) for converters. For example, this is the code we've shown you above:
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofitApiV1 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v1/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit retrofitApiV2 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v2/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
Here, we're creating two Gson instances by separately calling GsonConverterFactory.create()
. That means those two instances won't share any cache with each other. Instead, you should pass the same Gson (factory) instance to the two Retrofit instances:
GsonConverterFactory gsonFactory = GsonConverterFactory.create();
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofitApiV1 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v1/")
.client(okHttpClient)
.addConverterFactory(gsonFactory)
.build();
Retrofit retrofitApiV2 = new Retrofit.Builder()
.baseUrl("https://futurestud.io/v2/")
.client(okHttpClient)
.addConverterFactory(gsonFactory)
.build();
Don't forget to optimize your app's performance with these small details!
Summary
In this tutorial you've learned that it's important to share the OkHttp instance, or at least its core, and converters between various Retrofit instances. This optimizes your app's performance with a very little implementation detail!
Do you have further questions on this topic or about Retrofit in general? Just let us know on Twitter @futurestud_io or leave a comment below.
Enjoy coding & make it rock!
Additional Resources
- Video of Jake Wharton on this topic