Mikhail
Mikhail

Reputation: 808

Dagger 2 and Retrofit 2 change base URL

In my case, I need to change URL dynamically, but I don't want to create 2 instances of the retrofit client. I'm trying to change base URL via interceptor modifications, but retrofit still uses the old value. What am I doing wrong?

App.java

public class App extends Application {
    private static AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent =
                DaggerAppComponent
                        .builder()
                        .appModule(new AppModule(this))
                        .build();

    }

    @NonNull
    public static App get(@NonNull Context context) {
        return (App) context.getApplicationContext();
    }

    public static AppComponent getAppComponent() {
        return appComponent;
    }
}

AppModule.java

@Module
public class AppModule {
    private Context context;

    public AppModule(Context context) {
        this.context = context;
    }

    @Provides
    @Singleton
    Context provideContext() {
        return context;
    }
    
}

NetModule.java

@Module
public class NetModule {

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }
    @Provides
    @Singleton
    MainInterceptor provideMyApiInterceptor() {
        return MainInterceptor.get();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.readTimeout(15, TimeUnit.SECONDS);
        client.connectTimeout(20, TimeUnit.SECONDS);
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("first.url.com")
                .client(okHttpClient)
                .build();
    }
}

NetComponent.java

@Singleton
@Component(modules = NetModule.class)
public interface NetComponent {
    Retrofit getRetrofit();

    MainInterceptor getInterceptor();
}

MainInterceptor.class

public class MainInterceptor implements Interceptor {
    private static MainInterceptor sInterceptor;
    private String mScheme;
    private String mHost;

    public static MainInterceptor get() {
        if (sInterceptor == null) {
            sInterceptor = new MainInterceptor();
        }
        return sInterceptor;
    }
    
    private MainInterceptor() {
    }

    public void setInterceptor(String url) {
        HttpUrl httpUrl = HttpUrl.parse(url);
        mScheme = httpUrl.scheme();
        mHost = httpUrl.host();
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();

        if (mScheme != null && mHost != null) {
            HttpUrl newUrl = original.url().newBuilder()
                    .scheme(mScheme)
                    .host(mHost)
                    .build();
            original = original.newBuilder()
                    .url(newUrl)
                    .build();
        }
        return chain.proceed(original);
    }
}

And this is the code to initialize the component in some class.

NetComponent component= DaggerNetComponent.create();
DataService service = component.getRetrofit().create(DataService.class);
MainInterceptor interceptor = component.getInterceptor();
interceptor.setInterceptor("second.url.com");
service.getSomeData();

After that, the URL is still "first.url.com"

Upvotes: 7

Views: 5754

Answers (4)

div
div

Reputation: 1533

If you already have a base URL set but you want to override it for just one API call and not all of them, it can be done pretty easily.

@PUT("https://my-api.com/user")
fun cancelOrder(@Path("user") user: String): Single<MyResponse>

Works the same way for POST and GET as well.

Upvotes: -1

eurosecom
eurosecom

Reputation: 2992

Add Interceptor in your AppModule

@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache, MainInterceptor interceptor) {
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .cache(cache)
            .build();
    return okHttpClient;
}

and then in your activity or presenter set url before calling retrofit service

mInterceptor.setInterceptor(urlname);
mRetrofitService.call();

Upvotes: 1

Siarhei
Siarhei

Reputation: 2436

The simple way is to use @Named:

@Provides
@Singleton
@Named("retrofit_1")
Retrofit provideRetrofit1(Gson gson, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                ...
                .baseUrl("url_1.com")
                ...;}

@Provides
@Singleton
@Named("retrofit_2")
Retrofit provideRetrofit2(Gson gson, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                ...
                .baseUrl("url_2.com")
                ...;}

then use the same @Named:

@Provides
IApi1 provideApi_1(@Named("retrofit_1") RestAdapter adapter){....}

@Provides
IApi2 provideApi_2(@Named("retrofit_2") RestAdapter adapter){....}

Upvotes: 8

phatnhse
phatnhse

Reputation: 3940

When you provide a Retrofit, you already set baseUrl "first.url.com"

return new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl("first.url.com")
            .client(okHttpClient) //<------ It not OkHttp with your MainInterCeptor
            .build();

And fun fact is, your MainInterceptor which is set-up with new url is unrelated to your Retrofit, because Retrofit was built with OkHttp

@Provides
@Singleton
OkHttpClient provideOkhttpClient() {
    OkHttpClient.Builder client = new OkHttpClient.Builder();
    client.readTimeout(15, TimeUnit.SECONDS);
    client.connectTimeout(20, TimeUnit.SECONDS);
    return client.build();
}

If you want to dynamically change your BASE_URL, there are many ways for you to do that.

public class ApiConstant {

    public static String BASE_URL = "yourUrl";

    private ApiConstant() {
    }
}

Create a @Scope for Retrofit because it can be changed

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}

Then use it when building Retrofit

@Provides
@PerActivity
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
    return new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl("first.url.com")
            .client(ApiConstant.BASE_URL)
            .build();
}

Then change the BASE_URL when init DaggerActivityComponent. (Or create a new @Scope for new url)

Read this documentation for more detail

Hope this help!

Upvotes: 2

Related Questions