Why do I have multiple dangling OkHttp ConnectionPool threads - android

I have an Android app which is performing HTTP get requests in the background. I'll give some details of how this is performed shortly. I'll focus on describing the issue first.
I've observed in the debugger that OkHttp ConnectionPools are stacking up in the Threads window in Android Studio. This is concerning me as it seems to indicate a resource leak and over time these will increase indefinitely (it seems).
My main problem is that I have no idea why this would be happening. I'm almost certain that it's my fault, but I don't have a lead on what to look for. So my general question is:
"What might cause a new pool to be created each time a HTTP request is made?"
Firstly, here's the code I use to do the get request. Note that it's simplified, but I've run with this exact code and see the problem.
private TestResult test() {
final long startTime = SystemClock.elapsedRealtime();
final String server = "connectivitycheck.gstatic.com";
URL url;
try {
url = new URL("http", SOME_PATH);
} catch (MalformedURLException e) {
// We shouldn't get here since the url is well known
return new TestResult(
"failed to generate url " + e,
SystemClock.elapsedRealtime() - startTime
);
}
HttpURLConnection urlConnection = null;
int httpResponseCode;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
InputStream is = urlConnection.getInputStream();
httpResponseCode = urlConnection.getResponseCode();
is.close();
} catch (IOException e) {
return new TestResult(
e,
SystemClock.elapsedRealtime() - startTime
);
} finally {
if (urlConnection != null)
urlConnection.disconnect();
}
if (httpResponseCode != HttpURLConnection.HTTP_NO_CONTENT) {
return new TestResult(
"invalid error code, expected 204 but got " + httpResponseCode,
SystemClock.elapsedRealtime() - startTime
);
}
return new TestResult(
SystemClock.elapsedRealtime() - startTime
);
}
So, when is this code run...?
Firstly let me say that I've tried running this code from my home activity on my app (in a separate thread - as HTTP isn't allowed on the main android thread). When I do that, everything is fine. I can make multiple requests and never see more than one pool.
The situation in which I run the code and see the issues is as follows:
I have a background Service
In that service I start a new Thread
In that thread I create an ExecutorService:
ExecutorService executorService = Executors.newCachedThreadPool();
CompletionService<FutureResult> completionService = new ExecutorCompletionService<>(executorService);
Using that ExecutorService I submit a callable task which runs my test method (above).
Note that this may all sound convoluted but there's purpose to all of this. For one, the ExecutorService will be performing many other tasks other than just running the test method.
I've had a good look for hanging references that somehow might be keeping the underlying HTTP resources alive but I can't see any.
Does anyone with good knowledge of OkHttp know why this might happen? I'm hoping for some insight to help me uncover the problem.

The easiest way to find out is to set a breakpoint in the ConnectionPool constructor and run your app. That'll show who is creating it. My guess is it's an unrelated class like a crash reporter or analytics library.

Related

How to read available text (not ending with new line) from TCP socket using BufferedReader

I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.

Picasso working incorrectly after overriding OkHttpDownloader.load()

I have the following requirements for image download:
ignoring SSL errors (yes I am aware of the risks)
using a session cookie
I tried to adapt Picasso 2.4.0 to do that, below is my approach:
public static Picasso getPicasso(Context context) {
/* an OkHttpClient that ignores SSL errors */
final OkHttpClient client = getUnsafeOkHttpClient();
return new Picasso.Builder(context)
.downloader(new OkHttpDownloader(client) {
#Override
public Response load(Uri uri, boolean localCacheOnly) throws IOException {
final String RESPONSE_SOURCE_ANDROID = "X-Android-Response-Source";
final String RESPONSE_SOURCE_OKHTTP = "OkHttp-Response-Source";
HttpURLConnection connection = openConnection(uri);
connection.setRequestProperty("Cookie", getCookieHandler().
getCookieStore().getCookies().get(0).toString());
connection.setUseCaches(true);
if (localCacheOnly)
connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE);
int responseCode = connection.getResponseCode();
if (responseCode == 401)
relogin();
else if (responseCode >= 300) {
connection.disconnect();
throw new ResponseException(responseCode + " " + connection.getResponseMessage());
}
String responseSource = connection.getHeaderField(RESPONSE_SOURCE_OKHTTP);
if (responseSource == null)
responseSource = connection.getHeaderField(RESPONSE_SOURCE_ANDROID);
long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
boolean fromCache = parseResponseSourceHeader(responseSource);
return new Response(connection.getInputStream(), fromCache, contentLength);
}
}).build();
}
The only thing that I changed from the original source is adding a Cookie for the HttpURLConnection. I also copied (unchanged) the parseResponseSourceHeader() method since it has private access.
Note that the approach given here does NOT work (response code 401).
The image loading basically works, but there are major issues:
caching doesn't work (fromCache is always false and Picasso always reloads an image which has already been downloaded)
there's no "Content-Length" header, so contentLength is always -1
though the cache doesn't work, the RAM usage increases when loading next image (into exactly the same or any other ImageView), it seems the Bitmap object stays somewhere in the memory
when used inside the BaseAdapter of a GridView, it seems that Picasso tries to load all (or at least as many as the number of times getView() was called) images at the same time. Those images appear, then the app freezes and closes with the following (OOM?) log:
A/Looper﹕ Could not create wake pipe. errno=24
or
A/Looper﹕ Could not create epoll instance. errno=24
The described issues occur no matter if I use a custom Target of just an ImageView.
It seems I have broken some of Picasso mechanisms by overriding the load() method of the OkHttpDownloader, but I'm not getting what's wrong since I did minimal changes. Any suggestions are appreciated.
In case someone has a similar problem: it was a really lame mistake of mine. I was creating multiple Picasso instances which is complete nonsense. After ensuring the singleton pattern with a helper class that returns a single Picasso instance everything works as intended.

Is it a good way to call AsyncTask in infinity loop to pull data from server?

Well in app I'm trying to pull the data from sever for every 4 sec,and update the app.
I'm using handler,in that I'm calling AsynTask to fetch the data from server for every 4 sec.
Just I'm worried about the instance created for AsynTask every 4'sec causes any problem ?
This is what I'm doing.
private static final int DELAY = 1000 * 4;
final Handler printHandler = new Handler();
private boolean keepLooping = true;
printHandler.postDelayed(printStuff, DELAY);
Runnable printStuff = new Runnable(){
#Override
public void run(){
// call AsynTask to perform network operation on separate thread
new DownloadMainScore().execute("http://server/root/score.php");
if(keepLooping)
printHandler.postDelayed(this, DELAY);
}
};
On your choice of concurrency tool:
You are right that this is not so good. AsyncTasks are designed to be useful helpers when designing occasional asynchronous calls that then need to update a UI. As such, in old (< 1.6) versions of Android the maximum thread pool size was 10!
It would be better to go straight to the very robust Java out of which AsyncTask is built. Given you want to do this repeatedly, try a ScheduledExecutorService. I see they've even made a nice example for you.
Or, given that you seem to be getting a score down, best might be to maintain a persistent connection over a protocol like XMPP, for which there are many Java server and clients.
Finally, you might like to look at gcm.
On design issues in general
I see you want to print a score frequently. Once every four seconds in fact. But what's the point is the score hasn't changed? Furthermore, what if you've got a slow internet connection, and eight seconds later the one for four seconds ago hasn't finished? Right now you will set off yet another download request, even though the other one when it comes back will be up to date!
The solution is to decouple the download mechanism and the UI update mechanism. One way to do it is to have your scheduled download on a single threaded executor- not something you can control in an AsyncTask, which when finishes causes the UI to update and show the score.
Wishing you the best of luck!
Code sketch
Don't have environment set up right now, but in a very rough code sketch (check syntax), using a scheduled executor would look like:
In class:
private final ScheduledExecutorService downloadScheduler = Executors.newSingleThreadScheduledExecutor(1);
Then elsewhere, wherever you start doing this
final Runnable scoreHttpRunnable = new Runnable() {
#Override public void run() {
...
//do Http Syncronously here- I guess whatever is in the doInBackground(...) part of that Async task you wrote!
...
final int newScoreResult = ... (do whatever you do here)
...
runOnUiThread(new Runnable() { #Override public void run() { yourView.updateHoweverYouLike(newScoreResult); } })
...
};
downloadScheduler.scheduleAtFixedRate(scoreHttpRunnable, 0, 4, TimeUnit.SECONDS);
Going one of the other two routes is really too much to post in a single answer to a question. That'd be a another SO question if there isn't already one.
Be sure that next call send to asyc class only after once its done for that make a variable(IsLoadRunning) and make it true in on preExecute() and false in onPOstExecute and add a condition if(!IsLoadRunning){new DownloadMainScore().execute();}
As official documentation states
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
Services can serve better in you case. Have a look at the accepted answer here
#Override
protected String doInBackground(String... params) {
Log.d(TAG, "type - " + params[0] + ", url = " + params[1] + ", name = " + params[2]);
downloadFile(params[1], params[2]);
return null;
}
here is download method
URL url = new URI(Url.replace(" ", "%20")).toURL();
URLConnection connection = url.openConnection();
connection.setConnectTimeout(1000);
int fileLength = connection.getContentLength();
mSavePath = CommonUtilities.getFileSavePath(mContext, fileName, fileLength);
Log.d(TAG, "*** saveFilePath - " + mSavePath);
InputStream inputStream = connection.getInputStream();
if (inputStream != null) {
File file = new File(mSavePath);
BufferedOutputStream bufferOutputStream = new BufferedOutputStream(new FileOutputStream(file));
byte byteArray[] = new byte[1024];
int len = 0;
long total = 0;
while ((len = inputStream.read(byteArray)) != -1) {
bufferOutputStream.write(byteArray, 0, len);
total += len;
}
bufferOutputStream.flush();
bufferOutputStream.close();
inputStream.close();
} else {
Log.d(TAG, "*** inputStream is null");
}

Android's SSLServerSocket causes increasing native memory in the App, OOM

Background
I am developing an Android App which provides a simple HTTP/HTTPS server. If the HTTPS serving is configured then on every connection an increasing native memory usage is observed which eventually leads to an app crash (oom), while using the HTTP configuration keeps the native memory usage relative constant. The app's Java VM keeps relative constant in both configurations.
The app serves an HTML page which contains a javascript with periodic polling (one json poll every second), so calling the app page using the HTTPS configuration and keeping the page open for several hours will lead to the mentioned out-of-memory because of increasing native memory usage. I have tested many SSLServerSocket and SSLContext configurations found on internet with no luck.
I observe the same problem on various Android devices and various Android versions beginning with 2.2 up to 4.3.
The code for handling client requests is the same for both configurations HTTP/HTTPS. The only difference between the two configurations is the setup of the server socket. While in the case of HTTP server socket one single line similar to this "ServerSocket serversocket = new ServerSocket(myport);" does the job, in the case of HTTPS server setup the usual steps for setting up the SSLContext are taken -- i.e. setting up the keymanager and initializing the SSLContext. For now, I use the default TrustManager.
Need For Your Advice
Does somebody know about any memory leak problems in Android's default TLS Provider using OpenSSL? Is there something special I should consider to avoid the leak in the native memory? Any hint is highly appreciated.
Update: I have also tried both TLS providers: OpenSSL and JSSE by explicitly giving the provider name in SSLContext.getInstance( "TLS", providerName ). But that did not change anything.
Here is a code block which demonstrates the problem. Just create a sample app put it into the bottom of the main activity's onCreate and build & run the app. Make sure that your Wifi is on and call the HTML page by following address:
https://android device IP:9090
Then watch the adb logs, after a while you will see the native memory beginning to increase.
new Thread(new Runnable() {
public void run() {
final int PORT = 9090;
SSLContext sslContext = SSLContext.getInstance( "TLS" ); // JSSE and OpenSSL providers behave the same way
KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
char[] password = KEYSTORE_PW.toCharArray();
// we assume the keystore is in the app assets
InputStream sslKeyStore = getApplicationContext().getResources().openRawResource( R.raw.keystore );
ks.load( sslKeyStore, null );
sslKeyStore.close();
kmf.init( ks, password );
sslContext.init( kmf.getKeyManagers(), null, new SecureRandom() );
ServerSocketFactory ssf = sslContext.getServerSocketFactory();
sslContext.getServerSessionContext().setSessionTimeout(5);
try {
SSLServerSocket serversocket = ( SSLServerSocket )ssf.createServerSocket(PORT);
// alternatively, the plain server socket can be created here
//ServerSocket serversocket = new ServerSocket(9090);
serversocket.setReceiveBufferSize( 8192 );
int num = 0;
long lastnatmem = 0, natmemtotalincrease = 0;
while (true) {
try {
Socket soc = (Socket) serversocket.accept();
Log.i(TAG, "client connected (" + num++ + ")");
soc.setSoTimeout(2000);
try {
SSLSession session = ((SSLSocket)soc).getSession();
boolean valid = session.isValid();
Log.d(TAG, "session valid: " + valid);
OutputStream os = null;
InputStream is = null;
try {
os = soc.getOutputStream();
// just read the complete request from client
is = soc.getInputStream();
int c = 0;
String itext = "";
while ( (c = is.read() ) > 0 ) {
itext += (char)c;
if (itext.contains("\r\n\r\n")) // end of request detection
break;
}
//Log.e(TAG, " req: " + itext);
} catch (SocketTimeoutException e) {
// this can occasionally happen (handshake timeout)
Log.d(TAG, "socket timeout: " + e.getMessage());
if (os != null)
os.close();
if (is != null)
is.close();
soc.close();
continue;
}
long natmem = Debug.getNativeHeapSize();
long diff = 0;
if (lastnatmem != 0) {
diff = natmem - lastnatmem;
natmemtotalincrease += diff;
}
lastnatmem = natmem;
Log.i(TAG, " answer the request, native memory in use: " + natmem / 1024 + ", diff: " + diff / 1024 + ", total increase: " + natmemtotalincrease / 1024);
String html = "<!DOCTYPE html><html><head>";
html += "<script type='text/javascript'>";
html += "function poll() { request(); window.setTimeout(poll, 1000);}\n";
html += "function request() { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( \"GET\", \"/\", false ); xmlHttp.send( null ); return xmlHttp.responseText; }";
html += "</script>";
html += "</head><body onload=\"poll()\"><p>Refresh the site to see the inreasing native memory when using HTTPS: " + natmem + " </p></body></html> ";
byte[] buffer = html.getBytes("UTF-8");
PrintWriter pw = new PrintWriter( os );
pw.print("HTTP/1.0 200 OK \r\n");
pw.print("Content-Type: text/html\r\n");
pw.print("Content-Length: " + buffer.length + "\r\n");
pw.print("\r\n");
pw.flush();
os.write(buffer);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
soc.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
-- EDIT --
I have uploaded a sample app project called SSLTest for eClipse which demonstrates the problem:
http://code.google.com/p/android/issues/detail?id=59536
-- UPDATE --
Good news: today the reported Android issue above was identified and proper submissions were made to fix the memory leak. For more details see the link above.
I imagine this would be a substantial time investment, but I see that Valgrind has been ported to Android. You could try getting that up and running. Of course, if you find there's an internal memory leak, there isn't a lot you can do about it except attempt to get the bug fixed in future Android releases.
As a workaround, you could make your application multi-process and put the https service in a separate process. That way you could restart it periodically, avoiding OOM. You might also have to have a third process just accepting port 443 connections and passing them on to the https worker - in order to avoid tiny outages when the https worker is restarted.
This also sounds like a substantial time investment :) But it would presumably successfully avoid the problem.
--- EDIT: More detail ---
Yes, if you have a main application with its own UI, a worker process for handling SSL and a worker process for accepting the SSL requests (which as you say probably can't be 443), then on top of your normal Activity classes, you would have two Service classes, and the manifest would place them in separate processes.
Handling SSL process: Rather than waiting for an OOM to crash the service, the service could monitor its own Debug.getNativeHeapSize(), and explicitly restart the service when it increased too much. Either that, or restart automatically after every 100 requests or so.
Handling listening socket process: This service would just listen on the TCP port you choose and pass on the raw data to the SSL process. This bit needs some thought, but the most obvious solution is to just have the SSL process listen on a different local port X (or switch between a selection of different ports), and the listening socket process would forward data to port X. The reason for having the listening socket process is to gracefully handle the possibility that X is down - as it might be whenever you restart it.
If your requirements allow for there being occasional mini-outages I would just do the handling SSL process, and skip the listening socket process, it's a relatively simple solution then - not that different to what you'd do normally. It's the listening socket process that adds complexity to the solution...
Does it help to explicitly close the input stream? In the sample code the input stream seems to only be closed in the case of a SocketTimeoutException exception.
--EDIT--
You could rename run() to run2() and move the while loop into run() and remove it from run2() and see if that makes a difference? This couldn't be a solution but would tell you if any of the long-lived objects free up the memory when their references are dropped.
There is one detail I would recommend changing in your implementation.
Make a list of all your resource variables, for example Sockets, Streams, Writers, etc. Be sure to have the declaration outside your try statement and be sure to do cleanup / closing in the finally statement. I normally do something like this to be 100% sure:
InputStream in = null;
OutputStream out = null;
try {
//assign a proper value to in and out, and use them as needed.
} catch(IOException e) {
//normal error handling
} finally {
try {
in.close();
} catch(IOException e) {}
try {
out.close();
} catch(IOException e) {}
}
It looks a little bit confusing, but imagine you use your in Stream inside the try block and you get some Exception, then your Streams never get closed and that is a potential reason for memory leaks.
I cannot guarantee that this is the reason, but it should be a good startup point.
About managing your service. I had a lot of bad experiences with Android services because I was running them in the same thread as the GUI. Under some circumstances, Android will see some code that is executing for too long and kill your main process in order to protect from crashes. The solution I found was to follow the suggestion from this tutorial (look at point 4):
http://www.vogella.com/articles/AndroidServices/article.html
After this, my service just worked as expected and didn't interfere with my GUI Process.
Regards

Fastest way to seek (skip) an inputstream with http protocol

I am making a download service of sorts, and it has the ability to resume a previous partial download. I am currently using the skip method like this
long skipped = 0;
while (skipped < track.getCacheFile().length()){
skipped += is.skip(track.getCacheFile().length()-skipped);
}
I just did a test and it took about 57 seconds seconds to skip 45 mb in an inputstream. I am curious how certain native code does this, for instance, the mediaplayer can seek to any part of a remote stream instantaneously. I realize that I do not have access to the same libraries, but can I achieve something similar.
btw, that test was on wifi. It is obviously much slower on normal data networks.
Update: very simple (thanks to below)
if (track.getCacheFile().length() > 0){
request.setHeader("Range","bytes="+track.getCacheFile().length()+"-");
}
If you are using http as a protocol to initiate your inputstream, you may try the RANGE header.
Take a look here :
http://www.west-wind.com/Weblog/posts/244.aspx
The problem with the skip method is that you have to read the data even if you skip them so you need to receive them. The best solution is probably to request the server only the part you want.
You can do it like this:
private InputStream getRemote(String url, long offset) {
try {
URLConnection cn = new URL( url ).openConnection();
cn.setRequestProperty ("Range", "bytes="+offset+"-");
cn.connect();
length = cn.getContentLength();
return cn.getInputStream();
} catch (MalformedURLException e ) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }
return null;
}
Then when you need to skip, you actually do a reconnect via HTTP to the new offset. Works quick and reliable, much better than using the inputstream's skip.

Categories

Resources