Apperside
Apperside

Reputation: 3602

Android AsyncTask and Thread different behavior

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

Answers (2)

Tal Kanel
Tal Kanel

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

tyczj
tyczj

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

Related Questions