Set dynamic base url using Retrofit 2.0 and Dagger 2
Support for this use-case was removed in Retrofit2. The recommendation is to use an OkHttp interceptor instead.
HostSelectionInterceptor
made by swankjesse
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
/** An interceptor that allows runtime changes to the URL hostname. */
public final class HostSelectionInterceptor implements Interceptor {
private volatile String host;
public void setHost(String host) {
this.host = host;
}
@Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = this.host;
if (host != null) {
//HttpUrl newUrl = request.url().newBuilder()
// .host(host)
// .build();
HttpUrl newUrl = HttpUrl.parse(host);
request = request.newBuilder()
.url(newUrl)
.build();
}
return chain.proceed(request);
}
public static void main(String[] args) throws Exception {
HostSelectionInterceptor interceptor = new HostSelectionInterceptor();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Request request = new Request.Builder()
.url("http://www.coca-cola.com/robots.txt")
.build();
okhttp3.Call call1 = okHttpClient.newCall(request);
okhttp3.Response response1 = call1.execute();
System.out.println("RESPONSE FROM: " + response1.request().url());
System.out.println(response1.body().string());
interceptor.setHost("www.pepsi.com");
okhttp3.Call call2 = okHttpClient.newCall(request);
okhttp3.Response response2 = call2.execute();
System.out.println("RESPONSE FROM: " + response2.request().url());
System.out.println(response2.body().string());
}
}
Or you can either replace your Retrofit instance (and possibly store the instance in a RetrofitHolder
in which you can modify the instance itself, and provide the holder through Dagger)...
public class RetrofitHolder {
Retrofit retrofit;
//getter, setter
}
Or re-use your current Retrofit instance and hack the new URL in with reflection, because screw the rules. Retrofit has a baseUrl
parameter which is private final
, therefore you can access it only with reflection.
Field field = Retrofit.class.getDeclaredField("baseUrl");
field.setAccessible(true);
okhttp3.HttpUrl newHttpUrl = HttpUrl.parse(newUrl);
field.set(retrofit, newHttpUrl);
Dagger + Retrofit dynamic URL
I see 2 options here:
- Use dagger as it is intended. Create for every
baseUrl
their ownRetrofit
client, or - Use an interceptor to modify the request before sending it
Dagger approach
If you were to brute force urls, this would probably not be the right choice, since it relies on creating a new Retrofit
instance for each.
Now every time the url changes, you just recreate the following demonstrated UrlComponent
by supplying it with a new UrlModule
.
Clean up
Clean your @Singleton
module, so that it provides GsonConverterFactory
, and RxJavaCallAdapterFactory
to make proper use of dagger and not recreate shared objects.
@Module
public class SingletonModule {
@Provides
@Singleton
GsonConverterFactory provideOkHttpClient() {/**/}
@Provides
@Singleton
RxJavaCallAdapterFactory provideOkHttpClient() {/**/}
}
@Singleton
@Component(modules = SingletonModule.class)
interface SingletonComponent {
// sub component
UrlComponent plus(UrlModule component);
}
Url Scoped
Introduce a @UrlScope
to scope your Retrofit
instances.
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlScope {
}
Then create a subcomponent
@SubComponent(modules=UrlModule.class)
public interface UrlComponent {}
And a module for it
@Module
class UrlModule {
private final String mUrl;
UrlModule(String url) { mUrl = url; }
@Provides
String provideUrl() {
return mUrl;
}
@Provides
@UrlScope
OkHttpClient provideOkHttpClient(String url) {
return new OkHttpClient.Builder().build();
}
@Provides
@UrlScope
Retrofit provideRetrofit(OkHttpClient client) {
return new Retrofit.Builder().build();
}
}
Use scoped Retrofit
Instantiate the component and use it.
class Dagger {
public void demo() {
UrlModule module = new UrlModule(/*some url*/);
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
UrlComponent urlComponent = singletonComponent.plus(module);
urlComponent.getRetrofit(); // done.
}
}
OkHttp approach
Provide a properly scoped interceptor (@Singleton
in this case) and implement the corresponding logic.
@Module
class SingletonModule {
@Provides
@Singleton
GsonConverterFactory provideGsonConverter() { /**/ }
@Provides
@Singleton
RxJavaCallAdapterFactory provideRxJavaCallAdapter() { /**/ }
@Provides
@Singleton
MyApiInterceptor provideMyApiInterceptor() { /**/ }
@Provides
@Singleton
OkHttpClient provideOkHttpClient(MyApiInterceptor interceptor) {
return new OkHttpClient.Builder().build();
}
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient client) {
return new Retrofit.Builder().build();
}
}
@Singleton
@Component(modules = SingletonModule.class)
interface SingletonComponent {
Retrofit getRetrofit();
MyApiInterceptor getInterceptor();
}
todo Implement the MyApiInterceptor
. You will need to have a setter for the base url, and then just rewrite / modify the requests coming through.
Then, again, just go ahead and use it.
class Dagger {
public void demo() {
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MyService service = singletonComponent.getRetrofit().create(MyService.class);
MyApiInterceptor interceptor = singletonComponent.getInterceptor();
interceptor.setBaseUrl(myUrlA);
service.doA();
interceptor.setBaseUrl(someOtherUrl);
service.doB();
}
}
As a third approach, you could also use reflection to just directly change base the base URL—I added this last just for completeness.
Related Topics
How to Force the Action Bar to Be at the Bottom in Ics
Visual Studio (2015) Emulator for Android Not Working - Xde.Exe - Exit Code 3
How to Get Access Token After User Is Signed in from Gmail in Android
Android:Static Fields and Memory Leaks
Error: Status{Statuscode=Developer_Error, Resolution=Null}
How to Open a PDF via Intent from Sd Card
How to Set Time to Device Programmatically
How to Resolve Target 'Android-Xx'
Android: How to Select Texts from Webview
How to Scale/Resize Text to Fit a Textview
Geocoder.Getfromlocation Throws Ioexception on Android Emulator
Camera Activity Returning Null Android
How to Open or Simulate a Click on an Android Preference, Created with Xml, Programmatically
Led Flashlight on Galaxy Nexus Controllable by What API
Host-Based Card Emulation with Fixed Card Id
Android.Content.Context.Getpackagename()' on a Null Object Reference