Android DownloadManager Progress
Asked Answered
W

2

43

I'm developing an app where users are able to download different content packages. For the download process, I'm using the DownloadManager class. That's working fine so far.

How can I get the current progress of a running download which was started with the DownloadManager. I know that there is the builtin Download Notification and so on, but for me it is necessary that I get the progress of the running download so I can use it to show the progress in a custom progress bar in my app.

Is it even possible or am I just blind and can't find the solution.

Whiny answered 19/10, 2011 at 16:40 Comment(0)
H
41

I am looking for a better way of doing this also, but so far I am planning to just poll for progress every 1sec or so.

DownloadManager mgr = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
long id = mgr.enqueue(request);

DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(id);
Cursor cursor = mgr.query(q);
cursor.moveToFirst();
int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
cursor.close();

Edit:

A FileObserver can help with this. This is the skeleton of one I have put together to help keep track of which files our app has downloaded. Start it in an activity or service's onStart and stop it in onStop. Combined with a manual synchronization of the state of things during onStart, this can give you a pretty complete picture of what's going on.

For progress in particular, watching for the OPEN/CLOSE_WRITE events can help you decide when to start/stop polling the DownloadManager for updates.

public class DownloadsObserver extends FileObserver {

    public static final String LOG_TAG = DownloadsObserver.class.getSimpleName();

    private static final int flags =
            FileObserver.CLOSE_WRITE
            | FileObserver.OPEN
            | FileObserver.MODIFY
            | FileObserver.DELETE
            | FileObserver.MOVED_FROM;
    // Received three of these after the delete event while deleting a video through a separate file manager app:
    // 01-16 15:52:27.627: D/APP(4316): DownloadsObserver: onEvent(1073741856, null)

    public DownloadsObserver(String path) {
        super(path, flags);
    }

    @Override
    public void onEvent(int event, String path) {
        Log.d(LOG_TAG, "onEvent(" + event + ", " + path + ")");

        if (path == null) {
            return;
        }

        switch (event) {
        case FileObserver.CLOSE_WRITE:
            // Download complete, or paused when wifi is disconnected. Possibly reported more than once in a row.
            // Useful for noticing when a download has been paused. For completions, register a receiver for 
            // DownloadManager.ACTION_DOWNLOAD_COMPLETE.
            break;
        case FileObserver.OPEN:
            // Called for both read and write modes.
            // Useful for noticing a download has been started or resumed.
            break;
        case FileObserver.DELETE:
        case FileObserver.MOVED_FROM:
            // These might come in handy for obvious reasons.
            break;
        case FileObserver.MODIFY:
            // Called very frequently while a download is ongoing (~1 per ms).
            // This could be used to trigger a progress update, but that should probably be done less often than this.
            break;
        }
    }
}

Usage would be something like this:

public class MyActivity extends Activity {

    private FileObserver fileObserver = new DownloadsObserver(
            getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());

    @Override
    protected void onStart() {
        super.onStart();
        fileObserver.startWatching();
        syncUpDatabaseWithFileSystem();
    }

    @Override
    protected void onStop() {
        fileObserver.stopWatching();
        super.onStop();
    }
}
Haviland answered 3/11, 2011 at 21:41 Comment(4)
how shall we get the value of the download done in the case of MODIFY? how to know the percentage of downloaded part?Plattdeutsch
Works great for me! Bear in mind, however, that FileObserver#onEvent() runs on a separate thread. For any UI operations (such as updating a progress bar), you need to do something like new Handler(Looper.getMainLooper()).post(new Runnable() {...}); and place your code in the run() method of the Runnable.Glenoid
Amendment: works great on KitKat but not on Marshmallow. According to #32354989, this appears to be a bug in Marshmallow...Glenoid
Works on android 7 for sure.Uboat
G
3

It turns out that the FileObserver implementation on Marshmallow has a bug. As a result, FileObserver will not report any modifications of a file downloaded by the download manager. (Older Android versions do not have this issue – it works fine on KitKat.) Source

For me, the following code (based on this answer) works well. I poll every second – I've tried halving that interval, but without any visible effect.

private static final int PROGRESS_DELAY = 1000;
Handler handler = new Handler();
private boolean isProgressCheckerRunning = false;

// when the first download starts
startProgressChecker();

// when the last download finishes or the Activity is destroyed
stopProgressChecker();

/**
 * Checks download progress.
 */
private void checkProgress() {
    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterByStatus(~(DownloadManager.STATUS_FAILED | DownloadManager.STATUS_SUCCESSFUL));
    Cursor cursor = downloadManager.query(query);
    if (!cursor.moveToFirst()) {
        cursor.close();
        return;
    }
    do {
        long reference = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
        long progress = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
        // do whatever you need with the progress
    } while (cursor.moveToNext());
    cursor.close();
}

/**
 * Starts watching download progress.
 * 
 * This method is safe to call multiple times. Starting an already running progress checker is a no-op.
 */
private void startProgressChecker() {
    if (!isProgressCheckerRunning) {
        progressChecker.run();
    isProgressCheckerRunning = true;
    }
}

/**
 * Stops watching download progress.
 */
private void stopProgressChecker() {
    handler.removeCallbacks(progressChecker);
    isProgressCheckerRunning = false;
}

/**
 * Checks download progress and updates status, then re-schedules itself.
 */
private Runnable progressChecker = new Runnable() {
    @Override
    public void run() {
        try {
            checkProgress();
            // manager reference not found. Commenting the code for compilation
            //manager.refresh();
        } finally {
            handler.postDelayed(progressChecker, PROGRESS_DELAY);
        }
    }
};
Glenoid answered 14/6, 2016 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.