9

I am currently using AsyncTask to download a large file in the background within my app, currently the download progress is shown as a ProgressDialog which is updated via onProgressUpdate as below:

protected String doInBackground(String... sUrl) {
        try {
            String destName = sUrl[1];
            file_Delete(destName); // Just to make sure!

            URL url = new URL(sUrl[0]);
            URLConnection connection = url.openConnection();
            connection.connect();
            int fileLength = connection.getContentLength();

            InputStream input = new BufferedInputStream(url.openStream());
            OutputStream output = new FileOutputStream(destName);

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;
                publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }

            output.flush();
            output.close();
            input.close();

        } catch (Exception e) {
            Log.e(TAG, NAME + ": Error downloading file! " + e.getMessage());
            return e.getMessage();
        }

        return null;
    }

@Override protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        DownloadImage.mProgressDialog.setProgress(progress[0]);


    }

This works fine, however I now want to use a notification in the notification bar so keep track of the download instead (as the file can be rather large and users would like to keep track from outside the app).

I have tried the below code however the UI starts to lag badly, I can see its due to the publishProgress getting called alot, so how could I go about changing the background code to call publishProgress only every second

@Override protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        DownloadImage.mProgressDialog.setProgress(progress[0]);

        DownloadImage.myNotification = new NotificationCompat.Builder(c)
        .setContentTitle("Downloading SlapOS")
        .setContentText("Download is " + progress[0] + "% done")
        .setTicker("Downloading...")
        .setOngoing(true)
        .setWhen(System.currentTimeMillis())
        .setProgress(100, progress[0], false)
        .setSmallIcon(R.drawable.icon)
        .build();

        DownloadImage.notificationManager.notify(1, DownloadImage.myNotification);

    }
DevWithZachary
  • 3,545
  • 11
  • 49
  • 101
  • What version of android are you currently running this on? – JPM Jun 27 '13 at 14:18
  • Min SDK version is 8, but I have tested on a device running 4.2.1 and a device running 2.3.4 both with the same result (UI lags out until the systemUI crashes and gets restarted) – DevWithZachary Jun 27 '13 at 14:21

2 Answers2

18

so how could I go about changing the background code to call publishProgress only every second

I have done this before for an upload function that showed the % in a Notification, but same exact idea. Have your AsyncTask keep track of what percentDone the download is, and ONLY call publishProgress when percentDone changes. That way, you will only ever call publishProgress when the % downloaded changes, and the Notification therefore needs to update. This should resolve the UI lag.

I was writing this up as my suggested implementation, sounds like the OP already got it working. But maybe this will help someone else in the future:

    byte data[] = new byte[1024];
    long total = 0;
    int count, latestPercentDone;
    int percentDone = -1;
    while ((count = input.read(data)) != -1) {
        total += count;
        latestPercentDone = (int) Math.round(total / fileLength * 100.0);
        if (percentDone != latestPercentDone) {
            percentDone = latestPercentDone;
            publishProgress(percentDone);
        }
        output.write(data, 0, count);
    }
Steven Byle
  • 13,149
  • 4
  • 45
  • 57
  • Fixed lags for me too! – Vahid Amiri Jan 27 '17 at 14:57
  • 1
    Thx, I changed latestPercentDone = (int)(total*100 / fileLength ); and work perfect!!! – cristianego Mar 07 '17 at 22:26
  • Awesome logic. Only problem I face was `latestPercentDone = (int) Math.round(total / fileLength * 100.0);` This was always giving latestPercentDone = 0. So I changed it to `latestPercentDone = (int) ((total * 100) / fileLength);` – AnujDeo Nov 01 '17 at 15:49
  • 1
    @AnujDeo You were getting 0 because `total` and/or `fileLength` must be a `double`. If both `total` and `fileLength` were `int` or `long` you will get 0 because 99/100=0 with non floating point numbers. – Steven Byle Nov 01 '17 at 19:33
1

I really liked your approach to it! I found that changing the code to the following made my progressbar update correctly. I had some issues while using math.round().

latestPercentDone = (int) ((dataBytesWritten / (float) totalSize) * 100);
    if (percentDone != latestPercentDone) {
    percentDone = latestPercentDone;
    publishProgress(percentDone);
    }
bezman
  • 19
  • 1