In retrofit 2.0 i want to use only one url .The url is same as base url as that of #GET in interface.I am facing the problem for getting the response.If Any one have better solution for using the whole url in #GET then please suggest the solution.
here is the code
public class RestClient {
private static ApiInterface apiInterface ;
private static String baseUrl = "here is my whole base url";
public static ApiInterface getClient() {
if (apiInterface == null) {
OkHttpClient okClient = new OkHttpClient();
okClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response;
}
});
Retrofit client = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverter(String.class, new ToStringConverter())
.client(okClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiInterface = client.create(ApiInterface.class);
Log.e("RETROFIT RESPONCE IS...", client.toString());
}
return ApiInterface ;
}
public interface ApiInterface {
#Headers("User-Agent: Retrofit2.0Tutorial-App")
#GET("here is my whole base url”)
Call<EventResult> getEvent();
}
}
With retrofit 2 is possible to use the #Url annotation. Let's assume your Retrofit configuration is
Retrofit builder = new Retrofit.Builder()
.baseUrl("http://wwww.example.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
Test r = builder.create(Test.class);
you declare your interface:
public interface Test {
#GET
Call<Example> getTest(#Url String url);
}
and for getTest you don't want to use the baseUrl you declared in the configuration. The #Url will ignore the baseUrl you declared and will use the one you provide as argument
I don't think it's possible since BaseURL is mandatory in Retrofit Builder and even you supply the builder with the full URL the builder will parse it and save only the BaseURL. I guess the reason why they do this is to keep it simple and consistent.
for reference you can see the source code here
Related
I have a scenario where I have to call an API with the same base URL, e.g. www.myAPI.com but with a different baseUrl.
I have an instance of Retrofit 2 which is built via a Builder:
return new Retrofit.Builder()
.baseUrl(FlavourConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build();
The FlavourConstants.BASE_URL looks like this:
public static final String BASE_URL = "http://myApi.development:5000/api/v1/";
For some WebRequests, I must call the same API but on others, I must call it from a completely different BaseUrl. How do I change the Retrofit instance to therefore point to a different URL during runtime?
The Retrofit instance doesn't have a .setBaseUrl or setter or anything similar as it's built via a Builder.
Any ideas?
Lucky for you Retrofit have a simple solution for that:
public interface UserManager {
#GET
public Call<ResponseBody> userName(#Url String url);
}
The url String should specify the full Url you wish to use.
Retrofit 2.4, MAY 2019
Two simple solution for this hassle are:
Hardcode the new URL, while leaving the base URL as it is:
#GET("http://example.com/api/")
Call<JSONObject> callMethodName();
Pass the new URL as an argument, while leaving the base URL as it is:
#GET
Call<JSONObject> callMethodName(#Url String url);
N.B: These methods work for GET or POST. However, this solution is only efficient if you just need to use an exception of one or two different URLs than your base URL. Otherwise, things can get a little messy in terms of code neatness.
If your project demands fully dynamically generated base URLs then you can start reading this.
Also there is a such hack in Kotlin while defining base url
e.g.
#FormUrlEncoded
#Headers("Accept: application/json")
#POST
suspend fun login(
baseUrl: String,
#Field("login") login: String,
#Field("password") password: String
#Url url: String = "$baseUrl/auth"
): ResponseAuth
It's not working. Throws:
java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #1)
The only way is suggested by Jake Wharton https://github.com/square/retrofit/issues/2161#issuecomment-274204152
Retrofit.Builder()
.baseUrl("https://localhost/")
.create(ServerApi::class.java)
class DomainInterceptor : Interceptor {
#Throws(Exception::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
return chain.proceed(
request.newBuilder()
.url(
request.url.toString()
.replace("localhost", "yourdomain.com:443")
.toHttpUrlOrNull() ?: request.url
)
// OR
//.url(HttpUrl.parse(request.url().toString().replace("localhost", "yourdomain.com:443")) ?: request.url())
.build()
)
}
}
The easiest (but not the most performant) way to change the Retrofit2 base URL at runtime is to rebuild the retrofit instance with the new url:
private Retrofit retrofitInstance = Retrofit.Builder().baseUrl(FlavourConstants.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();
public void setNewBaseUrl(String url) {
retrofitInstance = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient).build();
}
...
retrofitInstance.create(ApiService.class);
Alternatively, if you are using OkHttp with Retrofit, you can add an OkHttp interceptor like this one when building your OkHttp client:
HostSelectionInterceptor hostInterceptor = new HostSelectionInterceptor();
hostInterceptor.setHost(newBaseUrl);
return new OkHttpClient.Builder()
.addInterceptor(hostInterceptor)
.build();
I just used the below function when i faced this problem. but i was on hurry and i believe that i have to use another and i was using "retrofit2:retrofit:2.0.2"
public static Retrofit getClient(String baseURL) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
} else {
if (!retrofit.baseUrl().equals(baseURL)) {
retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
return retrofit;
}
[Update]
I have found this link that explain the #Url that can be sent as a parameter and i believe it is more professional than my old solution.
Please find below the scenario:
interface APIService{
#POST
Call<AuthenticationResponse> login(#Url String loginUrl,[other parameters])
}
And below is the method in the class that provide the retrofit object
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl("http://baseurl.com") // example url
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Then you can call the method as below:
APIInterface apiInterface = ApiClient.getClient2().create(ApiInterface.class);
apiInterface.login("http://tempURL.com").enqueue(......);
You should use interceptor like this:
class HostSelectionInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
apiHost?.let { host ->
val request = chain.request()
val newUrl = request.url.newBuilder().host(host).build()
val newRequest = request.newBuilder().url(newUrl).build()
return chain.proceed(newRequest)
}
throw IOException("Unknown Server")
}
}
You just need to change at runtime the apiHost variable (var apiHost = "example.com"). Then add this interceptor to OkHttpClient builder:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HostSelectionInterceptor())
.build()
Ok , if I dont remember wrong the docs of Retrofit says you can point to another URL if you just simply add in your interface servicse the full url of the ws, that is different fomr the BASE_URL in Retrofit Builder. One example...
public interface UserManager {
#GET("put here ur entire url of the service")
public Call<ResponseBody> getSomeStuff();
}
A solution is to have two distinct instance of retrofit, one for your FLAVOURED base URL and another for the other base URL.
So just define two functions :
public Retrofit getFlavouredInstance() {
return new Retrofit.Builder().baseUrl(FlavourConstants.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();
}
public Retrofit getOtherBaseUrl() {
return Retrofit.Builder().baseUrl(OTHER_BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();
}
and after you just have to use the right one.
Please try the following code:
private void modify(String url) throws Exception {
Class mClass = retrofit.getClass();
Field privateField = mClass.getDeclaredField("baseUrl");
if (privateField != null) {
privateField.setAccessible(true);
System.out.println("Before Modify:MSG = " + retrofit.baseUrl().url().getHost());
privateField.set(retrofit, HttpUrl.parse(url));
System.out.println("After Modify:MSG = " + retrofit.baseUrl().url().getHost());
}
}
You can regenerate the DaggerAppComponent after changing your apiUrl it will generate a new instance of providerRetrofit with the new url
DaggerAppComponent.builder() .application(this) .build() Log.init( LogConfiguration .Builder() .tag("...") .logLevel(LogLevel.NONE) .build() )
I need change the URL base in retrofit, i'm using koin to create a retrofit module on app startup and i want change this url in runtime.
I already tried change the baseUrl("http://192.168.192.168/") to baseUrl("http://")and change the url on retrofit call but my app crashs and return illegal URL error.
This is my fun to create the builder
fun createRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("http://192.168.192.168/")//i need change this at runtime
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
create a bean to my module
val retrofitModule: Module = applicationContext {
bean { createRetrofit(get()) }
}
and start the koin:
startKoin(application = this,
modules = listOf(retrofitModule, ...)
)
someone can i help me with this?
you must have to add these lines in your code:
First Step:
Add the new CallAdapter RxJavaCallAdapterFactory.create() when building a Retrofit instance.
public static final String BASE_URL = "http://google.com/";
public static Retrofit getClient(String baseUrl) {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Next step:
Update the APIService for example:-> savePost(String title, String body, String userId) method to become an Observable.
public interface APIService {
#GET
Call<ResponseBody> list(#Url String url);
//or
#POST("/posts")
#FormUrlEncoded
Observable<Post> savePost(#Field("title") String title,
#Field("body") String body,
#Field("userId") long userId);
}
Final step:
When making the requests, our anonymous subscriber responds to the observable's stream which emits event.
public void sendPost(String title, String body) {
// RxJava
mAPIService.savePost(title, body, 1).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Post>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Post post) {
showResponse(post.toString());
}
});
}
this is way you build your dynamic urls: want to learn more details full description link: Sending Data With Retrofit 2 HTTP Client for Android
and See base URL for details of how the value will be resolved against a base URL to create the full endpoint URL.
if you are doing using kotlin: follow this link. dynamic urls at Runtime with Retrofit 2
I already tried change the baseUrl("http://192.168.192.168/") to baseUrl("http://")and change the url on retrofit call but my app crashs and return illegal URL error.
You can leave it as a baseUrl if you use #URL it will overwrite the one on yout Retrofit.Builder()
You can use #URL parameter to change the endpoint dynamically.
#GET
fun getUsers(#Url String url) : Observable<UserResponse>
In my code, I want to send post request with basic auth.
Here is my postman screenshot :
here is my apiInterface class
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
here is my apiclient
public class ApiClient {
public static final String BASE_URL = "http://192.**********";
private static Retrofit retrofit = null;
private static OkHttpClient sClient;
public static Retrofit getClient() {
if(sClient == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
sClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT))
.addInterceptor(interceptor)
.build();
}
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(sClient)
.build();
}
return retrofit;
}
}
My question is how can i send post request,using header :
Header Username : EBA Token :
34242353453456563DSFS
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/
make header like this way..
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (context == null) {
request = request
.newBuilder()
.build();
} else {
request = request
.newBuilder()
.addHeader("Authorization", "Bearer " + AppSetting.getStringSharedPref(context, Constants.USER_KEY_TOKEN, ""))
.build();
}
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
Use Header annotation
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Header("Authorization") token: String,#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
Simple-Retrofit-API-request-and-Data-Loading Here I just add the project where create the API call to access data from database using retrofit library; which is leading library to access data on network. And display the accessed data in the List format. Create the Simple Android Studio Project with Empty Activity. Create the Adapter and activity item to show normal lists in android app. Now Create the App class extending Application, as Application class is a singleton that you can access from any activity or anywhere else you have a Context object.
You can check the more details about Application class from https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class Why extend an Application class? https://developer.android.com/reference/android/app/Application.html
Add android:name=".YourApplication" i.e. class name extending the Application class in android. and class will be like public class YourApplication extends Application Init the Retrofit in Application class
//network code start
//init http logger
httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// init client client = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
Gson gson = new GsonBuilder().setLenient().create();
Retrofit mRetrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson)).build();
mWebservice = mRetrofit.create(Webservice.class);
While Constants.API_BASE_URL is base url Create the Webervice.class where you can call the API with parameters e.g. In case of GET Method:
#GET("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
In case of POST method:
#FormUrlEncoded
#Headers({"Content-Type: application/x-www-form-urlencoded"})
#POST("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
You can See the more in details About Retrofit on Official API declaration here: http://square.github.io/retrofit/
We can parse the values with POJO i.e. Setter and Getter, using the Parceble class. Since parsing key name should be equal to the value we are receiving from the JSON response. POJO class should be declared like public class ClientData implements Parcelable { then declare the keys in the class, key values means
public class ClientData implements Parcelable
{
public String client_id;
public String company_name;
public String address_line;
public String city;
public String pincode;
public String state;
public String country;
}
Now using Alt+Enter i.e. select the option Add Parceble Implementation and press enter. Then automatically parceble class will be added. Also you have to add Setter and Getter method in class using Alt + Insert. Note: Don’t add the Setter and Getter methods for CREATER: Creater<> method If you want to use different key that JSON response key, then you should use Serialization. When I was using same key then its is like public String client_id; But when I am using the Serialization, then I can use like #Serializattion(“client_id”) public String ClientID; Now last but not a list, We call the API using retrofit, and use the response to view the Item in list-
RetroFitApplication.getWebservice().updateChatStatus().enqueue(new Callback() {
#Override public void onResponse(Call call, Response response) {
Log.d("retrofilt success", "" + response.body());
if (response.body() != null) {
clientResponceData = response.body();
Gson gson = new Gson();
String body = gson.toJson(response.body());
Log.d("retrofilt success2", "clientData" + clientResponceData.getResponse());
if (clientResponceData.getResponse() != null) {
initRV();
}
} else {
// Empty Client List Toast.makeText(ClientList.this, "Empty List", Toast.LENGTH_SHORT).show();
}
}
#Override public void onFailure(Call call, Throwable t) {
Log.d("retrofilt error", "" + t);
Toast.makeText(ClientList.this, "No Internet Connection", Toast.LENGTH_SHORT).show();
}
});
By using the Construction in Adapter, we can use the values from the response. Guys I added this repository to get the Entire idea of calling the API and get the response from server using the Retrofit Library. I write this entire documents in details with simple word.
I have sent and retrieved a url String from a json response using Parcelable in a fragment like so
Received String value
String profile_url = student.getProfile();
I want to use this string value as the basUrl to make another request using Retrofit like so
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
But getting the following error
java.lang.RuntimeException: An error occured while executing doInBackground()
......
Caused by: java.lang.IllegalArgumentException: baseUrl must end in /:
How do i put the / at the end of the baseUrl?
Putting it directly like so
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url/)
.addConverterFactory(GsonConverterFactory.create())
.build();
does not work, Express expected.
Any help. Thanks
Debug Profile url profile_url=http://services.hanselandstudent.com/student.svc/1.1/162137/category/1120/json?banner=0&atid=468f8dc2-9a38-487e-92ae-a194e81809d9
Since you've a complete url to call, you will need the #Url annotation in Retrofit2.
Define your interface with something like that
public interface YourApiService {
#GET
Call<Profile> getProfile(#Url String url);
class Factory {
public static YourApiService createService() {
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl("http://www.what.com/")
.build();
return retrofit.create(YourApiService.class);
}
}
}
Then call it with
YourApiService.createService().getProfile(profile_url);
You must have "/" in the end of your string variable.
String profile_url = student.getProfile() + "/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
I'm trying to perform a login action using Retrofit 2.0 using Dagger 2
Here's how I set up Retrofit dependency
#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;
}
Here's the API interface.
interface LoginAPI {
#GET(relative_path)
Call<Boolean> logMe();
}
I have three different base urls users can log into. So I can't set a static url while setting up Retrofit dependency. I created a setUrl() and getUrl() methods on Application class. Upon user login, I set the url onto Application before invoking the API call.
I use lazy injection for retrofit like this
Lazy<Retrofit> retrofit
That way, Dagger injects dependency only when I can call
retrofit.get()
This part works well. I got the url set to retrofit dependency. However, the problem arises when the user types in a wrong base url (say, mywifi.domain.com), understands it's the wrong one and changes it(say to mydata.domain.com). Since Dagger already created the dependency for retrofit, it won't do again.
So I have to reopen the app and type in the correct url.
I read different posts for setting up dynamic urls on Retrofit using Dagger. Nothing really worked out well in my case. Do I miss anything?
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());
}
}
Or you can either replace your Retrofit instance (and possibly store the instance in a RetrofitHolder in which you can modify the instance itself, and provide the holder through Dagger)...
public class RetrofitHolder {
Retrofit retrofit;
//getter, setter
}
Or re-use your current Retrofit instance and hack the new URL in with reflection, because screw the rules. Retrofit has a baseUrl parameter which is private final, therefore you can access it only with reflection.
Field field = Retrofit.class.getDeclaredField("baseUrl");
field.setAccessible(true);
okhttp3.HttpUrl newHttpUrl = HttpUrl.parse(newUrl);
field.set(retrofit, newHttpUrl);
Retrofit2 library comes with a #Url annotation. You can override baseUrl like this:
API interface:
public interface UserService {
#GET
public Call<ResponseBody> profilePicture(#Url String url);
}
And call the API like this:
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");
For more details refer to this link: https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
This worked for me in 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)
}
}
Add the interceptor to OkHttpClient builder
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HostSelectionInterceptor())
.cache(null)
.build()
This might be late but Retrofit allows you to use dynamic URLs while making the network call itself using #Url annotation.
I am also using Dagger2 to inject the Retrofit instance in my repositories and this solution is working fine for me.
This will use the base url
provided by you while creating the instance of Retrofit.
#GET("/product/123")
fun fetchDataFromNetwork(): Call<Product>
This ignore the base url
and use the url you will be providing this call at run time.
#GET()
fun fetchDataFromNetwork(#Url url : String): Call<Product> //
Thanks to #EpicPandaForce for help. If someone is facing IllegalArgumentException, this is my working code.
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);
}
}
For latest Retrofit library, you can simply use singleton instance and change it with retrofitInstance.newBuilder().baseUrl(newUrl). No need to create another instance.
Dynamic url using Retrofit 2 and Dagger 2
You are able to instantiate new object using un-scoped provide method.
#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;
}
}
Now you can change base url via getting baseUrlHolder from the component
App.appComponent.getBaseUrlHolder().set("https://www.changed.com");
this.loginApi = App.appComponent.getLoginApi();
Please look into my workaround for Dagger dynamic URL.
Step1: Create an Interceptor
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;
}
}
Step 2:
add your newly created interceptor in your module
#Provides
#Singlton
DomainURLInterceptor getChangeURLInterceptor() {
return new DomainURLInterceptor();
}
step 3:
add interceptor into list of HttpClient interceptors
#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();
}
step 4:
#Provides
#Singlton
Retrofit provideRetrofit() {
return new Retrofit.Builder()
.baseUrl(ApiConfig.BASE_URL) // this is default URl,
.addConverterFactory(provideConverterFactory())
.client(provideHttpClient())
.build();
}
Note: if the user has to change the Base URL from settings, remember to validate the newly created URL with below method:
public final static boolean isValidUrl(CharSequence target) {
if (target == null) {
return false;
} else {
return Patterns.WEB_URL.matcher(target).matches();
}
}