使用 Retrofit 2.0 和 Dagger 2 设置动态基础 url
Set dynamic base url using Retrofit 2.0 and Dagger 2
我正在尝试使用 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();
}
我有三个不同的 urls 用户可以登录。所以我无法在设置 Retrofit 依赖项时设置静态 url。我在 Application class 上创建了 setUrl() 和 getUrl() 方法。用户登录后,我在调用 API 调用之前将 url 设置到 Application 上。
我这样使用延迟注入进行改造
Lazy<Retrofit> retrofit
这样,Dagger 仅在我可以调用时注入依赖关系
retrofit.get()
这部分效果很好。我将 url 设置为改造依赖项。但是,当用户输入错误的基数 url(比如 mywifi.domain.com)时,问题就出现了,理解它是错误的并改变它(比如 mydata.domain.com)。由于 Dagger 已经为 retrofit 创建了依赖,所以它不会再做。
所以我必须重新打开应用程序并输入正确的 url.
我阅读了有关使用 Dagger 在 Retrofit 上设置动态 urls 的不同帖子。就我而言,没有什么能真正奏效。我想念什么吗?
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);
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");
有关详细信息,请参阅此 link:https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
感谢@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);
}
}
动态 url 使用 Retrofit 2 和 Dagger 2
您可以使用 un-scoped 提供方法实例化新对象。
@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();
对于最新的 Retrofit 库,您可以简单地使用单例实例并将其更改为 retrofitInstance.newBuilder().baseUrl(newUrl)
。无需创建另一个实例。
这可能会迟到,但 Retrofit
允许您在使用 @Url
注释进行网络调用时使用动态 URL。
我还使用 Dagger2
在我的存储库中注入 Retrofit
实例,这个解决方案对我来说工作正常。
这将使用基数 url
由您在创建 Retrofit 实例时提供。
@GET("/product/123")
fun fetchDataFromNetwork(): Call<Product>
这里忽略基数 url
并使用 url 您将在 运行 时间提供此呼叫。
@GET()
fun fetchDataFromNetwork(@Url url : String): Call<Product> //
这在 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()
请查看我的 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();
}
}
我正在尝试使用 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();
}
我有三个不同的 urls 用户可以登录。所以我无法在设置 Retrofit 依赖项时设置静态 url。我在 Application class 上创建了 setUrl() 和 getUrl() 方法。用户登录后,我在调用 API 调用之前将 url 设置到 Application 上。
我这样使用延迟注入进行改造
Lazy<Retrofit> retrofit
这样,Dagger 仅在我可以调用时注入依赖关系
retrofit.get()
这部分效果很好。我将 url 设置为改造依赖项。但是,当用户输入错误的基数 url(比如 mywifi.domain.com)时,问题就出现了,理解它是错误的并改变它(比如 mydata.domain.com)。由于 Dagger 已经为 retrofit 创建了依赖,所以它不会再做。 所以我必须重新打开应用程序并输入正确的 url.
我阅读了有关使用 Dagger 在 Retrofit 上设置动态 urls 的不同帖子。就我而言,没有什么能真正奏效。我想念什么吗?
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);
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");
有关详细信息,请参阅此 link:https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
感谢@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);
}
}
动态 url 使用 Retrofit 2 和 Dagger 2
您可以使用 un-scoped 提供方法实例化新对象。
@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();
对于最新的 Retrofit 库,您可以简单地使用单例实例并将其更改为 retrofitInstance.newBuilder().baseUrl(newUrl)
。无需创建另一个实例。
这可能会迟到,但 Retrofit
允许您在使用 @Url
注释进行网络调用时使用动态 URL。
我还使用 Dagger2
在我的存储库中注入 Retrofit
实例,这个解决方案对我来说工作正常。
这将使用基数 url
由您在创建 Retrofit 实例时提供。
@GET("/product/123")
fun fetchDataFromNetwork(): Call<Product>
这里忽略基数 url
并使用 url 您将在 运行 时间提供此呼叫。
@GET()
fun fetchDataFromNetwork(@Url url : String): Call<Product> //
这在 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()
请查看我的 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();
}
}