Android NetworkOnMainThreadException seems unsolvable - java

Working on this app, which communicated with a webserver.
I'am using okHttp for sending http requests and getting responses.
And for some reason I'll get NetworkOnMainThreadException when the request is taking to long.
All the solutions I found just wont work out.
Here is the code which works until the receiving data is taking to long.
HttpGetRunnable.java
public class HttpGetRunnable implements Runnable {
private Request request;
private Response response;
public HttpGetRunnable(String route) {
String url = "http://10.0.2.2:8080" + route;
request = new Request.Builder()
.url(url)
.build();
}
#Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
public Response getResponse() {
return response;
}
}
Usage
try {
HttpGetRunnable httpGet = new HttpGetRunnable("/timesheet/" + user.getId());
Thread thread = new Thread(httpGet);
thread.start();
thread.join();
Response response = httpGet.getResponse();
String jsonString = response.body().string();
// ^ throw exception on this line when taking to long
} catch (Exception e) {
e.printStackTrace(System.out);
}
Error
04-16 23:06:53.042 31145-31145/com.example.jim.app I/System.out: android.os.NetworkOnMainThreadException
.. (no caused at)
What I tried so far:
using Callable (same way as Runnable, returning Response)
using Callable returning response body String
Executing runnable in ExecutorService single pool
Submitting callable in ExecutorService single pool then future.get
Using okHttp deferred #Override methods onFailed onResponse
I just can't figure it out..
What am I doing wrong here?
Thanks
P.S
using this version com.squareup.okhttp3:okhttp:3.2.0

What am I doing wrong here?
You are blocking the main application thread, via join(). Get rid of that.
For example, here is a sample app that uses OkHttp3 to request the latest Stack Overflow android questions. I have a dedicated LoadThread that handles the HTTP I/O:
/***
Copyright (c) 2013-2016 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
From _The Busy Coder's Guide to Android Development_
https://commonsware.com/Android
*/
package com.commonsware.android.okhttp;
import android.util.Log;
import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.Reader;
import de.greenrobot.event.EventBus;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
class LoadThread extends Thread {
static final String SO_URL=
"https://api.stackexchange.com/2.1/questions?"
+ "order=desc&sort=creation&site=stackoverflow&tagged=android";
#Override
public void run() {
try {
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder().url(SO_URL).build();
Response response=client.newCall(request).execute();
if (response.isSuccessful()) {
Reader in=response.body().charStream();
BufferedReader reader=new BufferedReader(in);
SOQuestions questions=
new Gson().fromJson(reader, SOQuestions.class);
reader.close();
EventBus.getDefault().post(new QuestionsLoadedEvent(questions));
}
else {
Log.e(getClass().getSimpleName(), response.toString());
}
}
catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception parsing JSON", e);
}
}
}
But I just start that thread in onCreate() of a fragment. I do not then try to block the main application thread via join(). Instead, I use greenrobot's EventBus to find out when the data is loaded, and then use it.

Related

AWS API Gateway WebSocket Timeout?

How do I keep an API Gateway Websocket API with a Lambda Java back end from timing out after 30 seconds?
The AWS API Gateway and re:invent videos mention using pings or heartbeats to keep the Websocket connection alive but I haven't found a straight-forward working example (in Java). The front end is using HTML 5 Websockets (vanilla javascript).
I'm including my Lambda test driver in Java. It causes the API Gateway WS API to timeout after 30 seconds as documented. The driver will return successfully if you remove the delay. Any help is appreciated...
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestWSClient implements RequestStreamHandler {
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
try {
//
// How do we keep API Gateway from timing out after 30 seconds?
// Is something like this even needed in the lambda?
//
new Thread(() -> {
while(true) {
try {
// Ping client every 15 seconds
Thread.sleep(15000);
//outputStream.write(); // What to write -- 0x89 0x00?
outputStream.flush();
} catch(Exception e) { e.printStackTrace(); }
}
}).start();
//
// Simulate long processing time or streaming
//
// NOTE: commenting sleep enables service to return w/o a timeout
// connection from API Gateway
//
try { Thread.sleep(60000); } catch(Exception e) {}
var response = Map.of(
"statusCode", 200,
"headers", Map.of("Content-Type", "text/csv"),
"body", "Hello,World"
);
ObjectMapper om = new ObjectMapper();
outputStream.write(om.writeValueAsBytes(response));
outputStream.flush();
} catch(Exception e) { e.printStackTrace(); }
finally { try { outputStream.close(); } catch(Exception e) {} }
}
}
I do not think I understand your problem correctly but here is how WebSocket API work in my experience.
client(s) <-(1)-> API Gateway <-(2)-> Lambda
1) is the web socket connection which stays open for a maximum of 2 hours, with idle timeout of 10 minutes as mentioned here. https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html
2 )communication is managed using #connection https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-how-to-call-websocket-api-connections.html
I believe you want to use #connection for talking to your API Gateway from lambda.
It looks like a NAT Gateway is required for a Lambda to call the postToConnection function provided by the AWS ApiGatewayManagementApi SDK.
Its a shame this requirement isn't specified anywhere regarding Websockets API or API Gateway.
Search for 'NAT' on this page, and read that sentence:

Apache HttpClient 4.x - Multi-threaded execution and resource leaks

Apache HttpClient 4.2.1
I have a Java process that downloads multiple files from one CouchDB A and uploads the same to another CouchDB B. The downloads and uploads are done using 40 worker threads using an ExecutorService.
When the file count is high (~25k) The process throws a SocketException of "too many open files" due to the Linux open file descriptor limit. I figured this might be due to a resource leak and proceeded to analyze the code which is as follows:
Initiate worker threads
private boolean copyDocumentsFromAtoB(Array ids) {
ExecutorService downloader = Executors.newFixedThreadPool(50);
ExecutorService uploader = Executors.newFixedThreadPool(50);
for (String id: ids) {
downloader.execute(new DownloaderThread(id, downloadedDocs));
}
for (JsonElement doc: downloadedDocs) {
uploader.execute(new UploaderThread(doc));
}
}
Runnable Downloader class (UploaderThread class has a similar implementation)
private static final class DocumentDownloader implements Runnable {
private final String documentId;
private final JsonArray downloadedDocs;
DocumentDownloader(String documentId, JsonArray downloadedDocs) {
this.documentId = documentId;
this.downloadedDocs = downloadedDocs;
}
#Override
public void run() {
InputStream docStream = null;
String url = buildUrl(documentId);
HttpGet doc = new HttpGet(url);
try {
//doc.setHeaders()
HttpClient httpClient = new DefaultHttpClient();
HttpResponse docResponse = httpClient.execute(doc);
docStream = docResponse.getEntity().getContent();
//document parsing and adding to downloadedDocs. Not important
} catch (Exception e) {
//handle exceptions
} finally {
if (docStream != null) {
try {
docStream.close();
} catch (IOException e) {
LOGGER.debug("Cannot close input stream", e);
}
}
}
}
}
Findings:
HttpClient is local to each thread. This is sub-optimal according to many resources and posts I found online.
If I did, httpClient.getConnectionManager().shutdown(); at the end of the Runnable class, the open file count decreased.
Using a global HttpClient (instantiated only once in the copyDocumentsFromAtoB method and passed down to the Runnable classes through the contructors) with a PoolingClientConnectionManager instance and a local context object, decreased the open file count further.
Questions:
Why did the original implementation have a increased number of open file descriptors during the copying process? How does multiple HttpClient instances contribute towards this?
Doesn't the docStream.close(); in the finally block close whatever Http connection that is created between the process and the database?
Does the HttpClient instance get destroyed (releasing any open resources in the process) when the thread that it belongs to terminates? (This would explain why the open file count decreases after the copying process terminates prematurely)
Are there any other optimizations I can do (in terms of resource leaks) other than using a single global HttpClient?
Are there tools that I could use to obtain quantifiable statistics of this scenario for different implementations?
What steps can I follow to find the optimum number of worker
threads, setMaxTotal and setDefaultMaxPerRoute (for the
PoolingClientConnectionManager)?

Fire and forget for HTTP in Java

We're implementing our own analytics for that we've exposed a web service which needs to be invoked which will capture the data in our DB.
The problem is that as this is analytics we would be making lot of calls (like for every page load, call after each js, CSS loads etc...) so there'll be many many such calls. So I don want the server to be loaded with lots of requests to be more precise pending for response. Because the response we get back will hardly be of any use to us.
So is there any way to just fire the web service request and forget that I've fired it?
I understand that every HTTP request will have as response as well.
So one thing that ticked my mind was what if we make the request timeout to zero second? But I'm pretty not sure if this is the right way of doing this.
Please provide me with more suggestions
You might find following AsyncRequestDemo.java useful:
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.http.client.fluent.Async;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback;
/**
* Following libraries have been used:
*
* 1) httpcore-4.4.5.jar
* 2) httpclient-4.5.2.jar
* 3) commons-logging-1.2.jar
* 4) fluent-hc-4.5.2.jar *
*
*/
public class AsyncRequestDemo {
public static void main(String[] args) throws Exception {
URIBuilder urlBuilder = new URIBuilder()
.setScheme("http")
.setHost("stackoverflow.com")
.setPath("/questions/38277471/fire-and-forget-for-http-in-java");
final int nThreads = 3; // no. of threads in the pool
final int timeout = 0; // connection time out in milliseconds
URI uri = null;
try {
uri = urlBuilder.build();
} catch (URISyntaxException use) {
use.printStackTrace();
}
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
Async async = Async.newInstance().use(executorService);
final Request request = Request.Get(uri).connectTimeout(timeout);
Future<Content> future = async.execute(request, new FutureCallback<Content>() {
public void failed(final Exception e) {
System.out.println("Request failed: " + request);
System.exit(1);
}
public void completed(final Content content) {
System.out.println("Request completed: " + request);
System.out.println(content.asString());
System.exit(0);
}
public void cancelled() {
}
});
System.out.println("Request submitted");
}
}
I used this:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
URL url = new URL(YOUR_URL_PATH, "UTF-8"));
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<HttpResponse> response = executor.submit(new HttpRequest(url));
executor.shutdown();
for HttpRequest,HttpResponse
public class HttpRequest implements Callable<HttpResponse> {
private URL url;
public HttpRequest(URL url) {
this.url = url;
}
#Override
public HttpResponse call() throws Exception {
return new HttpResponse(url.openStream());
}
}
public class HttpResponse {
private InputStream body;
public HttpResponse(InputStream body) {
this.body = body;
}
public InputStream getBody() {
return body;
}
}
that is.
Yes, you could initiate the request and break the connection without waiting for a response... But you probably don't want to do that. The overhead of the server-side having to deal with ungracefully broken connections will far outweigh letting it proceed with returning a response.
A better approach to solving this kind of performance problem in a Java servlet would bet to shove all the data from the requests into a queue, respond immediately, and have one or more worker threads pick up items out of the queue for processing (such as writing it into a database).

Play 2.5: get response body in custom http action

I'm trying to create a custom http action (https://playframework.com/documentation/2.5.x/JavaActionsComposition) to log request and response bodies with Play 2.5.0 Java. This is what I've got so far:
public class Log extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Context ctx) {
CompletionStage<Result> response = delegate.call(ctx);
//request body is fine
System.out.println(ctx.request().body().asText())
//how to get response body string here while also not sabotaging http response flow of the framework?
//my guess is it should be somehow possible to access it below?
response.thenApply( r -> {
//???
return null;
});
return response;
}
}
Logging is often considered a cross-cutting feature. In such cases the preferred way to do this in Play is to use Filters:
The filter API is intended for cross cutting concerns that are applied indiscriminately to all routes. For example, here are some common use cases for filters:
Logging/metrics collection
GZIP encoding
Security headers
This works for me:
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import javax.inject.Inject;
import akka.stream.*;
import play.Logger;
import play.mvc.*;
public class LoggingFilter extends Filter {
Materializer mat;
#Inject
public LoggingFilter(Materializer mat) {
super(mat);
this.mat = mat;
}
#Override
public CompletionStage<Result> apply(
Function<Http.RequestHeader, CompletionStage<Result>> nextFilter,
Http.RequestHeader requestHeader) {
long startTime = System.currentTimeMillis();
return nextFilter.apply(requestHeader).thenApply(result -> {
long endTime = System.currentTimeMillis();
long requestTime = endTime - startTime;
Logger.info("{} {} took {}ms and returned {}",
requestHeader.method(), requestHeader.uri(), requestTime, result.status());
akka.util.ByteString body = play.core.j.JavaResultExtractor.getBody(result, 10000l, mat);
Logger.info(body.decodeString("UTF-8"));
return result.withHeader("Request-Time", "" + requestTime);
});
}
}
What is it doing?
First this creates a new Filter which can be used along with other filters you may have. In order to get the body of the response we actually use the nextFilter - once we have the response we can then get the body.
As of Play 2.5 Akka Streams are the weapon of choice. This means that once you use the JavaResultExtractor, you will get a ByteString, which you then have to decode in order to get the real string underneath.
Please keep in mind that there should be no problem in copying this logic in the Action that you are creating. I just chose the option with Filter because of the reason stated at the top of my post.

BlazeDS - AMFConnection.call giving HTTP 400 status

I'm trying to use BlazeDS's AMFConnection class to connect to pyamf, but when I call AMFConnection.call(), I get HTTP status 400 (Bad Request - "The request body was unable to be successfully decoded."). I'm more or less following this example: (pyamf.org/wiki/ClientHowTo ... sorry, I'm a new user so I guess I can't use hyperlinks. append a "http://" to those if you want to follow them)
Here's my code:
package amfconnectiontest;
import flex.messaging.io.amf.client.AMFConnection;
import flex.messaging.io.amf.client.exceptions.*;
public class Main {
public static void main(String[] args) {
AMFConnection amfConnection = new AMFConnection();
String url = "http://demo.pyamf.org/gateway/recordset";
String service = "service.getLanguages";
try
{
amfConnection.connect(url);
}
catch (ClientStatusException cse)
{
System.out.println(cse);
return;
}
// Make a remoting call and retrieve the result.
try
{
Object result = amfConnection.call(service);
System.out.println("results: " + result.toString());
}
catch (ClientStatusException cse)
{
System.out.println(cse);
}
catch (ServerStatusException sse)
{
System.out.println(sse);
}
// Close the connection.
amfConnection.close();
}
}
Any ideas?
The ability to en/decode BlazeDS specific messages (implementing ISmallMessage) has landed on the PyAMF trunk (r2726 and up). See the related ticket - http://pyamf.org/ticket/581
This version or one very similar is likely to become 0.5. If you need to connect to a BlazeDS service I would suggest checking out the trunk.

Categories

Resources