使用Retrofit 2.0和Dagger 2

时间:2016-04-08 11:10:05

标签: 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;     
}

这是API接口。

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

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

我使用懒惰注射来改造这样的

Lazy<Retrofit> retrofit

这样,Dagger只有在我可以调用

时才会注入依赖关系
retrofit.get()

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

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

8 个答案:

答案 0 :(得分:41)

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

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

或者您可以替换您的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);

答案 1 :(得分:33)

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

答案 2 :(得分:5)

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

}

将拦截器添加到OkHttpClient构建器中

val okHttpClient = OkHttpClient.Builder()
                .addInterceptor(HostSelectionInterceptor())
                .cache(null)
                .build()

答案 3 :(得分:2)

感谢@EpicPandaForce的帮助。如果有人面临IllegalArgumentException,这是我的工作代码。

# A tibble: 7 x 3
    obs   grp sub_grp
  <dbl> <chr>   <chr>
1     1     A      A1
2     2     A      A1
3     3     A      A2
4     4     A      A2
5     8     C      C1
6     9     C      C2
7    10     C      C3

答案 4 :(得分:2)

您可以使用无范围的提供方法实例化新对象。

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

答案 5 :(得分:2)

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

这将使用基本网址

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

@GET("/product/123")
fun fetchDataFromNetwork(): Call<Product>

这会忽略基本网址

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

@GET()
fun fetchDataFromNetwork(@Url url : String): Call<Product> //

答案 6 :(得分:0)

对于最新的Retrofit库,您可以简单地使用单例实例并使用retrofitInstance.newBuilder().baseUrl(newUrl)对其进行更改。无需创建另一个实例。

答案 7 :(得分:0)

请查看我的Dagger动态URL解决方法。

第一步:创建一个拦截器

import android.util.Patterns;

import com.nfs.ascent.mdaas.repo.network.ApiConfig;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class DomainURLInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();

        String requestUrl = original.url().toString();
        String PROTOCOL = "(?i:http|https|rtsp)://";
        String newURL = requestUrl.replaceFirst(PROTOCOL, "")
                .replaceFirst(Patterns.DOMAIN_NAME.toString(), "");
        newURL = validateBackSlash(newURL) ? ApiConfig.BASE_URL.concat(newURL) : newURL.replaceFirst("/", ApiConfig.BASE_URL);
        original = original.newBuilder()
                .url(newURL)
                .build();

        return chain.proceed(original);
    }

    private boolean validateBackSlash(String str) {
        if (!str.substring(str.length() - 1).equals("/")) {
            return true;
        }
        return false;
    }

}

第2步:

在模块中添加新创建的拦截器

    @Provides
    @Singlton
    DomainURLInterceptor getChangeURLInterceptor() {
        return new DomainURLInterceptor();
    }

步骤3: 将拦截器添加到HttpClient拦截器列表中

    @Provides
    @Singlton
    OkHttpClient provideHttpClient() {
        return new OkHttpClient.Builder()
                .addInterceptor(getChangeURLInterceptor())
                .readTimeout(ApiConfig.API_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                .connectTimeout(ApiConfig.API_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                .build();
    }

第4步:

    @Provides
    @Singlton
    Retrofit provideRetrofit() {
        return new Retrofit.Builder()
                .baseUrl(ApiConfig.BASE_URL) // this is default URl,
                .addConverterFactory(provideConverterFactory())
                .client(provideHttpClient())
                .build();
    }

注意:如果用户必须从设置中更改基本URL,请记住使用以下方法来验证新创建的URL:

    public final static boolean isValidUrl(CharSequence target) {
        if (target == null) {
            return false;
        } else {
            return Patterns.WEB_URL.matcher(target).matches();
        }
    }