Is it possible to add an Interceptor to an okHttp3 client that is already built? I use a singleton client and want to add an Interceptor to an already built client so I can add a custom cookie to the client without having to manually add it to each request.
Accessing the interceptors list directly from the client returns an immutable list.
This is what the newBuilder() method is designed for:
val modifiedClient = client.newBuilder()
.addNetworkInterceptor(MyCookieInterceptor())
.build()
You can customize a shared OkHttpClient instance with newBuilder(). This builds a client that shares the same connection pool, thread pools, and configuration. Use the builder methods to configure the derived client for a specific purpose.
See the first couple paragraphs of the javadoc for more details.
Related
I'm currently building a Retrofit object as follows:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
I don't need to pass it a client, i.e. no need to deal with the internals.
Now I would like to set a timeout, but all the examples I found involve creating an okHttpClient and assigning it to the Retrofit object.
Does it mean that okHttp is the de-facto client used by the system in all the situations?
Does it mean that okHttp is the de-facto client used by the system in all the situations?
Yes. Retrofit uses OkHttp for its network I/O. For example, in the Retrofit documentation, they mention in the section on R8/Proguard:
You might also need rules for OkHttp and Okio which are dependencies of this library
Analyzing some previously written code I have some questions concerning the set up with OkHttpClient. We did create a single OkHttpClient instance and reuse it for all of our HTTP calls. We execute REST API calls and caching is not needed.
However I do see some code in an interceptor
request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=${120}").build()
Question 1: Would this have any effect if caching is not enabled?
Secondly there is one API call that fetches configuration data and I feel it can benefit from caching.
Question 2: Can we enable caching for just one call, say, if we customize the client using newBuilder()?
It could have an effect on caches between yourself and the remote service. But this specific interceptor seems designed to force the request to only use the local cache, and not attempt to use the network.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache-control/only-if-cached/
To rephrase my question slightly ...if no cache is set on the original call then the request with the cache-control will not do anything right?
Typically you would not need an interceptor, instead you would apply the cache settings to the request.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache/
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().noCache().build())
.url("http://publicobject.com/helloworld.txt")
.build();
Not sure this addresses the second question. I'm asking if we have set up one single OkHttpClient instance can we have only some requests cached or the fact that we turn on cache for one request turns it on for all other requests onward?
With version 1.15 of the Parse Android SDK, the previous network interceptor model was removed in favor of OKHTTP interceptors. However, I can't find any information on how to add an interceptor. Creating an interceptor class is fine, but then what do I do with it? The Parse.Configuration.Builder class, which is where that used to be done, doesn't seem to have a method to do it.
There is nowhere to directly pass in an interceptor, but Parse.Configuration.Builder has a method clientBuilder. You make a OkHttpClient.Builder however you want, for example with a HttpLoggingInterceptor, and pass it to clientBuilder.
I am building my OkHttp in the application class and holding a static reference to it. This way, I have one instance of OkHttp at any given time.
okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(context.getCacheDir(), 50 * 1024 * 1024))
.addInterceptor(new HttpInterceptor())
.build();
Now, I am making two kind of requests, one requires OAuth2 while the other require the API key and secret supplied as parameters to the request URL. I am using an interceptor for the request that requires an OAuth2.
So the problem is the latter request uses resorts to the interceptor for authentication, thus the request fails. Is there any way I can tell it "See, ignore the interceptor for this request"? Or do I need two instances of OkHttpClient?
You will need two instances since after creating single instance wit interceptor attached to it will not ignore interceptor.
A hack can be to identify the url in the interceptor and ignore interceptor operation for that request.
The cleanest solution is to have a single OkHttp instance, but in your interceptor have the logic to decide how to authenticate.
This example ServiceInterceptor.java checks each request and decides how to authenticate in a pluggable manner. You could do something similar.
There are many benefits of this approach over different clients including
reuse of the executor
possibility that the connections can be coalesced for multiple hosts e.g. www.mysvc.com and api.mysvc.com
Support for redirection across hosts
How would the scope work with Auth Tokens? I cannot create my Retrofit instance until I can add an interceptor that signs it with my auth token. Therefore, I would like to create Retrofit when the auth tokens are available (after sign-in). How do I get scope working correctly in this situation?
Thanks a lot!
There is no best way of doing this, and it might also depend on how often you change / recreate your Retrofit instances.
What's better, or which better fits your use case depends very strongly on what you are trying to accomplish and how. There's many ways how what you are trying to achieve is possible, but in general you have 2 options
Create a new client for every retrofit instance (e.g. if you just log the user in once), so you would just add the client within the same scope
Create a #Singleton instance of okhttp3 and modify the client when required by using the newBuilder()
I think the first point is self explanatory, just create your client when you create retrofit, use the same scope and be done.
The second approach uses Okhttp3 feature of the newBuilder() method, by adding your interceptor to the okhttp client when creating your retrofit instance.
It would look something like this:
// Some singleton client to maybe also use in other parts of your app
#Singleton
OkHttpClient provideClient() { return new OkHttpClient(); }
// creating your retrofit client
#UserScope
Retrofit provideRetrofit(OkHtpClient client, Interceptor userInterceptor) {
return new Retrofit.Builder()
.client(client.newBuilder() // new builder to modify okhttp3
.addNetworkInterceptor(interceptor)
.build())
/* other settings */
.build();
}
If you get creative you can also just expose a setCredentials() method on your interceptor, then you can just create them once and reuse all the objects by adding them to the #Singleton scope. You'd then change your user by accessing and modifying your interceptor, albeit this is not a clean approach in my humble opinion.