2016-04-08 21 views
16

Ich versuche, eine Login-Aktion auszuführen 2.0 mit Retrofit mit Dagger 2Set dynamische URL Base Retrofit 2.0 and Dagger 2

Hier ist, wie ich

@Provides 
@Singleton 
Retrofit provideRetrofit(Gson gson, OkHttpClient client) { 
    Retrofit retrofit = new Retrofit.Builder() 
          .addConverterFactory(GsonConverterFactory.create(gson) 
          .client(client) 
          .baseUrl(application.getUrl()) 
          .build(); 
    return retrofit;  
} 

Retrofit Abhängigkeit Hier aufgebaut ist die API-Schnittstelle .

interface LoginAPI { 
    @GET(relative_path) 
    Call<Boolean> logMe(); 
} 

Ich habe drei verschiedene Basis-URLs, in denen sich Benutzer anmelden können. Daher kann ich beim Einrichten der Retrofit-Abhängigkeit keine statische URL festlegen. Ich habe eine setUrl() - und getUrl() -Methode für die Application-Klasse erstellt. Nach der Benutzeranmeldung setze ich die URL auf Application, bevor ich den API-Aufruf aufruft.

ich faul Injektion für die Nachrüstung wie diese

Lazy<Retrofit> retrofit 

auf diese Weise verwenden, spritzt Dagger Abhängigkeit nur, wenn ich

retrofit.get() 

Dieser Teil gut funktioniert aufrufen können. Ich habe die URL zur Nachrüstung eingerichtet. Das Problem tritt jedoch auf, wenn der Benutzer eine falsche Basis-URL eingibt (z. B. mywifi.domain.com), versteht, dass es falsch ist, und ändert es (z. B. mydata.domain.com). Da Dagger bereits die Abhängigkeit für Retrofit geschaffen hat, wird es nicht mehr tun. Also muss ich die App wieder öffnen und die richtige URL eingeben.

Ich las verschiedene Beiträge zum Einrichten von dynamischen URLs auf Retrofit mit Dolch. Nichts hat in meinem Fall gut funktioniert. Vermisse ich etwas?

Antwort

26

Support for this use-case was removed in Retrofit2. The recommendation is to use an OkHttp interceptor instead.

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

Oder Sie können entweder Ihre Retrofit Instanz ersetzen (und speichern möglicherweise die Instanz in einem RetrofitHolder, in dem Sie die Instanz ändern sich, und geben Sie den Halter durch Dagger) ...

public class RetrofitHolder { 
    Retrofit retrofit; 

    //getter, setter 
} 

Oder verwenden Sie Ihre aktuelle Retrofit-Instanz und hacken Sie die neue URL mit Reflektion ein, denn verschrauben Sie die Regeln. Retrofit hat einen baseUrl Parameter, der private final ist, deshalb können Sie nur mit Reflexion darauf zugreifen.

Field field = Retrofit.class.getDeclaredField("baseUrl"); 
field.setAccessible(true); 
okhttp3.HttpUrl newHttpUrl = HttpUrl.parse(newUrl); 
field.set(retrofit, newHttpUrl); 
+0

Danke für die Antwort

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

Jetzt können Sie Basis-URL ändern! Ich hatte eine graue Vorstellung von der zweiten Option, die Sie erwähnt haben. Ich denke, die zweite Option ist viel besser, da sie nicht jede Anfrage abfangen muss, die von der App gesendet wird. Welche Option ist Ihrer Meinung nach empfehlenswert? – Renjith

+1

Die offizielle Empfehlung ist die erste. Eine mögliche Problemumgehung ist die zweite. Der dritte ist ein hässlicher Hack, aber ich liebe Reflexion, hah. Ehrlich gesagt, würde ich mit "RealmHolder" von einem Anbieter mit beschränkter Reichweite, was ich gemacht habe, wahrscheinlich "2" machen. – EpicPandaForce

+0

Aber wenn Sie diese zwei Anforderungen gleichzeitig parallel ausführen? Wird es einen Fehler geben, weil zwei Anfragen im ersten Beispiel dieselbe baseUrl haben? –

13

Retrofit2 Bibliothek kommt mit einer @Url Anmerkung. Sie können baseUrl wie dies außer Kraft setzen:

API-Schnittstelle:

public interface UserService { 
    @GET 
    public Call<ResponseBody> profilePicture(@Url String url); 
} 

Und die API wie folgt aufrufen:

Retrofit retrofit = Retrofit.Builder() 
    .baseUrl("https://your.api.url/"); 
    .build(); 

UserService service = retrofit.create(UserService.class); 
service.profilePicture("https://s3.amazon.com/profile-picture/path"); 

Für weitere Informationen zu diesem Link finden: https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests

+0

Ausnahme: Url kann nicht mit GET URL (Parameter # 1) verwendet werden – TeeTracker

+0

@TeeTracker Frage ist über Retrofit + DI –

+0

Dies ist, was ich gesucht habe, wirklich praktisch, wenn Sie eine benutzerdefinierte URL mit @Url verwenden können – stevyhacker

0

Danke an @EpicPandaForce für Hilfe. Wenn jemand mit IllegalArgumentException konfrontiert wird, ist dies mein Arbeitscode.

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

Sie sind in der Lage, neue Objekte zu instanziieren, indem Sie die Methode un-scoped verwenden. über immer baseUrlHolder von der Komponente

App.appComponent.getBaseUrlHolder().set("https://www.changed.com"); 
this.loginApi = App.appComponent.getLoginApi();