Okhttp : Asynchronous request doesn't stop immediately after onResponse - java

First sorry for my English which is not my native language.
I use okhttp to do some simple asynchronous calls but my program doesn't stop immediately after the call of onResponse. It takes some seconds and then stops. I don't have this issue on Android 5 but on my desktop. I have the same issue with others URLs. Maybe there is something I did wrong. The request is performed in another thread. My network is under a proxy.
I use : okhttp-2.4.0 and okio-1.4.0 and java8.
Redirect me if this issue was already answered.
This my code : `
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Settings.setProxy();
Request request = new Request.Builder()
.url("http://openlibrary.org/search.json?q=la+famille")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
e.printStackTrace();
System.out.println("error");
}
#Override
public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
System.out.println("coucou");
}
});
}
`

As you find out from the issue when you do an async call an ExecutorService is created and the pool is keeping the VM alive.
To shutdown the pool just get the dispatcher and close it:
client.getDispatcher().getExecutorService().shutdown();

I don't know how but this line worked for me
client.connectionPool().evictAll();

Related

Sending data from android to esp8266

I have an arduino project, where i was able to set up an esp8266 as a webserver, and i am able to send data to it, eg. if i put "http://192.168.4.1/get?data=010" into the browser, it works perfectly.
I want to send data using an android app, which pretty much means using the above mentioned url, just with different values for "data".
I've tried using okhttp3, but it doesn't work.
Here is what I've tried:
public void sendMessage(View view) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://192.168.4.1/get?data=010")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
System.out.println(response.toString());
}
public void onFailure(Call call, IOException e) {
System.out.println("Failed");
}
});
}
This seems to work, with other apis, eg. if i put in https://reqres.in/api/users?page=2 as the url I get a response, but when i try to connect to the arduino, it doesn't do anything.
Adding android:usesClearTextTraffic="true" to the AndroidManifest.xml file solved the issue.
Thanks for the answer blackapps.

How to wait for the HTTP request to get completed in java

I am writing Java code where i am downloading the file from a server and i have to copy the file in my local system when the file download is complete.
I am using the below code:-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.readTimeout(600, TimeUnit.SECONDS).writeTimeout(600, TimeUnit.SECONDS)
.connectTimeout(600, TimeUnit.SECONDS).build();
Request downloadRequest = new Request.Builder().url(url + fileName).addHeader("cache-control", "no-cache")
.addHeader("Authorization", token).build();
try {
Response downloadResponse = client.newCall(downloadRequest).execute();
System.out.println(downloadResponse.message());
System.out.println("got response from blob " + downloadResponse.isSuccessful() + " " + fileName);
return downloadResponse;
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
But the request is made asynchronously and before the request is completed then response is returned which is incomplete. Can anyone please help me how can i make a request and wait till the response is completed.
Any help is highly appreciated!
Looks like you're returning the response object (not the response body content).
try something like:
return downloadedResponse.body().string()
My experience with HttpClient is such that the headers return first. The content doesn't necessarily come across the wire unless/until you consume it.
To make a synchronous GET request we need to build a Request object based on a URL and make a Call. After its execution we get back an instance of Response:
#Test
public void whenGetRequest_thenCorrect() throws IOException {
Request request = new Request.Builder()
.url(BASE_URL + "/date")
.build();
Call call = client.newCall(request);
Response response = call.execute();
assertThat(response.code(), equalTo(200));
}
You are already using synchronous method calling.
client.newCall(downloadRequest).execute();
This is a synchronous way of requesting URL. If you want to do the aysynchronous call you need to use enqueue method of Call class.
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
// ...
}
public void onFailure(Call call, IOException e) {
fail();
}
});
I think problem is somewhere else. Kindly give more details why you are suspecting the current one as an asynchronous call so that we can do RCA.

How to run a method after sending a servlet response while also accepting further requests?

The scenario I am trying to complete is the following:
The client submits a HTTP POST request to the servlet.
The servlet sends a response to the client confirming the request has been received.
The servlet then sends an email notification to the system administrator.
So far I am able to complete the following steps in the order described above, however I run into an issue at the end. If the client makes another HTTP request to the same or another servlet while the email notification method EmailUtility.sendNotificationEmail() is still running, the servlet will not run any further code until this email method is completed (I am using javax.mail to send emails if that matters).
I have tried to use AsyncContext to get around this (which I may be using incorrectly), but unfortunately the issue still remains.
How can I make the EmailUtility.sendNotificationEmail() run in a different thread/asynchronously so that the servlets do not have to wait for this method to complete?
This is my code so far:
//Step 1: Client submits POST request to servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
//Step 2: Servlet sends response to client.
response.getWriter().write("Your request has been received");
response.getOutputStream().flush();
response.getOutputStream().close();
//Step 3: Servlet send email notification.
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
acontext.complete();
}
});
}
Try something simple, like a thread:
new Thread(new Runnable()
{
#Override
public void run()
{
EmailUtility.sendNotificationEmail();
}
}, "Send E-mail").start();
So I resolved the issue by using ExecutorService as follows:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
}
});
executorService.shutdown();

How to get html source to String using OkHttp - Android

I've been searching the simplest way to get Html code to String for some time now. I just need to fetch it so i can move forward with my project.
I tried:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
String html= null;
try {
html = run("http://google.com");
} catch (IOException e) {
e.printStackTrace();
}
text.setText(html);
}
}
I got Error android.os.NetworkOnMainThreadException.
I just started developing in Android studio and I'm not an expert in Java either. I would like if someone would explain what i need to do, with examples preferably. thank you in advance
As #CommonsWare and #christian have already said, you need to make network operations in the background and for this aim Okhttp has a special method enqueue(). This will create a background thread for you and simplify your work.
In your case, change the lines inside run() method to these:
String run(String url) throws IOException {
String result = "";
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// failure case
}
#Override
public void onResponse(Call call, Response response) throws IOException {
// success case
result = response.body().string();
}
});
}
You need to make network operations in background thread otherwise, you will get the exceptions. Android make it mandatory because network call takes a bit time and the UI-Thread will freeze.
Please refer https://github.com/square/okhttp/wiki/Recipes#asynchronous-get
and https://stackoverflow.com/a/6343299/1947419

How to avoid waiting on main thread when adding headers to requests using retrofit?

I use this to config my retrofit:
RestAdapter restAdapter = new RestAdapter.Builder()
//add headers to requests
.setRequestInterceptor(getAuthenticatedRequestInterceptor())
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(getGson()))
.build();
and The getAuthenticatedRequestInterceptor() method adds headers to request:
public AccountRequestInterceptor getAuthenticatedRequestInterceptor() {
AccountRequestInterceptor interceptor = new AccountRequestInterceptor();
Map<String, String> headers = new HashMap<>();
String accessToken = null;
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
}
getToken() method is:
private synchronized string getToken() throws InterruptedException {
if (!isRefreshing()) {
//This is very important to call notify() on the same object that we call wait();
final TokenProvider myInstance = this;
setRefreshing(true);
MyApplication.getRestClient().getAccountService().getRefreshedToken(mLoginData.getRefreshToken())
.subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onError(Throwable e) {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onNext(LoginResponse loginResponse) {
synchronized (myInstance) {
mLoginData = loginResponse;
mAccountProvider.saveLoginData(loginResponse);
myInstance.notifyAll();
}
}
});
}
this.wait();
return mLoginData.getToken();
}
The TokenProvider.getInstance(mContext).getToken() has a wait() on main thread to get the response from an async method and i know that is a bad thing to do but i need this here to wait for the response to take the token from it and then return the token.how can i do this in a separate thread to avoid waiting on the main thread?
Note:
1 - that this is called before any request with retrofit.
2 - I read this and i know i can refresh token after a fail request, but for business reasons i want to avoid having an invalid token.
3 - I call MyApplication.getRestClient().getAccountService().login(loginRequest,callback...‌​) in my Activity and before adding token everything happened in background thread. so I want to use my token and do not block the main thread.
UPDATE: I added the following Interceptor to my new OkHttp:
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request newRequest;
try {
Log.d("addHeader", "Before");
String token = TokenProvider.getInstance(mContext).getToken();
if (token != null) {
newRequest = request.newBuilder()
.addHeader("Bearer", token)
.build();
} else {
// I want to cancel the request or raise an exception to catch it in onError method
// of retrofit callback.
}
} catch (InterruptedException e) {
Log.d("addHeader", "Error");
e.printStackTrace();
return chain.proceed(request);
}
Log.d("addHeader", "after");
return chain.proceed(newRequest);
}
}
Now how can i cancel the request or raise an exception to catch it in onError method of retrofit callback, if token is null?
It's a little bit strange issue but let me try to help you. :)
As you know you can refresh token after a failed request with retrofit using response interceptor.
Let's try to use interceptor before request.
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Here where we'll try to refresh token.
// with an retrofit call
// After we succeed we'll proceed our request
Response response = chain.proceed(request);
return response;
}
}
And when you're creating your api create a new HttpClient:
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new RequestTokenInterceptor());
And add your http client to your adapter like below:
.setClient(new OkClient(client))
If this works, before every request you'll try to refresh token first and then will proceed your api request. So in ui there'll be no difference with your normal api calls.
Edit:
I'm editing my answer too. If you want to return an error in else case if token null, in else case you can create your custom response:
private Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
}
or simply you can return a null response. if you build your custom response and set your code not to 200 such as 401 or 400+ you'll receive that response in Retrofit's callbacks failure method. Than you can do what ever you want.
If you return null you'll get a RuntimeException i think and still you can catch response in your callback's failure method.
After you create your own response in else you can create your custom callback and catch your null response and transform your custom error how ever you want like below:
public abstract class DefaultRequestCallback<T> implements Callback<T> {
public abstract void failure(YourCustomException ex);
public abstract void success(T responseBean);
#Override
public void success(T baseResponseBean, Response response) {
if (response == null) {
// Here we catch null response and transform it to our custom Exception
failure(new YourCustomException());
}
} else {
success(baseResponseBean);
}
}
#Override
public void failure(RetrofitError error) {
// Here's your failure method.
// Also you can transform default retrofit errors to your customerrors
YourCustomException ex = new YourCustomException();
failure(ex);
}
}
This can help you i think.
Edit 2:
You can build a new Response like below. There's a builder pattern in Retrofit's Response class. You can check it from there.
Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();
You could make all long actions in AsyncTask doInBackground method, while in onPre- and onPostExecute you could show/hide some progress bars when user is waiting
Ok, I think if you are calling your getAuthenticatedRequestInterceptor() on the main thread and which in turns call getInstance(),in which i feel you would be creating an object of Type TokenProvider hence when you create this object in the main thread your object.wait() runs on main thread hence to run this on a background thread probably modify your getAuthenticatedRequestInterceptor() method to execute the following lines in a new thread.
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
but this will have problems for notifying your RestAdapter as the main thread will proceed executing, hence i would suggest
you call getAuthenticatedRequestInterceptor() method first in a new thread and then notify your main thread to build your RestAdapter.This will free your main thread but with the strategy you are employing you will have to wait until you receive the token to make any calls.

Categories

Resources