How can I be sure that calling cancel() on the Retrofit cause request is not delivered to the server?
Currently I know, that calling cancel on the Retrofit will invoke onFailure branch of the callback. And then I can check if the cause of the failure is because call was cancelled.
https://futurestud.io/tutorials/retrofit-2-cancel-requests
But can I be somehow sure, that request didn't arrive to the server?
Code snippet from here
new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "request success");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (call.isCanceled()) {
Log.e(TAG, "request was cancelled");
}
else {
Log.e(TAG, "other larger issue, i.e. no network connection?");
}
}
};
According to that this will raise in case call was canceled, as it say
// something happened, for example: user clicked cancel button
call.cancel();
If you call call.cancel() let's say later it means you already got a response from the server, therefore you won't get to onFailure point where call.isCanceled() is being checked :)
If you cancel the request, Retrofit will classify this as a failed request and thus call the onFailure() callback in your request declaration. This callback is also used when there is no Internet connection or something went wrong on the network level. From the app perspective, these errors are quite a bit different.
Related
Imagine this scenario:
I start a requestA using the Call.enqueue() method, then, before requestA be finished, I start requestB at the same endpoint of requestA. While I'm using Call.enqueue() method, requestB will be executed after requestA? Or enqueue() method is just used to do requests asynchronously?
I search that information at docs and here on StackOverflow but all the information is superficial about this specific method.
Here is my code - this same code is used for both requests:
foolRequest.enqueue(new Callback<Response>() {
#Override
public void onResponse(#NonNull Call<Response> call,
#NonNull retrofit2.Response<Response> response) {
//do something
}
#Override
public void onFailure(#NonNull Call<Response> call,
#NonNull Throwable t) {
//do something
}
});
I think so,
Otherwise, if you implement your own connection client.
By the source code from OkHttpClient, there is a dispatcher class, save all the enqueue API, and it uses queue to save the relative task
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
Suppose we have a retrofit Callback as follows:
Callback<T> callback = new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
mProgressBar.dismiss();
if (response.code() == 401) {
Toast.makeText(getApplicationContext(),
"Invalid session, logging out",
Toast.LENGTH_LONG).show();
Session.getInstance(getApplicationContext()).clear();
this.navigateToLoginActivity();
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
}
}
Suppose the retrofit request with this callback runs for a long time and if the activity starting the request has already finished by the time onResponse is invoked, then it will cause memory leak we are referring to the activity via
this.navigateToLoginActivity()
So the question is how to get the context in onResponse? WeakReference? But this is such a common use case, i.e. to logout user, in case, the authentication fails. I want to know the best practice in this scenario for a general AsyncTask case and retrofit case as well.
I use the com.squareup.retrofit:retrofit:2.0.0-beta2 and encounter some problem.
I want to add the possibility to close some of the downloading by button press. So I found the cancel(Object tag) method in OkHTTPClient.
I tried to find the place where I can put this tag value but didn't found anything. Also passing null as a parameter not work at all.
Could someone help to tell me where I can put tag or suggest another approach?
Retrofit2 has a cancel() method as well. You can use that. Here is an example:
Call<ResponseBody> call =
downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "request success");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "request failed");
}
});
// something happened, for example: user clicked cancel button
call.cancel();
Be aware that if you cancel the request, Retrofit will classify it as a failure & call onFailure().
Further reading in case you're interested: https://futurestud.io/blog/retrofit-2-cancel-requests
when I user the OkHttp Library with a asynchronous way like this:
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
}
});
In the onFailure method, how I get the response status code to distinguish different errors. For example, Network error or Server error ?
As far as I remember, onFailure gets triggered when you get no response. So, if your receive an error, onResponse will be called. You can do something like this in onResponse:
#Override
public void onResponse(Call call, Response response) throws IOException {
switch(response.code()){
//your desired catched codes here.
}
}
And official doc for onResponse method:
Note that transport-layer success (receiving a HTTP response code, headers and body) does not necessarily indicate application-layer success: response may still indicate an unhappy HTTP response code like 404 or 500.
https://github.com/square/okhttp/issues/1769
According to the link, above, onFailure() is called if and only if there were problems with the client.
If the request was successfully delivered but there was a server problem you can check response.isSuccessful(). If it returns false, check response.code() and handle the error.
You check that using response.code()
You can also make use of response.message() to get more info.
I am making call using the following callback method:
Callback<PeopleList> callback = new Callback<PeopleList>() {
#Override
public void onResponse(Response<PeopleList> response) {
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Throwable t) {
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
}
};
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
retrofit.create(MyService.class).getPeopleData().enqueue(callback);
To the following interface:
public interface MyService {
#Headers("Accept: application/json")
#GET("/data/people/")
Call<PeopleList> getPeopleData();
}
This callback works just fine on successful requests. On unsuccessful ones however it does not give me the opportunity to investigate further as the onFailure method does not allow me to retrieve the http error code that came with the response.
On investigating further I found that according to several stackoverflow threads, the onResponse method should be called even on unsuccessful requests. This however seems to be at odds not only with my personal experience but also with the documentation of the Callback interface, which states that:
Communicates responses from a server or offline requests. One and only one method will be
invoked in response to a given request.
So the question is, how do I get the HTTP error code from a failed response if the onResponse method isn't called?
I think that the onResponse method gets called even if there is a response with an Error so something like this might work(sorry if I did something wrong first attempt to answer anybody :)
#Override
public void onResponse(Response<PeopleList> response) {
if(response.isSuccess()){ //good http request, do something with response.body()....
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
} else { //bad http response do something with error message
try {
Toast.makeText(LoginActivity.this,response.errorBody().string().toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e){
//IOException caught from response.errorBody().string() method
}
}
}
onResponse() will be always called, for failed requests .body() is null. response.isSuccess() is used to quickly distinguish requests with http codes between 200 and 300.
If you want to access http codes you can do the following:
int htppResultCode = response.raw().code();
It accesses the raw Response object which holds information about general outcome of the request.