On some APIs on the app I'm working on, the user location is sent as one of the parameters, so their locations can be monitored via web dashboard, like this:
#FormUrlEncoded
#POST("v1/visit")
Call<StartVisitResponse> startVisit(
#Header("Authorization") String token,
#Header("latitude") String latiude,
#Header("longitude") String longitude,
#Field("outlet_id") int outlet_id,
#Field("date") String String visit_date
);
And this is an activity that illustrates the main problem:
class StartVisitActivity implements AppCompactActivity {
ActivityStartVisitBinding uiBinding;
InstantLocate theInstantLocate; // from https://github.com/mukul56/InstantLocate
#Override
protected void onCreate(Bundle savedInstanceState) {
uiBinding.btnGetLocation( v -> {
initLocation();
});
uiBinding.btnStartVisit( v -> {
ApiClient.getClient().create(ApiInterface.class).startVisit(
Helper.getAuthToken(),
""+theInstantLocate.getlatitude(),
""+theInstantLocate.getlongitude(),
outletId,
date).enqueue(new Callback<StartVisitResponse>() {
#Override
public void onResponse(Call<StartVisitResponse> call, Response<StartVisitResponse> response) {
if (response.isSuccessful()) {
} else {
}
}
#Override
public void onFailure(Call<StartVisitResponse> call, Throwable t) {
}
});
})
}
private void initLocation(){
theInstantLocate = new InstantLocate(this);
theInstantLocate.instantLocation();
Toast.makeText(getApplicationContext, "Hi you are going to visit outlet ("+theInstantLocate.getlatitude()+","+theInstantLocate.getlongitude()+").", Toast.LENGTH_SHOW).show();
}
}
First press the "Get Location" button to get our current location, then the "Start Visit" button. While inspecting the web dashboard, we found some users visiting different outlets started at the same position (which actually didn't). Weird.
After about 15 minutes of changing FakeGPS location and calling initLocation() (without quitting the app) repeatedly, both locations are always match. The issue is on startVisit(). For example I changed locations a few time: Tokyo -> Bangkok -> Jakarta. When inspecting the startVisit log, my detection location was still in Tokyo. Seems like Retrofit cached my old position parameters. How to clear those, so the latest values are always used?
For more information, this is my ApiClient class:
public class ApiClient {
public static final String BASE_URL = ".........";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Interceptor httpClient = chain -> {
Request original = chain.request();
String method = original.method();
HttpUrl.Builder httpBuilder = original.url().newBuilder();
String token = Prefs.getString("TOKEN", null);
Request.Builder requestBuilder;
if (token != null){
requestBuilder = original.newBuilder()
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.method(method, original.body())
.header("Authorization", Helper.getToken())
.header("latitude", Helper.getLatitude())
.header("longitude", Helper.getLongitude());
}else {
requestBuilder = original.newBuilder()
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.method(method, original.body());
}
Request request = requestBuilder.url(httpBuilder.build()).build();
return chain.proceed(request);
};
OkHttpClient client = null;
client = new OkHttpClient.Builder().connectTimeout(100, TimeUnit.SECONDS)
.readTimeout(100, TimeUnit.SECONDS).addInterceptor(interceptor)
.addInterceptor(new ChuckerInterceptor(BaseApp.getAppContext()))
.addInterceptor(httpClient).build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Related
I am new to Android rest api, basically I was trying to retrieve some information from a demo website using rest api together with the retrofit. Somehow my api call always on failure, hope someone could help.
Api.class
String BASE_URL = "https://demo.openmrs.org/openmrs/ws/rest/v1/";
#GET("location?tag=Login%20Location")
Call<List<RetroLocation>> getLocation();
Location.class
#SerializedName("display")
#Expose
protected String display;
#Expose
private String name;
public RetroLocation() {}
public RetroLocation(String display) {
this.display = display;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Call the api from the main class
private void retriveLocation() {
//create object for the RetrofitInstance
RestApi api = RetrofitInstance.getRetrofitInstance().create(RestApi.class);
//making the call object using the api method created in the api class
Call<List<RetroLocation>> call = api.getLocation();
//making the call using enqueue(), it takes callback interface as an argument
call.enqueue(new Callback<List<RetroLocation>>() {
#Override
public void onResponse(Call<List<RetroLocation>> call, Response<List<RetroLocation>> response) {
progressDoalog.dismiss();
if (response.body() != null) {
//goes to my list
List<RetroLocation> locationList = response.body();
//creating an string array for the listview
String[] location = new String[locationList.size()];
//fill in the array with the response data from json
for (int i = 0; i < locationList.size(); i++) {
location[i] = locationList.get(i).getName();
}
//displaying the string array into the listView
ArrayAdapter adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.custom_row, R.id.location, location);
listviewLocation.setAdapter(adapter);
Log.d("result", "Respond");
//generateDataList(response.body());
}else
Log.d("result", "No response");
}
#Override
public void onFailure(Call<List<RetroLocation>> call, Throwable t) {
//if(progressDoalog != null && progressDoalog.isShowing())
//progressDoalog.dismiss();
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
Log.d("result", "onFailuer");
}
});
RestInstance.class
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(RestApi.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
if(retrofit!=null)
Log.d("result", "logged in");
return retrofit;
}
static OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", "Basic "+ getAuth())
.header("Accept", "application/json")
.method(original.method(),original.body())
.build();
return chain.proceed(request);
}
}).build();
Update: Here is the throwable error message
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:119)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:218)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:112)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
The output of base_url
<org.openmrs.module.webservices.rest.SimpleObject serialization="custom">
<unserializable-parents/>
<map>
<default>
<loadFactor>0.75</loadFactor>
<threshold>12</threshold>
</default>
<int>16</int>
<int>3</int>
<string>uuid</string>
<string>b1a8b05e-3542-4037-bbd3-998ee9c40574</string>
<string>display</string>
<string>Inpatient Ward</string>
<string>links</string>
<list>
<org.openmrs.module.webservices.rest.web.Hyperlink>
<rel>self</rel>
<uri>
http://demo.openmrs.org/openmrs/ws/rest/v1/location/b1a8b05e-3542-4037-bbd3-998ee9c40574
</uri>
</org.openmrs.module.webservices.rest.web.Hyperlink>
</list>
</map>
<linked-hash-map>
<default>
<accessOrder>false</accessOrder>
</default>
</linked-hash-map>
</org.openmrs.module.webservices.rest.SimpleObject>
<org.openmrs.module.webservices.rest.SimpleObject serialization="custom">
<unserializable-parents/>
<map>
<default>
<loadFactor>0.75</loadFactor>
<threshold>12</threshold>
</default>
<int>16</int>
<int>3</int>
<string>uuid</string>
<string>2131aff8-2e2a-480a-b7ab-4ac53250262b</string>
<string>display</string>
<string>Isolation Ward</string>
<string>links</string>
<list>
<org.openmrs.module.webservices.rest.web.Hyperlink>
<rel>self</rel>
<uri>
http://demo.openmrs.org/openmrs/ws/rest/v1/location/2131aff8-2e2a-480a-b7ab-4ac53250262b
</uri>
</org.openmrs.module.webservices.rest.web.Hyperlink>
</list>
</map>
<linked-hash-map>
<default>
<accessOrder>false</accessOrder>
</default>
</linked-hash-map>
</org.openmrs.module.webservices.rest.SimpleObject>
try this
BASE URL https://demo.openmrs.org/
Retrofit Interface
#GET("openmrs/ws/rest/v1/location")
Call<List<RetroLocation>> getLocation(#Query("tag") String tag);
Change like this may work.
String BASE_URL = "https://demo.openmrs.org/";
#GET("openmrs/ws/rest/v1/location?tag=Login%20Location")
Call<List<RetroLocation>> getLocation();
I am just trying to do post api call using Retrofit.The server is responding with correct data.I checked with Postman(Chrome). My code is as follows
public class MainActivity extends Activity implements retrofit2.Callback>{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(6, TimeUnit.MINUTES)
.readTimeout(6, TimeUnit.MINUTES)
.writeTimeout(6, TimeUnit.MINUTES)
.build();
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://kokanplaces.com/")
.addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient)
.build();
// prepare call in Retrofit 2.0
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<List<CityModel>> call = apiService.getCitiesList();;
//asynchronous call
call.enqueue(this);
}
#Override
public void onResponse(Call<List<CityModel>> call, Response<List<CityModel>> response) {
int code = response.code();
if (code == 200) {
for (CityModel cityModel : response.body()) {
System.out.println(
cityModel.getCityname() + " (" + cityModel.getCityId() + ")");
}
} else {
Toast.makeText(this, "Did not work: " + String.valueOf(code), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<List<CityModel>> call, Throwable t) {
Toast.makeText(this, "failure", Toast.LENGTH_LONG).show();
System.out.println(t.fillInStackTrace());
t.printStackTrace();
}
}
public interface ApiInterface {
#POST("wp-json/getCities")
Call<List<CityModel>> getCitiesList();
}
Every time it is throwing Socket timeout exception.
Any solution will be great help.
I met the problems like you before. I fixed by adding custom OkHttpClient:
Constants.TIMEOUT_CONNECTION = 60;
private OkHttpClient getOkHttpClient() {
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.NANOSECONDS)
.connectTimeout(Constants.TIMEOUT_CONNECTION, TimeUnit.SECONDS)
.writeTimeout(Constants.TIMEOUT_CONNECTION, TimeUnit.SECONDS)
// .sslSocketFactory(getSSLSocketFactory())
.build();
return okHttpClient;
}
and retrofitAdapter:
retrofitAdapter = new Retrofit.Builder()
.baseUrl(ConstantApi.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(getOkHttpClient())
.build();
Remember readTimeout is 0, I am using retrofit 2.1.0. Default timeout of retrofit is 10 seconds. I tried to set readTimeout is 60 seconds but no effect.
Topic tags: Socket closed, socket timeout
Explanation: Retrofit maintains connection which is locking socket.
More: https://github.com/square/okhttp/issues/3146
SOLUTION:
configure connectionPool like in below example:
private OkHttpClient getOkHttpClient() {
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(0,1,TimeUnit.NANOSECONDS))
.build();
return okHttpClient;
}
Please remember to mark answer as correct :)
I am really trying to get a hang of using Retrofit with RxJava / RxAndroid. I've done this using normal Retrofit2 Callback method in a previous app without the use of Reactive Programming and it worked fine. So, here is it. I need to Tail Recall a function meant to fetch all Local Government from the server. The API uses pagination (I have to construct the URL with ?page=1, perPage=2). I've to do this till I've the whole data. So, below is my Rx code
public static Observable<LgaListResponse> getPages(Context acontext) {
String token = PrefUtils.getToken(acontext);
BehaviorSubject<Integer> pageControl = BehaviorSubject.<Integer>create(1);
Observable<LgaListResponse> ret2 = pageControl.asObservable().concatMap(integer -> {
if (integer > 0) {
Log.e(TAG, "Integer: " + integer);
return ServiceGenerator.createService(ApiService.class, token)
.getLgas(String.valueOf(integer), String.valueOf(21))
.doOnNext(lgaListResponse -> {
if (lgaListResponse.getMeta().getPage() != lgaListResponse.getMeta().getPageCount()) {
pageControl.onNext(initialPage + 1);
} else {
pageControl.onNext(-1);
}
});
} else {
return Observable.<LgaListResponse>empty().doOnCompleted(pageControl::onCompleted);
}
});
return Observable.defer(() -> ret2);
}
And my ServiceGenerator Class
public class ServiceGenerator {
private static final String TAG = "ServiceGen";
private static OkHttpClient.Builder builder = new OkHttpClient.Builder();
private static Retrofit.Builder retrofitBuilder =
new Retrofit.Builder()
.baseUrl(BuildConfig.HOST)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create(CustomGsonParser.returnCustomParser()));
public static <S> S createService(Class<S> serviceClass, String token) {
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
/*builder.addNetworkInterceptor(new StethoInterceptor());*/
builder.connectTimeout(30000, TimeUnit.SECONDS);
builder.readTimeout(30000, TimeUnit.SECONDS);
if (token != null) {
Interceptor interceptor = chain -> {
Request newRequest = chain.request().newBuilder()
.addHeader("x-mobile", "true")
.addHeader("Authorization", "Bearer " + token).build();
return chain.proceed(newRequest);
};
builder.addInterceptor(interceptor);
}
OkHttpClient client = builder.build();
Retrofit retrofit = retrofitBuilder.client(client).build();
Log.e(TAG, retrofit.baseUrl().toString());
return retrofit.create(serviceClass);
}
public static Retrofit retrofit() {
OkHttpClient client = builder.build();
return retrofitBuilder.client(client).build();
}
public static class CustomGsonParser {
public static Gson returnCustomParser(){
return new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();
}
}
}
So, I noticed on the first call, I get a response, but on the second one, the 440Error is thrown. The URL is formed, but the request throws a 400Error. I don't know why it's throwing a 400 everything is working fine if I use POSTMAN to test. And, I tested with my old code too. The Log is too long, so I put it in pastebin LOGS any help thanks. I've written most of this app with RxAndroid / RxJava. Thanks
I suggest you simplify things (and remove recursion). First build up your pages using something like
public static Observable<LgaListResponse> getPages(Context acontext, int initialPage, int perPage) {
String token = PrefUtils.getToken(acontext);
BehaviorSubject<Integer> pagecontrol = BehaviorSubject.<Integer>create(initialPage);
Observable<LgaListResponse> ret2 = pagecontrol.asObservable().concatMap(
new Func1<Integer,Observable<LgaListResponse>>() {
Observable<LgaListResponse> call(Integer pageNumber) {
if (pageNumber > 0) {
return ServiceGenerator.createService(ApiService.class, token)
.getLgas(String.valueOf(aKey), String.valueOf(perPage))
.doOnNext(
new Action1<LgaListResponse>() {
void call(LgaListResponse page) {
if (page.getMeta().getPage() != page.getMeta().getPageCount()) {
pagecontrol.onNext(page.getMeta().getNextPage());
} else {
pagecontrol.onNext(-1);
}
}
}
);
}
else {
return Observable.<LgaListResponse>empty().doOnCompleted(()->pagecontrol.onCompleted());
}
}
}
);
return Observable.defer(
new Func0<Observable<LgaListResponse>() {
Observable<LgaListResponse> call() {
return ret2;
}
}
);
}
then subscribe to the resulting observable. It looks horrible because I've avoided using lambdas but it should work.
I want to fetch json files for different users according to their User_ID, but currently this code is showing empty recyclerview
Retrofit File
public void loadJSONClaim() {
Retrofit retrofitclaim = new Retrofit.Builder()
.baseUrl("http://ec2-54-191-118-200.us-west-2.compute.amazonaws.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterfaceClaim requestclaim = retrofitclaim.create(RequestInterfaceClaim.class);
String User_ID = 5;
Call<JSONResponseClaim> call = requestclaim.getJSONClaim(User_ID);
call.enqueue(new Callback<JSONResponseClaim>() {
#Override
public void onResponse(Call<JSONResponseClaim> call, Response<JSONResponseClaim> responseclaim) {
JSONResponseClaim jsonResponseClaim = responseclaim.body();
dataclaim = new ArrayList<>(Arrays.asList(jsonResponseClaim.getAndroidClaim()));
adapterclaim = new DataAdapterClaim(dataclaim);
}
RequestInterFaceClaim
public interface RequestInterfaceClaim {
#GET("claim.php") // we want to access claim.php?User_ID=5
Call<JSONResponseClaim> getJSONClaim(#Query("User_ID") String User_ID);
}
I am using retrofit in my application like this
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new YourInterceptor());
final OkClient okClient = new OkClient(okHttpClient);
Builder restAdapterBuilder = new RestAdapter.Builder();
restAdapterBuilder.setClient(okClient).setLogLevel(LogLevel.FULL)
.setEndpoint("some url");
final RestAdapter restAdapter = restAdapterBuilder.build();
public class YourInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
// TODO Auto-generated method stub
Request request = chain.request();
if (request != null) {
Request.Builder signedRequestBuilder = request.newBuilder();
signedRequestBuilder.tag("taggiventorequest");
request = signedRequestBuilder.build();
request.tag();
}
return chain.proceed(request);
}
}
after sending request i am calling
okHttpClient.cancel("taggiventorequest");
but request is not cancelling i am getting the response from retrofit
dont know why it is not cancelling my request
I need volley like cancelation retrofit
As Retrofit API Spec, Canceling request will be included in version 2.0.
cancel() is a no-op after the response has been received. In all other
cases the method will set any callbacks to null (thus freeing strong
references to the enclosing class if declared anonymously) and render
the request object dead. All future interactions with the request
object will throw an exception. If the request is waiting in the
executor its Future will be cancelled so that it is never invoked.
For now, you can do it by creating custom callback class which implements on Callback from retrofit.
public abstract class CancelableCallback<T> implements Callback<T> {
private boolean canceled;
private T pendingT;
private Response pendingResponse;
private RetrofitError pendingError;
public CancelableCallback() {
this.canceled = false;
}
public void cancel(boolean remove) {
canceled = true;
}
#Override
public void success(T t, Response response) {
if (canceled) {
return;
}
onSuccess(t, response);
}
#Override
public void failure(RetrofitError error) {
if (canceled) {
return;
}
onFailure(error);
}
protected abstract void onSuccess(T t, Response response);
protected abstract void onFailure(RetrofitError error);
}
MyApi.java,
private interface MyApi {
#GET("/")
void getStringList(Callback<List<String>> callback);
}
In Activity or Fragment,
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.URL)
.build();
MyApi service = restAdapter.create(MyApi.class);
CancelableCallback callback = new CancelableCallback<List<String>>() {
#Override
protected void onSuccess(List<String> stringList, Response response) {
for (String str : stringList) {
Log.i("Result : ", str);
}
}
#Override
protected void onFailure(RetrofitError error) {
Log.e("Error : ", error.getMessage() + "");
}
};
service.getStringList(callback);
To cancel your request, simple call
callback.cancel();
This is an simple example to cancel each request. You can handle (cancel, pause, resume) two or more request at the same time by creating callback manager class. Please take a look that comment for reference.
Hope it will be useful for you.
The problem is that the request is completed, from the docs:
http://square.github.io/okhttp/javadoc/com/squareup/okhttp/OkHttpClient.html#cancel-java.lang.Object-
cancel
public OkHttpClient cancel(Object tag)
Cancels all scheduled or in-flight calls tagged with tag. Requests that are already complete cannot be canceled.