I am trying to get a String of http response (just a JSON object), and sometimes this piece of code waits infinite time in
line = reader.readLine()
May be, the reason of such behavior is bad Internet connection (I use 3G modem), but I need a stable solution to avoid this infinite lock. What can be done here to avoid it?
HttpResponse response = httpClient.execute(httpPost);
InputStream content = null;
JSONObject json_obj;
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
try {
content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 256);
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {}
}
You can specify a read timeout:
HttpClient client = new DefaultHttpClient();
client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000);
Should set a read timeout to 30 seconds.
Probably, you also want to specify a connection timeout:
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
readLine()
Or any network/IO should be done in a background thread to prevent locking of the main thread.
If you test your code on Android 4.0+ you'll also see that networking is no longer allowed on the main thread and will throw an exception.
Take a look at AsyncTask which is a simple, painless way of running such tasks on a background thread.
A good solution to avoid the "infinite" lock is to do this http calls in a separate thread, and with a Handler, notice to the main thread that the info is loaded and can use it like you want.
Here an example of my own using a SaxParser too:
public void SearchFromSuperClass(String text)
{
mHandler = new Handler();
Thread t = new Thread(){
public void run() {
try {
String strurl="URLTOPATH";
URL url = new URL(strurl);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
FundsHandlerRanking handler = new FundsHandlerRanking();
parser.parse(url.openConnection().getInputStream(), handler);
search_response = handler.getrankings();
mHandler.post(mUpdateResults);
} catch (Exception e) {
search_response = null;
mHandler.post(mUpdateResults);
}
}
};
t.start();
}
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
private void updateResultsInUi() {
try
{
if(search_response != null)
{
lview.setAdapter(new SearchRankingAdapter(mycontext, search_response, false));
}
Pdialog.dismiss();
}
catch (Exception e) {
lview.setAdapter(null);
if (Pdialog.isShowing())
{
Pdialog.dismiss();
}
}
}
With mHandler.post(...) you put the call in the queue to be sended to the main UI thread, and there, you can modify UI objects without problem (CAN'T modify UI objects outside the Main thread).
Hope this helps
Related
EDIT: AsyncTask was called lots of times to update status in Server
and at the same time, my app tried to upload files. AsyncTask by
default, do only one operation at a time (serial mode) but you can put
it in Parallel mode, as I answered below.
After running normally during several hours (and sometimes a few minutes), my app stops connecting to the server with HTTPost. I set the Timeout to 20 sec and It's a good time since the connection is good (wifi). I have tested it in two servers: my own computer and a virtual private server. The problem happens equally.
Is there some way or reason for Android to avoid connecting to the http server?
Is there another reason for this to happen?
Is there a better way of doing this?
Thanks!!
This is how I make a POST request:
try{
new ConexaoHTTPPost.SolicitaDados(parametros).execute(url).get(20000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
new ReportException(getApplicationContext()).send(e,classeToErr);
} catch (ExecutionException e) {
new ReportException(getApplicationContext()).send(e,classeToErr);
} catch (TimeoutException e) {
new ReportException(getApplicationContext()).send(e,classeToErr);
}
HTTPost Code:
public class ConexaoHTTPPost {
public static String postDados(String urlUsuario, String parametrosUsuario) {
URL url;
HttpURLConnection connection = null;
try {
url = new URL(urlUsuario);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Lenght","" + Integer.toString(parametrosUsuario.getBytes().length));
connection.setRequestProperty("Content-Language","pt-BR");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
outputStreamWriter.write(parametrosUsuario);
outputStreamWriter.flush();
outputStreamWriter.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
StringBuffer resposta = new StringBuffer();
String linha;
while ( (linha = bufferedReader.readLine()) != null){
resposta.append(linha);
resposta.append('\r');
}
bufferedReader.close();
return resposta.toString();
}catch (Exception erro){
return null;
}finally {
if(connection != null){
connection.disconnect();
}
}
}
public static class SolicitaDados extends AsyncTask<String, Void, String> {
private String parametros;
//private String resultado = null;
public SolicitaDados(String parametros) {
this.parametros = parametros;
}
#Override
protected String doInBackground(String... urls) {
return ConexaoHTTPPost.postDados(urls[0], parametros);
}
/*
#Override
protected void onPostExecute(String resultado){
this.resultado = resultado;
}
*/
}
}
I solved It.
I was using execute() method but, actually, the correct way is to use executeOnExecutor() in this line:
new ConexaoHTTPPost.SolicitaDados(parametros).execute(url).get(20000,
TimeUnit.MILLISECONDS);
execute() executes everything in serial in the UI Thread, so you can't upload two files (or data) at the same time because the connection get blocked by the UI thread.
When using executeOnExecutor() method, you can upload multiple files ou data. I had only to change this line as shown:
new ConexaoHTTPPost.SolicitaDados(parametros).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
url).get(20000, TimeUnit.MILLISECONDS);
I changed it in all places it appears.
PS: You have to use AsyncTask.THREAD_POOL_EXECUTOR as a Thread Executor.
This question already has answers here:
How to avoid the ANR dialog Application Not Responding
(3 answers)
Closed 5 years ago.
I'm using the method below to extract to convert XML into JSON.!
First I a pass the url for the api call of the xml and then extract
the data and put it in String.
Then using this Library, I
convert the xml into JSON Object.
Finally I use the json object data
to get web data and populate my recyclerviews.
It works well, but sometimes it gives me ANR dialog..! any help?
public static void getResponse(final String xmlLink, final Activity activity,
final GetJSONRespone getjson) {
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable() {
#Override
public void run() {
URL url = null;
BufferedReader in = null;
try {
url = new URL(xmlLink);
in = new BufferedReader(
new InputStreamReader(
url.openStream(), "UTF-8"));
String inputLine;
System.setProperty("http.keepAlive", "false");
StringBuilder builder = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
builder.append(inputLine);
}
String urlContent = builder.toString();
XmlToJson xmlToJson = new XmlToJson.Builder(urlContent).build();
JSONObject response = xmlToJson.toJson();
Log.i("response: ", response.toString());
getjson.getJSON(response);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
}
This dialog appears when main thread of the application blocked for too long. So try to use ASYNCTASK to avoid this halt.
Basically, i have an app that uses JSON. I get the JSON in new HentData.execute(); and pass it to a string variable. But when I try to acutally do something with it my program crashes.
HentData extends AsyncTask, I know it gives me a JSON string that works
INSIDE oncreate()
new HentData().execute();
jsonToArray();
arrayToText();
This crashes
But when I run them like this it works, do I have to close the HentData class somehow?
protected void onPostExecute(String resultat){
json_string = resultat;
jsonToArray();
arrayToText();
}
This is my doInBackground()
protected String doInBackground(Void... voids){
try {
URL url = new URL(json_url);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream IS = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(IS));
StringBuilder sb = new StringBuilder();
while((json_string = bufferedReader.readLine())!=null){
sb.append(json_string+"\n");
}
bufferedReader.close();
IS.close();
httpURLConnection.disconnect();
return sb.toString().trim();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
But when I run them like this it works, do I have to close the HentData class somehow?
protected void onPostExecute(String resultat){
json_string = resultat;
jsonToArray();
arrayToText();
}
You don't have to close anything. This works because "async" in AsyncTask makes the code run in the background.
In other words,
new HentData().execute(); // Not waiting for result
jsonToArray(); // Continue on, even though there is no result yet --> error
arrayToText();
If you want a more flexible way to get results, see How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
If you hate writing AsyncTasks (for HTTP methods), see Comparison of Android networking libraries: OkHTTP, Retrofit, and Volley
I'm facing with an issue when try to send 'signal' to my AsyncTask class to stop execution and close socket connection. In doInBackground method I setting up socket connection, sending first payload packet and waiting for incoming packets:
mRunning = true;
try {
byte[] data = null;
mSocket = mTlsSocketFactory.createSocket(mTlsSocketFactory.getServerAddress(), mTlsSocketFactory.getServerPort());
LogHelper.printLogMsg("INFO socket created");
out = new DataOutputStream(mSocket.getOutputStream());
out.flush();
inStream = new DataInputStream(mSocket.getInputStream());
//send authenticate payload
requestSendPayload(authenticatePayload, params);
while (mRunning) {
int type = inStream.readInt();
type = Integer.reverseBytes(type);
mResID = type;
int length = inStream.readInt();
length = Integer.reverseBytes(length);
if (length > 0) {
data = new byte[length];
inStream.readFully(data);
publishProgress(data);
}
data = null;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) out.close();
if (inStream != null) inStream.close();
if (mSocket != null) mSocket.close();
} catch (IOException e) {
e.fillInStackTrace();
}
}
When I receive packet that I want, I should close connection. I have a public method inside AsyncTask class:
public void close() {
mRunning = false;
}
But the problem is that 'while' block never ends and doInBackground never finished.
There is a lot of posts with similar problem but I tried to call cancel(true) on my AsyncTask but with no result - doInBackground never finished. My question is how to send 'signal' to doInBackground method so that my while loop be able to finish?
I can rewrite closeMethod to something like this:
new Thread(new Runnable() {
#Override
public void run() {
try {
if (out != null) out.close();
if (inStream != null) inStream.close();
if (mSocket != null) mSocket.close();
} catch (IOException e) {
e.fillInStackTrace();
}
}
}).start();
And after that, I catch exception when try to read: DataInputStream.readInt() and then doInBackground will end. But I'm not sure if this is the correct solution.
You should implement OnCancelled in your async task method
#Override
protected void onCancelled() {
mRunning = false;
}
after that you just call
mySocketAsyncTaskObject.cancel(false);
Another option would be to remove your mRunning property and OnCancelled override and just check for IsCancelled()
while (!IsCancelled()) {
int type = inStream.readInt();
type = Integer.reverseBytes(type);
mResID = type;
int length = inStream.readInt();
length = Integer.reverseBytes(length);
if (length > 0) {
data = new byte[length];
inStream.readFully(data);
publishProgress(data);
}
data = null;
}
As a personal note, i want to add, that it is good practice to use Service class for incapsulation your Socket functionality and use plain Thread object not AsyncTask
I have a TCP socket connection which works well on Android 2.3 but now facing some problems on Android 4.1.
The problem is that InputStream.read() method always returns -1 (without blocking), like the connection is closed.
Creating socket:
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);
Retrieving input and output streams and writing some initial data:
InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();
Then, this condition always passes without blocking:
int c = inputStream.read();
if (c < 0) {
Log.d(TAG, "End of stream");
}
This code is running in a background thread. And it was working on Gingerbread.
Tried to use InputStreamReader and OutputStreamWriter instead of direct streams - no effect.
I have seen that very same error before, although this answer might look offtopic give it a chance and let me know if it worked, for some reason sockets are having strange behavior on jellybean even when they were working completely fine in lower android versions, the way I fixed this issue was to move the targetSdkVersion to jelly bean as well as the Project Build Target under Android properties of the project, didn't modify one single line of code, just that, and for some reason it does the trick...
Hope this helps.
Regards!
I had some similar issue where the inputStream.read() returned -1 and I did not get any Exception. In fact the server was down and the connection broken. I didn't test it with different versions, only with 4.0.
Here's the Google Bug Report about this behavior.
Unfortunately status of the bug seems to be 'closed' as not reproduceable.
My work around was to interpret the -1 as a close of the socket and an unreachable server. When you try to reconnect, you get the right errors.
I have had a similar problem and fixed it with a workaround like this
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private static class WatchDog implements Runnable{
private Thread thread = Thread.currentThread();
public void run() {
Log.d(LOG_TAG, "Interrupting read due to timeout");
thread.interrupt();
}
}
private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
int startingPos = bb.position();
long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;
ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
try {
while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
try{
int read = in.read(bb.array(), bb.position(), bb.remaining());
if(read > 0){
bb.position(bb.position()+read);
}
} catch(SocketTimeoutException e){}
if(bb.hasRemaining()){
Thread.sleep(5);
}
}
watchdogFuture.cancel(true);
} catch (InterruptedException e) {}
if(bb.hasRemaining()){
throw new SocketTimeoutException("Unable to read requested bytes: "
+ (bb.position()-startingPos) + "/" + (bb.limit()-startingPos)
+ " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
}
}
Using BufferedReader and PrintWriter works on all versions for me and is extremely convenient for sending and receiving anything you want (even JSON strings) via any communication protocol. Try saving them as member variables when starting your background thread like this:
mInput = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
For asynchronous communication your background thread might then look like this:
#Override
public final void run() {
while (!Thread.currentThread().isInterrupted()) {
if (mInput == null) {
break;
}
String message = null;
try {
message = mInput.readLine();
} catch (IOException e) {
// handle the exception as you like
break;
}
if (Thread.currentThread().isInterrupted()) {
// thread was interrupted while reading
break;
} else if (message != null) {
// handle the message as you like
}
}
}
Use another background thread to send messages:
#Override
public void run() {
if (mOutput != null) {
mOutput.println(<message to be );
if (mOutput == null) {
// the above thread was interrupted while writing
} else if (!mOutput.checkError()) {
// everything went fine
} else {
// handle the exception
}
}
}
Also, you will have to close the streams from outside to make sure readLine doesn't block forever:
try {
mOutput.close();
mInput.close();
mOutput = null;
mInput = null;
} catch (IOException e) {
// log the exception
}
Now, since you're using TCP sockets it may happen that the socket is actually dead and readLine is still blocking. You have to detect that and close the streams just like above. For that, you will have to add another thread (oh well) that periodically sends keep-alive-messages. If no message was received from the remote device for X seconds, it has to close the streams.
This whole approach makes sure the socket is closed and all threads finish at all circumstances. Of course you can make the communication synchronous, if that is what you need, by removing the sender-thread and including println() inside the reader-thread instead. I hope that helps you (even though the answer comes 8 months late).
Friend,
try inputStream.readLine(); (i.e) DataInputStream.readLine(); (Deprecated method)
this worked for me...
Try this code -
Runnable runnable = new Runnable() {
#Override
public void run() {
synchronized (this) {
Socket s = null;
String inMsg = null, msg2 = null;
try {
try {
s = new Socket(server, port);
} catch (Exception e) {
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
try {
inMsg = in.readLine()
+ System.getProperty("line.separator");
} catch (Exception e) {
return;
}
out.write(message + "\n\r");
out.flush();
try {
msg2 = in.readLine();
if (msg2 == null) {
return;
}
} catch (Exception e) {
return;
}
out.close();
s.close();
} catch (Exception e) {
return;
}
}
}
};
It works for me.
You should use Apache Commons IO: http://commons.apache.org/proper/commons-io/
See IOUtils.copy() http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/package-summary.html