BufferedReader with InputStreamReader fed with a finished HTTPResponse causes NetworkOnMainThreadException - android

I have some code that converts my HTTPResponse Object into a JSONObject, which works fine most of the time:
public static JSONObject httpResponseToJson(HttpResponse response) {
if (response != null) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(),
"UTF-8"));
String json = reader.readLine();
if (json != null) {
JSONObject jsonObject = new JSONObject(json);
printStatus(jsonObject);
return jsonObject;
}
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
However, sometimes it throws a the Android NetworkOnMainThread exception. I cannot figure out why, because the response is already finished and there should not be any more network IO involved in that call. For test reasons, if I allow NetworkOnMainThread, this method works fine all the time.
Note that all the HTTPResponse is fetched with an AsyncTask and this is working fine.
I am very interested in any suggestions.

Reading the response from a HttpResponse object also involves a Network Operation. Simply process that also in the doInBackground() method and modify your AsyncTask to pass to the onPostExecute() the real result once processed.

its mean you are performing some network operation on main thread.The point here is Unless the stream is not closed, you are still performing network operation so move that part into doInBackGround() too.

Related

How can I make this code an AsyncTask?

I can't figure out how to make this code work in an AsyncTask, I searched for multiple examples but it keeps crashing. I found this simple code on the internet and I want to adapt it to get the URL from a textfield and get the HTML code. I found out it has to be in an AsyncTask otherwise it won't work but even in an AsyncTask I can't get it to work. Here's my code:
String ETURL = ETURLInput.getText().toString();
try {
URL TestURL = new URL(ETURL);
BufferedReader bufferReader = new BufferedReader(
new InputStreamReader(TestURL.openStream()));
String outputCode;
while ((outputCode = bufferReader.readLine()) != null)
TVCode.setText(outputCode);
bufferReader.close();
} catch (Exception e) {
TVCode.setText("Oops, something went wrong.")
}
}
This is the code which needs to be executed inside an ActionListener. So when I click the button it should execute this code in an AsyncTask.
Hopefully somebody could help me with this.
You forgot to add openConnection, add this: URLConnection conn = TestURL.openConnection(); after creating your URL object.
To make it work with an asynctask, what you can do is storing your string in a class variable, returning it in the doInBackGround and using it in your onPostExecute.
An example of method you can create in your asynctask:
protected String getContentUrl(String URL) {
String line=null;
String result="";
try {
try {
URL url;
// get URL content
url = new URL(URL);
URLConnection conn = url.openConnection();
// open the stream and put it into BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
line=br.readLine();
while (line!= null) {
result=result+line;
line=br.readLine();
}
//System.out.print(result);
br.close();
return result;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
Then you get your result this way on doInBackGround:
getContentUrl(YOUR URL HERE)
Store this value in a String, and return it. Then you can use it in your onPostExecute
Hope it helps :)

How to implement long-running network uploads in Android not using AsyncTask and not using libraries

What is the native Android way to implement long running network operations (like uploading a bunch of photos) without having to use libraries like RoboSpice?
I've read numerous topics on stackoverflow suggesting that asynctask is not suitable for long running operations as it is closely tied to an activity's lifecycle, might lead to memory leaks and since android 3.2 there is only one thread for all asynctasks for an app. (not sure about this last one)
How do I replace my asynctask with something else?
Now, I've heard of handlers, executors, services and what not, but how exactly do I implement them in my code and which one to choose?
Here is an example of the asynctask I use
I have removed a lot of code, just so you can see the basic structure
public class UploadPhotosToServer extends AsyncTask<String, Void, Boolean> {
#Override
protected Boolean doInBackground(String... args) {
HashMap<String, String> params = new HashMap<String, String>();
try {
if(uploadImageToServer(id, path, params)) {
success = true;
} else {
success = false;
}
} catch (Exception e) {
e.printStackTrace();
success = false;
}
return success;
}
public boolean uploadImageToServer(int imageId, String imagePath, HashMap<String, String> params) throws Exception {
try {
JSONObject json = jsonParser.uploadImageToServer(imagePath, params);
JSONObject message = json.getJSONObject("message");
String serverResponse = message.getString("success");
if (serverResponse.contentEquals("true") {
return true;
} else {
return false;
}
} catch (JSONException e) {
e.printStackTrace();
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
and here is jsonParser.uploadImageToServer
public JSONObject uploadImageToServer(String imagePath, HashMap<String, String> params) throws Exception {
HttpResponse response;
MultipartEntityBuilder multipartEntity;
HttpPost postRequest;
HttpContext localContext;
Bitmap bitmap;
try {
// Set the http handlers
httpClient = new DefaultHttpClient();
localContext = new BasicHttpContext();
postRequest = new HttpPost(SERVER + "images");
// Send the package
multipartEntity = MultipartEntityBuilder.create();
multipartEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
multipartEntity.addPart("file", new FileBody(new File(imagePath)));
for (Map.Entry<String, String> entry : params.entrySet()) {
multipartEntity.addTextBody(entry.getKey(), entry.getValue());
}
postRequest.setEntity(multipartEntity.build());
// Get the response. we will deal with it in onPostExecute.
response = httpClient.execute(postRequest, localContext);
HttpEntity httpEntity = response.getEntity();
inputStream = httpEntity.getContent();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
json = sb.toString();
inputStream.close();
reader.close();
} catch (ClientProtocolException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// Try parsing the string to a JSON object
try {
jsonObject = new JSONObject(json);
} catch (JSONException e) {
e.printStackTrace();
}
// Return JSON String
return jsonObject;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I think for a set of uploads I would consider implementing an IntentService. As explained at the link it will process a list of intents in a worker thread until that list is exhausted at which point the service will shutdown again.
The implementation of an IntentService is very simple. An example based on the example you give above;
public class ImageUploadIntentService extends IntentService {
public ImageUploadIntentService() {
super("ImageUploadIntentService");
}
#Override
public void onCreate() {
// Not a required implementation but you might want to setup any dependencies
// here that can be reused with each intent that the service is about to
// receive.
super.onCreate();
}
#Override
public void onHandleIntent(Intent intent) {
// Process your intent, this presumably will include data such as the local
// path of the image that you want to upload.
try {
uploadImageToServer(intent.getExtra("image_to_upload"), params);
} catch (Exception e) {
// Oh :( Consider updating any internal state here so we know the state
// of play for later
}
}
public JSONObject uploadImageToServer(String imagePath, HashMap<String, String> params) throws Exception {
// All of your upload code
}
}
Then to call the service it is as simple as;
Intent intent = new Intent(this, ImageUploadIntentService.class)
.putExtra("image_to_upload", mImagePath);
startService(intent);
This does leave us with the issue of indicating the progress of your upload queue. We can solve this by using a ResultReceiver. A result receiver is Parcelable so we can send it with the intent in order to listen out for results we might be interested in. You can handle the ResultReceiver with either an Activity and suitable piece of progress dialog, or if you want a persistent notification with a progress bar then you could use a Service to host the receiver.
It is a little more involved than using an AsyncTask, but it does give you a little more flexibility and is not as attached to the Activity lifecycle. Another gotcha with the IntentService it will still only make you one worker thread so image uploads could not happen concurrently. But I might consider breaking your Bitmap JPEG compression to it's own IntentService then you could have the compression happening on the next image in the queue while the first is being uploaded.

Progress Bar not showing while waiting for Future to complete

I'm writing some code to discover the different ways of handling asynchronisity in Android, and I've already done AsyncTask, now I've had some luck with futures in the past, but this time it's bugging me a bit...
The code itself works fine, except for the fact that the (spinning) progressbar only shows up after the download operation has completed... despite the fact that I tell the code to display it before...
here's the code:
public void startDownload(final String url) {
bar.setVisibility(View.VISIBLE); // progress bar should appear here
ExecutorService pool = Executors.newCachedThreadPool();
Gson gson = new Gson();
Future<String> promise = pool.submit(new Callable<String>() {
#Override
public String call() throws Exception {
String response = "";
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
String s = "";
while((s = buffer.readLine()) != null) response += s;
} catch (IOException ioe) {
ioe.printStackTrace();
}
return response;
}
});
while(promise.isDone() == false) {
// wait
}
bar.setVisibility(View.GONE); // and disappear here
try {
GitHubStatus status = gson.fromJson(promise.get(), GitHubStatus.class);
statusText.setText(status.getStatus());
bodyText.setText(status.getBody());
dateText.setText(status.getCreationDate());
} catch (ExecutionException ee) {
ee.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
// instead it briefly appears here, and then disappears again
}
For those who don't know; futures execute themselves, and thus you don't need to call a "run"or "start" method to start it
I've tried to put a Log.v("DEBUG: ", "Loading..."); in the loop that waits for it to finish, and it does print the message just fine, many times at that
I've tried without the loop, that doesn't work either, and I've even tried with a ProgressDialog that doesn't work either...
I understand that I can't update the UI thread while the UI thread is busy, but I only set the visibility before and after any of the work in the UI thread is being done, so what's wrong here? Why isn't it working?
Try following:
public void startDownload(final String url) {
bar.setVisibility(View.VISIBLE); // progress bar should appear here
ExecutorService pool = Executors.newCachedThreadPool();
Gson gson = new Gson();
Future<String> promise = pool.submit(new Callable<String>() {
#Override
public String call() throws Exception {
String response = "";
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
String s = "";
while((s = buffer.readLine()) != null) response += s;
} catch (IOException ioe) {
ioe.printStackTrace();
}
runOnUiThread(new Runnable()
{
public void run()
{
bar.setVisibility(View.GONE); // and disappear here
try {
GitHubStatus status = gson.fromJson(promise.get(), GitHubStatus.class);
statusText.setText(status.getStatus());
bodyText.setText(status.getBody());
dateText.setText(status.getCreationDate());
} catch (ExecutionException ee) {
ee.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
});
return response;
}
});
}
Because of
while(promise.isDone() == false) {
// wait
}
You are blocking UIThread.
FutureTask can be used to run asynchronous tasks. However, it's get() method and isDone() method will block when called.
Thus, when you try to show a ProgressBar on the UI thread that has just been blocked by FutureTask, it won't show, till probably after the FutureTask is done.
In your case, your start the ProgressBar before initiating the FutureTask call, which is correct, but once FutureTask starts, running of the ProgressBar is blocked.
To solve this, you can run a FutureTask inside an AsyncTask:
Use the onPreExecute() method of the AsyncTask to show the ProgressBar`.
Use the onProgressUpdate() method of the AsyncTask to update the progress on the ProgressBar.
Use the onPostExecute() method of the AsyncTask to hide the ProgressBar; in a worst case scenario, you can call runOnUiThread() inside the FutureTask's Callable/Runnable, to hide the ProgressBar.

Extracting data from server takes too much time- android

I have a android application, where i extract data from the multiple urls and save then as arraylist of string. It works fine, but for fetching data from 13 urls, it takes close to 15-20 sec. Where as fetching the data from same set of urls take 3-4 sec in same app built using phonegap. Here is the code below.
#Override
protected String doInBackground(String... params) {
client = new DefaultHttpClient();
for(int i=0;i<url.size();i++)
{
get = new HttpGet(url.get(i));
try {
response = client.execute(get);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
entity = response.getEntity();
InputStream is = null;
try {
is = entity.getContent();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(is));
StringBuffer buffer = new StringBuffer();
String line = null;
do {
try {
line = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
buffer.append(line);
} while (line != null);
String str = buffer.toString();
param.add(str);
}
return null;
}
Could anyone please suggest how i can speed this execution and reduce the extraction time.
You could try starting a separate thread for each iteration from the for loop.
Smth like this :
for(int i = 0; i < url.size(); i++){
//start thread that gets data from url and adds it to the list
}

Android webservice GET request replies only a part of the XML response

I'm trying to connect to a webservice offered by my heating at home. Putting the request URL into Chrome results in a complete XML file.
Subsequently I tried to do the same programmatically with an Android application, which unfortunately only replies about the half of the XML file.
I already tried several attempts, amongst others a simple HttpConnection:
private void androidHttpConnect() {
HttpURLConnection urlConnection=null;
try {
URL url = new URL("http://10.0.0.140:8080/user/menu");
urlConnection = (HttpURLConnection) url.openConnection();
BufferedInputStream in = new BufferedInputStream(
urlConnection.getInputStream());
Log.i("myapp",convertStreamToString(in));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
}
private String convertStreamToString(InputStream is) {
return new Scanner(is).useDelimiter("\\A").next();
}
and the Android Http Client ...
HttpClient httpclient = AndroidHttpClient.newInstance("Android");
HttpGet httpget = new HttpGet("http://10.0.0.140:8080/user/menu");
HttpResponse response;
try {
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
Log.d("myapp", "content length "+len);
if (len != -1) {
try {
Log.d("myapp", EntityUtils.toString(entity));
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
// Stream content out
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Interestingly those attempts cut the result on different positions, even though they only differ in about 5 characters. At this position there is no special character and the XML is quite short.
Anyone any idea? I also tried to run it in a ASyncTask to ensure no interrupt by the UI thread, but without success.
Thanks for your help.
Finally found the solution by myself! The problem wasn't the request but the output in the LogCat. Logging every line separately obtained the desired full response!

Categories

Resources