Установите динамический базовый url с помощью Retrofit 2.0 и Dagger 2
Я пытаюсь выполнить действие входа с помощью Retrofit 2.0 с помощью Dagger 2
Здесь, как я устанавливаю зависимость RetoFit
@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();
}
У меня есть три разных базовых URL-адреса, которые могут войти в систему. Поэтому я не могу установить статический URL-адрес при настройке зависимостей Retrofit. Я создал методы setUrl() и getUrl() класса Application. После входа в систему я устанавливаю URL-адрес приложения перед вызовом вызова API.
Я использую ленивую инъекцию для такой модификации
Lazy<Retrofit> retrofit
Таким образом, кинжал вводит зависимость только тогда, когда я могу вызвать
retrofit.get()
Эта часть работает хорошо. Я получил URL-адрес, чтобы модифицировать зависимость. Однако проблема возникает, когда пользователь вводит неверный базовый url (скажем, mywifi.domain.com), понимает его неправильно и меняет его (скажем, на mydata.domain.com). Поскольку Кинжал уже создал зависимость для модификации, он больше не будет делать этого.
Поэтому мне нужно снова открыть приложение и ввести правильный URL-адрес.
Я читал разные сообщения для настройки динамических URL-адресов на "Дооснащение" с помощью кинжала. В моем случае ничего хорошего не получилось. Мне что-то не хватает?
Ответы
Ответ 1
Поддержка этого прецедента была удалена в 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());
}
}
Или вы можете либо заменить экземпляр Retrofit (и, возможно, сохранить экземпляр в RetrofitHolder
, в котором вы можете изменить сам экземпляр, и предоставить держателю через кинжал)...
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);
Ответ 2
Библиотека 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
Ответ 3
Спасибо @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);
}
}
Ответ 4
Вы можете создать новый объект с помощью метода предоставления без границ.
@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;
}
}
Теперь вы можете изменить базовый URL-адрес, получая baseUrlHolder из компонента
App.appComponent.getBaseUrlHolder().set("https://www.changed.com");
this.loginApi = App.appComponent.getLoginApi();
Ответ 5
В последней версии библиотеки Retrofit вы можете просто использовать одноэлементный экземпляр и изменить его с помощью retrofitInstance.newBuilder().baseUrl(newUrl)
. Нет необходимости создавать другой экземпляр.