Dagger + Retrofit dynamic URL

PROBLEMA

Necesito llamar a la API desde los dominios ingresados ​​por el USUARIO y necesito editar mi singleton Retrofit antes de la llamada de acuerdo con los datos insertados.

¿Hay alguna manera de “restablecer” mi singleton, forzándolo a volver a crear?

o

¿Hay alguna manera de actualizar mi baseUrl con mis datos (tal vez en Interceptor?) Justo antes de llamar?

CÓDIGO

Singletons

 @Provides @Singleton Retrofit provideRetrofit(SharedPreferences prefs) { String apiUrl = "https://%1s%2s"; apiUrl = String.format(apiUrl, prefs.getString(ACCOUNT_SUBDOMAIN, null), prefs.getString(ACCOUNT_DOMAIN, null)); OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(new HeaderInterceptor()) .build(); return new Retrofit.Builder() .baseUrl(apiUrl) .addConverterFactory(GsonConverterFactory.create()) .client(httpClient) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } @Provides @Singleton API provideAPI(Retrofit retrofit) { return retrofit.create(API.class); } 

API

 @FormUrlEncoded @POST("endpoint") Observable logIn(@Field("login") String login, @Field("password") String password); 

Cómo funciona ahora

Bueno, la idea era guardar datos de dominio del usuario a través de SharedPrefs antes de llamar a la API y modificar baseUrl con String formateado.

Veo 2 opciones aquí:

  • Use una daga como está previsto. Cree para cada baseUrl su propio cliente de Retrofit , o
  • Use un interceptor para modificar la solicitud antes de enviarla

Enfoque daga

Si tuviera urls de fuerza bruta, probablemente esta no sea la elección correcta, ya que depende de crear una nueva instancia de Retrofit para cada una.

Ahora, cada vez que cambia la URL, simplemente recrea el siguiente UrlComponent demostrado al proporcionarle un nuevo UrlModule .

Limpiar

Limpie su módulo @Singleton , de modo que proporcione GsonConverterFactory y RxJavaCallAdapterFactory para hacer un uso adecuado de daga y no recrear objetos compartidos.

 @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

Introduzca un @UrlScope para determinar el scope de las instancias de Retrofit .

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

Luego crea un subcomponente

 @SubComponent(modules=UrlModule.class) public interface UrlComponent {} 

Y un módulo para eso

 @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(); } } 

Utilice retrofit de ámbito

Crea una instancia del componente y úsalo.

 class Dagger { public void demo() { UrlModule module = new UrlModule(/*some url*/); SingletonComponent singletonComponent = DaggerSingletonComponent.create(); UrlComponent urlComponent = singletonComponent.plus(module); urlComponent.getRetrofit(); // done. } } 

Enfoque OkHttp

Proporcione un interceptor correctamente @Singleton ( @Singleton en este caso) e implemente la lógica correspondiente.

 @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 Implementar el MyApiInterceptor . Necesitarás tener un setter para la url base, y luego simplemente reescribir / modificar las solicitudes que se reciban.

Entonces, de nuevo, solo adelante y úsala.

 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(); } } 

Como tercer enfoque, también podría usar la reflexión para cambiar directamente la base de la URL, y agregué esta última para completarla.

Puede implementar BaseUrl y pasarlo en lugar de una URL fija. echa un vistazo a este enlace Otro enfoque es implementar Endpoint y hacer uso de setUrl() .So para cambiar el valor de un encabezado en tiempo de ejecución, entonces puedes usar el interceptor y agregarlo a OkHttp.