1. ホーム
  2. android

Retrofit 2.0とDagger 2を使用した動的なベースURLの設定

2023-10-14 11:42:50

質問

Retrofit 2.0を使ってDagger 2でログインアクションを行おうとしています。

Retrofitの依存関係を設定する方法は以下の通りです。

@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を設定するようにしました。

Retrofitのレイジーインジェクションはこんな感じで使っています。

Lazy<Retrofit> retrofit

を呼び出せるときだけ、Daggerは依存性を注入します。

retrofit.get()

この部分はうまくいきました。私は、urlを後付けの依存性に設定しました。しかし、ユーザーが間違ったベースURL(例えばmywifi.domain.com)を入力し、それが間違ったものだと理解して変更(例えばmydata.domain.comに)すると問題が発生します。Daggerはすでに後付けの依存関係を作成しているため、再度変更することはできません。 そのため、アプリを再度開き、正しいURLを入力する必要があります。

Daggerを使ってRetrofitに動的URLを設定するためのさまざまな投稿を読みました。私の場合、うまくいったものがありません。何か見落としているのでしょうか?

どのように解決するのですか?

このユースケースのサポートは Retrofit2 で削除されました。代わりに OkHttp インターセプターを使うことが推奨されています。

HostSelectionInterceptor 作成者 スワンクジェス

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をリフレクションでハックすることもできます。Retrofitは baseUrl というパラメータがあります。 private final であるため、リフレクションでのみアクセスすることができます。

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