使用Retrofit 2.0和Dagger 2设置动态基本URL

Ren*_*ith 45 android dagger-2 retrofit2

我正在尝试使用Dagger 2使用Retrofit 2.0执行登录操作

这是我如何设置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;     
}
Run Code Online (Sandbox Code Playgroud)

这是API接口.

interface LoginAPI {
   @GET(relative_path)
   Call<Boolean> logMe();
}
Run Code Online (Sandbox Code Playgroud)

我有三个不同的基本网址用户可以登录.因此,在设置Retrofit依赖项时,我无法设置静态URL.我在Application类上创建了一个setUrl()和getUrl()方法.用户登录后,我在调用API调用之前将url设置为Application.

我像这样使用懒惰注射进行改造

Lazy<Retrofit> retrofit
Run Code Online (Sandbox Code Playgroud)

这样,只有当我可以打电话时,Dagger才会注入依赖关系

retrofit.get()
Run Code Online (Sandbox Code Playgroud)

这部分效果很好.我把url设置为改进依赖.但是,当用户键入错误的基本URL(例如,mywifi.domain.com)时,会出现问题,理解它是错误的并更改它(比如mydata.domain.com).由于Dagger已经为改造创建了依赖关系,因此它不会再做了.所以我必须重新打开应用程序并输入正确的URL.

我阅读了不同帖子,使用Dagger在Retrofit上设置动态网址.在我的情况下,没有什么比这更好的了.我想念什么吗?

Epi*_*rce 46

在Retrofit2中删除了对此用例的支持.建议使用OkHttp拦截器.

HostSelectionInterceptor 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());
  }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以替换您的Retrofit实例(并且可能将实例存储RetrofitHolder在您可以修改实例本身的实例中,并通过Dagger提供持有者)...

public class RetrofitHolder {
   Retrofit retrofit;

   //getter, setter
}
Run Code Online (Sandbox Code Playgroud)

或者重新使用当前的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);
Run Code Online (Sandbox Code Playgroud)

  • 但是请确保,在替换url之后,您便拥有了自己的路径。示例:http://oldurl.com/api/v2/someapi/您希望将域名替换为newurl.com后将其更改为http://newurl.com/,但是您可以看到此处没有api路径。对我来说最好的解决方案:最终字符串BASE_URL =“ http:// {subdomain} .domain.com /字符串url = request.url()。toString()。replace(” {domain}“,domain);在您的拦截器中:request = request.newBuilder()。url(url).build(); (2认同)

小智 43

Retrofit2库附带一个@Url注释.您可以baseUrl像这样覆盖:

API接口:

public interface UserService {  
    @GET
    public Call<ResponseBody> profilePicture(@Url String url);
}
Run Code Online (Sandbox Code Playgroud)

并调用这样的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");
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅以下链接:https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests

  • 异常:Url 不能与 GET URL 一起使用(参数 #1) (6认同)
  • @TeeTracker您可以使用@GET(NEW_BASE_URL +“ / v1 / whatever”)或@GET和参数@Url String url。不能同时使用。 (3认同)
  • 糟糕的解决方案 (2认同)

Nan*_*amy 7

这在 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)
    }

}
Run Code Online (Sandbox Code Playgroud)

将拦截器添加到 OkHttpClient 构建器

val okHttpClient = OkHttpClient.Builder()
                .addInterceptor(HostSelectionInterceptor())
                .cache(null)
                .build()
Run Code Online (Sandbox Code Playgroud)


小智 6

感谢@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);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

这可能会晚,但Retrofit允许您在使用@Url注释进行网络调用时使用动态 URL 。我还使用在我的存储库中Dagger2注入Retrofit实例,这个解决方案对我来说很好用。

这将使用基本网址

由您在创建 Retrofit 实例时提供。

@GET("/product/123")
fun fetchDataFromNetwork(): Call<Product>
Run Code Online (Sandbox Code Playgroud)

这忽略了基本网址

并使用您将在运行时提供此调用的 url。

@GET()
fun fetchDataFromNetwork(@Url url : String): Call<Product> //
Run Code Online (Sandbox Code Playgroud)

  • 这是行不通的。我收到此错误“@Url 无法与 @GET URL(参数 #1)一起使用” (2认同)