Android DownloadManager Progress

后端 未结 2 1287
余生分开走
余生分开走 2020-12-02 09:38

i\'m developing an app where users are able to download different content packages. For the download process i am using the DownloadManager class. Thats working fine so far.

相关标签:
2条回答
  • 2020-12-02 09:57

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:59

    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);
            }
        }
    };
    
    0 讨论(0)
提交回复
热议问题