Reputation: 3602
I'm posting this question because I think this behaviour is really strange.
I wrote an AsyncTask
to download a file from the web. This AsyncTask
is declared in a separate class so I can call it from everywhere. I noticed that during the AsyncTask
execution my app was reacting very slowly, that is strange since the moment that, by definition, an AsyncTask
's doInBackground
method should run in a separate thread. I really wasn't understanding what was causing my app slowdown. Then I tryied to do the same thing with a thread: PUFFF!! 10 times better!!
Shouldn't it be the same thing?? Please can someone explain me why using a Thread
my app is by far more responsive than using an AsyncTask
?
Here are my implementations of Thread
and AsyncTask
.
With this implementation of AsyncTask
, once started the task, the UI become really unresponsive
public class DownloaderTask extends AsyncTask {
private Context context;
private PowerManager.WakeLock mWakeLock;
String fileName;
public DownloaderTask(Context context) {
this.context = context;
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
}
@Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
String credentials = "user" + ":" + "password";
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
connection.setRequestMethod("GET");
connection.addRequestProperty("Authorization", "Basic " + base64EncodedCredentials);
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
fileName = url.toString().substring(url.toString().lastIndexOf("/") + 1);
output = new FileOutputStream(fileName + "_temp");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
// if (fileLength > 0) // only if total length is known
// publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
@Override
protected void onPostExecute(String result) {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("File downloaded").setContentText("The requested files have been downloaded").setAutoCancel(true);
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(fileName + "_temp");
file.renameTo(new File(fileName));
file = new File(fileName);
Uri uri = Uri.fromFile(file);
resultIntent.setData(uri);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, resultIntent, 0);
mBuilder.setContentIntent(contentIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
if (result != null)
Toast.makeText(context, "Download error: " + result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show();
}
Using this implementation of thread, that does the same this, the UI if completly responsive
public class DownloaderTask extends Thread {
private Context context;
private PowerManager.WakeLock mWakeLock;
String fileName;
String url;
public DownloaderTask(Context context, String url) {
this.context = context;
this.url = url;
}
@Override
public synchronized void run() {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(this.url);
connection = (HttpURLConnection) url.openConnection();
String credentials = "user" + ":" + "password";
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
connection.setRequestMethod("GET");
connection.addRequestProperty("Authorization", "Basic " + base64EncodedCredentials);
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
postResult("Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage());
this.interrupt();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
fileName = url.toString().substring(url.toString().lastIndexOf("/") + 1);
output = new FileOutputStream(fileName + "_temp");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
total += count;
// publishing the progress....
// if (fileLength > 0) // only if total length is known
// publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
postResult(e.toString());
interrupt();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
postResult(null);
};
protected void postResult(String result) {
Intent downloadComplete = new Intent("OWNLOAD_COMPLETE");
context.sendBroadcast(downloadComplete);
NotificationCompat.Builder mBuilder;
// Creates an explicit intent for an Activity in your app
if (result != null) {
mBuilder= new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("Download failed").setContentText("The request download has failed").setAutoCancel(true);
new File( fileName + "_temp").delete();
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(null);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
} else {
mBuilder= new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher).setContentTitle("Download complete").setContentText("The request files have been downloaded").setAutoCancel(true);
Intent resultIntent = new Intent();
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(fileName + "_temp");
file.renameTo(new File(fileName));
file = new File(fileName);
Uri uri = Uri.fromFile(file);
resultIntent.setData(uri);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, resultIntent, 0);
mBuilder.setContentIntent(contentIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
}
}
Upvotes: 1
Views: 257
Reputation: 10785
Not sure, but i bet the problem is that you probably call AsyncTask.doInBackground()
explicitly instead of using AsyncTask.execute()
method.
calling doInBackground()
explicitly bypassing the entire asyncTask mechanism, and performing the code on the same thread that called it (probably the UI main thrad in your case).
more info: http://developer.android.com/reference/android/os/AsyncTask.html
Upvotes: 2
Reputation: 73753
Starting in Honeycomb 3.0 AsyncTasks tasks are executed on a single thread to avoid common application errors caused by parallel execution.
so if you are running multiple tasks at the same time only one will be running at a time.
if you want to run parallel tasks you need to use
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Upvotes: 0