web-dev-qa-db-ja.com

Retrofit 2.0とDagger 2を使用して動的なベースURLを設定する

Dagger 2を使用してRetrofit 2.0を使用してログインアクションを実行しようとしています

レトロフィットの依存関係を設定する方法は次のとおりです

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

これがAPIインターフェースです。

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

ユーザーがログインできる3つの異なるベースURLがあります。そのため、Retrofit依存関係の設定中に静的なURLを設定することはできません。 ApplicationクラスでsetUrl()およびgetUrl()メソッドを作成しました。ユーザーがログインしたら、API呼び出しを呼び出す前にURLをApplicationに設定します。

私はこのような改造のために怠laな注入を使用します

Lazy<Retrofit> retrofit

そうすれば、Daggerは、呼び出し可能な場合にのみ依存関係を注入します

retrofit.get()

この部分はうまく機能します。 URLをレトロフィット依存に設定しました。ただし、ユーザーが間違ったベースURL(mywifi.domain.comなど)を入力し、それが間違っていることを理解して変更すると(mydata.domain.comなどに)問題が発生します。 Daggerは既に改造のための依存関係を作成しているため、再度行うことはありません。そのため、アプリを再度開き、正しいURLを入力する必要があります。

Daggerを使用してRetrofitで動的URLを設定するためのさまざまな投稿を読みました。私の場合、何もうまくいきませんでした。私は何かを見逃していますか?

43
Renjith

この使用例のサポートはRetrofit2で削除されました。代わりにOkHttpインターセプターを使用することをお勧めします。

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

または、Retrofitインスタンスを置き換えることができます(インスタンスを変更できるRetrofitHolderにインスタンスを保存し、Daggerを介してホルダーを提供できます)...

public class RetrofitHolder {
   Retrofit retrofit;

   //getter, setter
}

または、現在のRetrofitインスタンスを再利用して、新しいURLをリフレクションでハッキングします。これは、ルールを台無しにしているためです。レトロフィットにはbaseUrlパラメーターがあり、これはprivate final、したがって、リフレクションでのみアクセスできます。

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

Retrofit2ライブラリには@Url注釈。次のようにbaseUrlをオーバーライドできます。

APIインターフェース:

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

そして次のようにAPIを呼び出します:

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");

詳細については、次のリンクを参照してください。 https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests

41
Jaydev Mehta

助けてくれた@EpicPandaForceに感謝します。誰かがIllegalArgumentExceptionに直面している場合、これは私の作業コードです。

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);
    }
}
2
indra

スコープのない提供メソッドを使用して、新しいオブジェクトをインスタンス化できます。

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

これで、コンポーネントからbaseUrlHolderを取得してベースURLを変更できます

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