Does onFailure automatically close the WebSocket connection in OkHttp? - android

I'm using the OkHttp library for WebSocket connections in Java, and I'm wondering if the onFailure method included in the WebSocketListener class will close the WebSocket connection by default or if I need to manually close the connection myself.
Here's my implementation of the onFailure method:
public void onFailure(#NonNull WebSocket webSocket, #NonNull Throwable t, #Nullable Response response) {
super.onFailure(webSocket, t, response);
if (!webSocket.close(1000, "Connection closed")) {
webSocket.cancel();
close();
}
}
As you can see, I'm manually calling the webSocket.close() method to close the WebSocket connection with a specified status code and reason. I've also included an if statement to check if the WebSocket connection is still open before closing it, and I'm calling the cancel() method to free up any resources held by the WebSocket connection if the close method returns false.
So, my question is: does the onFailure method automatically close the WebSocket connection in OkHttp, or do I need to manually close the connection myself as I've done in my implementation?

Related

Retrofit cancel() & check if request was delivered to server

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.

Android - OkHTTP requests

I am using HttpUrlConnection for doing requests to my mysql db using webservices. With HttpUrlConnection I can execute all my requests in background so the main thread don't get overloaded and start skipping frames.
With okHttp how does this is achieved? How do I make a request with it and print a response using JSON? Is it better than httpUrlConnection?
P.S I do not know anything about okHttp, I will be grateful if you are explicit with your examples.
With okHttp how does this is achieved?
Typically, you let it handle the background thread for you, using enqueue() for asynchronous operation:
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
#Override public void onFailure(Call call, IOException e) {
// handle the error
}
#Override public void onResponse(Call call, Response response) throws IOException {
// use the result
}
});
}
(sightly simplified from the OkHttp docs)
Or, if you already have a background thread, you can use execute() instead of enqueue() for synchronous operation.
You might wish to review the other examples on the OkHttp recipes page, plus the OkHttp Web page, plus the OkHttp wiki, to get a better sense of how it compares with what you are used to.

Android GCM Sending token to server

The GCM Sample Project gives a stubbed out example of sending a GCM token to your server:
public class RegistrationIntentService extends IntentService {
...
#Override
protected void onHandleIntent(Intent intent) {
try {
...
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(token);
...
} catch (Exception e) {
...
}
}
/**
* Persist registration to third-party servers.
*
* Modify this method to associate the user's GCM registration token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
}
}
but this is done in an IntentService which finishes as soon as onHandleIntent returns right? So if starting an http call to send the token with the popular android-async-http library, I'm not even seeing my onStart hit:
private void sendRegistrationToServer(String token) {
post("/devices", params, new AsyncHttpResponseHandler() {
// TODO: MAKE SURE ONSTART ACTUALLY CALLED TO MAKE SURE REQUEST AT LEAST GOES UPSTREAM EVEN IF I DON'T RECEIVE A CALLBACK SINCE IN INTENTSERVICE
// IF NOT THEN MIGHT HAVE TO CHANGE THE INTENTSERVICE TO A SERVICE FOR DEVICE REGISTRATION
#Override
public void onStart() {
// not actually using callback because request sent from intentservice
Log.d("tagzzz", "sending upstream");
}
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
// not actually using callback because request sent from intentservice
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
// not actually using callback because request sent from intentservice
}
});
}
Will my http request even be sent upstream before onHandleIntent returns and finishes the IntentService? If not, why does Google give this as their example for sending your token to your server?
Will my http request even be sent upstream before onHandleIntent returns and finishes the IntentService?
Given that you are using a library named "android-async-http", I would assume that the default behavior is for it to execute the HTTP asynchronously. It is indeterminate whether or not the post() call will complete its work before onHandleIntent() returns, but it seems unlikely.
why does Google give this as their example for sending your token to your server?
Google doesn't. Google has a stub sendRegistrationToServer(), as you can see in your first code listing. I am not aware of any Google examples that use the "android-async-http" library.
You decided to use an asynchronous mechanism for sending that HTTP request. That is an inappropriate choice for inside an IntentService. Now, perhaps that library has a synchronous option, in which case you could switch to that. Otherwise, use something else synchronous for the HTTP request (HttpURLConnection, OkHttp3, etc.) from the IntentService.
Note that Volley is not a great choice here, insofar as Volley is also strongly tilted towards asynchronous work.

How to get the HTTP error code from a failed request in Retrofit 2.0.0?

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.

Handling specific errors by sending another request transparently in retrofit

Here's the case I'm trying to handle,
If a request is executed, and the response indicates the auth token is expired,
send a refresh token request
if the refresh token request succeeds, retry the original request
This should be transparent to the calling Activity, Fragment... etc. From the caller's point of view, it's one request, and one response.
I've achieved this flow before when using OkHttpClient directly, but I don't know how to achieve this in Retrofit.
Maybe something related to this open issue about a ResponseInterceptor?
If there's no straight-forward way to achieve this in retrofit, what would be the best way to implement it? A base listener class?
I'm using RoboSpice with Retrofit as well, if it can be helpful in such case.
Since I'm using RoboSpice, I ended up doing this by creating an abstract BaseRequestListener.
public abstract class BaseRequestListener<T> implements RequestListener<T> {
#Override
public void onRequestFailure(SpiceException spiceException) {
if (spiceException.getCause() instanceof RetrofitError) {
RetrofitError error = (RetrofitError) spiceException.getCause();
if (!error.isNetworkError()
&& (error.getResponse().getStatus() == INVALID_ACCESS_TOKEN_STATUS_CODE)) {
//I'm using EventBus to broadcast this event,
//this eliminates need for a Context
EventBus.getDefault().post(new Events.OnTokenExpiredEvent());
//You may wish to forward this error to your listeners as well,
//but I don't need that, so I'm returning here.
return;
}
}
onRequestError(spiceException);
}
public abstract void onRequestError(SpiceException spiceException);
}

Categories

Resources