From fc9459d6c5b48423a726e37be5caf93e3a75bae8 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 5 Apr 2016 11:08:11 +0200 Subject: [PATCH] send Downloader progress on a 100 ms timer No need to flood receivers with progress events since they are basically always going to the UI, and the UI will only refresh every so often. If the refresh rate is 50Hz, then that's every 20ms. 100ms seems to make a smooth enough progress bar, and saves some CPU time. This becomes more important if there are multiple downloads happening in the background, like if we make DownloaderService support parallel downloads from different repos --- .../org/fdroid/fdroid/net/Downloader.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java index 5dfc80f6e..8be443376 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java @@ -10,6 +10,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.Timer; +import java.util.TimerTask; public abstract class Downloader { @@ -25,6 +27,9 @@ public abstract class Downloader { public static final String EXTRA_TOTAL_BYTES = "org.fdroid.fdroid.net.Downloader.extra.TOTAL_BYTES"; private volatile boolean cancelled = false; + private volatile int bytesRead; + private volatile int totalBytes; + private Timer timer; private final OutputStream outputStream; @@ -42,6 +47,9 @@ public abstract class Downloader { void sendProgress(URL sourceUrl, int bytesRead, int totalBytes); } + /** + * For sending download progress, should only be called in {@link #progressTask} + */ private DownloaderProgressListener downloaderProgressListener; protected abstract InputStream getDownloadersInputStream() throws IOException; @@ -123,6 +131,9 @@ public abstract class Downloader { private void throwExceptionIfInterrupted() throws InterruptedException { if (cancelled) { Utils.debugLog(TAG, "Received interrupt, cancelling download"); + if (timer != null) { + timer.cancel(); + } throw new InterruptedException(); } } @@ -140,16 +151,17 @@ public abstract class Downloader { * progress counter. */ private void copyInputToOutputStream(InputStream input, int bufferSize) throws IOException, InterruptedException { - - int bytesRead = 0; - int totalBytes = totalDownloadSize(); + bytesRead = 0; + totalBytes = totalDownloadSize(); byte[] buffer = new byte[bufferSize]; + timer = new Timer(); + timer.scheduleAtFixedRate(progressTask, 0, 100); + // Getting the total download size could potentially take time, depending on how // it is implemented, so we may as well check this before we proceed. throwExceptionIfInterrupted(); - sendProgress(bytesRead, totalBytes); while (true) { int count; @@ -168,19 +180,25 @@ public abstract class Downloader { } bytesRead += count; - sendProgress(bytesRead, totalBytes); outputStream.write(buffer, 0, count); - } + timer.cancel(); + timer.purge(); outputStream.flush(); outputStream.close(); } - private void sendProgress(int bytesRead, int totalBytes) { - if (downloaderProgressListener != null) { - downloaderProgressListener.sendProgress(sourceUrl, bytesRead, totalBytes); + /** + * Send progress updates on a timer to avoid flooding receivers with pointless events. + */ + private final TimerTask progressTask = new TimerTask() { + @Override + public void run() { + if (downloaderProgressListener != null) { + downloaderProgressListener.sendProgress(sourceUrl, bytesRead, totalBytes); + } } - } + }; /** * Overrides every method in {@link InputStream} and delegates to the wrapped stream.