Set dynamic base url using Retrofit 2.0 and Dagger 2

前端 未结 8 533
南旧
南旧 2020-11-27 12:36

I\'m trying to perform a login action using Retrofit 2.0 using Dagger 2

Here\'s how I set up Retrofit dependency

@Provides
@Singleton
Retrofit provid         


        
相关标签:
8条回答
  • 2020-11-27 12:41

    For latest Retrofit library, you can simply use singleton instance and change it with retrofitInstance.newBuilder().baseUrl(newUrl). No need to create another instance.

    0 讨论(0)
  • 2020-11-27 12:45

    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);
    
    0 讨论(0)
  • 2020-11-27 12:50

    Please look into my workaround for Dagger dynamic URL.

    Step1: Create an Interceptor

    import android.util.Patterns;
    
    import com.nfs.ascent.mdaas.repo.network.ApiConfig;
    
    import java.io.IOException;
    
    import okhttp3.Interceptor;
    import okhttp3.Request;
    import okhttp3.Response;
    
    public class DomainURLInterceptor implements Interceptor {
    
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();
    
            String requestUrl = original.url().toString();
            String PROTOCOL = "(?i:http|https|rtsp)://";
            String newURL = requestUrl.replaceFirst(PROTOCOL, "")
                    .replaceFirst(Patterns.DOMAIN_NAME.toString(), "");
            newURL = validateBackSlash(newURL) ? ApiConfig.BASE_URL.concat(newURL) : newURL.replaceFirst("/", ApiConfig.BASE_URL);
            original = original.newBuilder()
                    .url(newURL)
                    .build();
    
            return chain.proceed(original);
        }
    
        private boolean validateBackSlash(String str) {
            if (!str.substring(str.length() - 1).equals("/")) {
                return true;
            }
            return false;
        }
    
    }
    

    Step 2:

    add your newly created interceptor in your module

        @Provides
        @Singlton
        DomainURLInterceptor getChangeURLInterceptor() {
            return new DomainURLInterceptor();
        }
    

    step 3: add interceptor into list of HttpClient interceptors

        @Provides
        @Singlton
        OkHttpClient provideHttpClient() {
            return new OkHttpClient.Builder()
                    .addInterceptor(getChangeURLInterceptor())
                    .readTimeout(ApiConfig.API_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                    .connectTimeout(ApiConfig.API_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                    .build();
        }
    

    step 4:

        @Provides
        @Singlton
        Retrofit provideRetrofit() {
            return new Retrofit.Builder()
                    .baseUrl(ApiConfig.BASE_URL) // this is default URl,
                    .addConverterFactory(provideConverterFactory())
                    .client(provideHttpClient())
                    .build();
        }
    

    Note: if the user has to change the Base URL from settings, remember to validate the newly created URL with below method:

        public final static boolean isValidUrl(CharSequence target) {
            if (target == null) {
                return false;
            } else {
                return Patterns.WEB_URL.matcher(target).matches();
            }
        }
    
    0 讨论(0)
  • 2020-11-27 12:52

    This worked for me in Kotlin

    class HostSelectionInterceptor: Interceptor {
    
        override fun intercept(chain: Interceptor.Chain): Response {
    
            var request = chain.request()
    
            val host: String = SharedPreferencesManager.getServeIpAddress()
    
            val newUrl = request.url().newBuilder()
                .host(host)
                .build()
    
            request = request.newBuilder()
                .url(newUrl)
                .build()
    
            return chain.proceed(request)
        }
    
    }
    

    Add the interceptor to OkHttpClient builder

    val okHttpClient = OkHttpClient.Builder()
                    .addInterceptor(HostSelectionInterceptor())
                    .cache(null)
                    .build()
    
    0 讨论(0)
  • 2020-11-27 13:02

    You are able to instantiate new object using un-scoped provide method.

    @Provides
    LoginAPI provideAPI(Gson gson, OkHttpClient client, BaseUrlHolder baseUrlHolder) {
        Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)
                            .client(client)
                            .baseUrl(baseUrlHolder.get())
                            .build();
        return retrofit.create(LoginAPI.class);     
    }
    
    @AppScope
    @Provides
    BaseUrlHolder provideBaseUrlHolder() {
        return new BaseUrlHolder("https://www.default.com")
    }
    


    public class BaseUrlHolder {
        public String baseUrl;
    
        public BaseUrlHolder(String baseUrl) {
            this.baseUrl = baseUrl;
        }
    
        public String getBaseUrl() {
            return baseUrl;
        }
    
        public void setBaseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
        }
    }
    

    Now you can change base url via getting baseUrlHolder from the component

    App.appComponent.getBaseUrlHolder().set("https://www.changed.com");
    this.loginApi = App.appComponent.getLoginApi();
    
    0 讨论(0)
  • 2020-11-27 13:05

    Thanks to @EpicPandaForce for help. If someone is facing IllegalArgumentException, this is my working code.

    public class HostSelectionInterceptor implements Interceptor {
        private volatile String host;
    
        public void setHost(String host) {
            this.host = HttpUrl.parse(host).host();
        }
    
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            String reqUrl = request.url().host();
    
            String host = this.host;
            if (host != null) {
                HttpUrl newUrl = request.url().newBuilder()
                    .host(host)
                    .build();
                request = request.newBuilder()
                    .url(newUrl)
                    .build();
            }
            return chain.proceed(request);
        }
    }
    
    0 讨论(0)
提交回复
热议问题