Mostrar el progreso de descarga dentro de la actividad usando DownloadManager

89

Estoy tratando de reproducir el mismo progreso que muestra DownloadManager en la barra de notificaciones dentro de mi aplicación, pero mi progreso nunca se publica. Estoy tratando de actualizarlo usando runOnUiThread (), pero por alguna razón no se ha actualizado.

mi descarga:

String urlDownload = "https://dl.dropbox.com/s/ex4clsfmiu142dy/test.zip?token_hash=AAGD-XcBL8C3flflkmxjbzdr7_2W_i6CZ_3rM5zQpUCYaw&dl=1";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(urlDownload));

request.setDescription("Testando");
request.setTitle("Download");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "teste.zip");

final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

final long downloadId = manager.enqueue(request);

final ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);

new Thread(new Runnable() {

    @Override
    public void run() {

        boolean downloading = true;

        while (downloading) {

            DownloadManager.Query q = new DownloadManager.Query();
            q.setFilterById(downloadId);

            Cursor cursor = manager.query(q);
            cursor.moveToFirst();
            int bytes_downloaded = cursor.getInt(cursor
                    .getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
            int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

            if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                downloading = false;
            }

            final double dl_progress = (bytes_downloaded / bytes_total) * 100;

            runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    mProgressBar.setProgress((int) dl_progress);

                }
            });

            Log.d(Constants.MAIN_VIEW_ACTIVITY, statusMessage(cursor));
            cursor.close();
        }

    }
}).start();

mi método statusMessage:

private String statusMessage(Cursor c) {
    String msg = "???";

    switch (c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
    case DownloadManager.STATUS_FAILED:
        msg = "Download failed!";
        break;

    case DownloadManager.STATUS_PAUSED:
        msg = "Download paused!";
        break;

    case DownloadManager.STATUS_PENDING:
        msg = "Download pending!";
        break;

    case DownloadManager.STATUS_RUNNING:
        msg = "Download in progress!";
        break;

    case DownloadManager.STATUS_SUCCESSFUL:
        msg = "Download complete!";
        break;

    default:
        msg = "Download is nowhere in sight";
        break;
    }

    return (msg);
}

Mi registro funciona perfectamente, mientras mi descarga se está ejecutando dice "¡Descarga en curso!" y cuando se completa "¡Descarga completa!", pero no ocurre lo mismo con mi progreso, ¿por qué? Realmente necesito ayuda, otras lógicas para hacer que son muy apreciadas

Victor Laerte
fuente
¿Podría ser que su archivo es demasiado pequeño y que la descarga se completa antes de que se publique el progreso? ¿Qué devuelve la consulta de descarga en su tarea? Si la tarea solo se ejecuta después de un período de tiempo, es posible que tenga alguna otra operación de ejecución prolongada en su hilo principal.
Paul Lammertsma
Actualicé el código, ¿puedes echar un vistazo ahora? Y sobre la longitud del archivo no es demasiado pequeño, puedo ver el progreso de la descarga en la barra de notificaciones
Victor Laerte

Respuestas:

62

Estás dividiendo dos números enteros:

final double dl_progress = (bytes_downloaded / bytes_total) * 100;

Como bytes_downloadedes menor que bytes_total, (bytes_downloaded / bytes_total)será 0 y, por lo tanto, su progreso siempre será 0.

Cambie su cálculo a

final int dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);

para obtener el progreso en percentiles completos (aunque en el suelo).

Paul Lammertsma
fuente
@AZ_ Gracias por tu contribución. Le sugiero que agregue su propia respuesta con la solución más elaborada.
Paul Lammertsma
Está bien, no quiero poner otra respuesta de respuesta ya aceptada porque será difícil para los usuarios. Puede optar por no aceptar mi edición :)
AZ_
1
Si su actividad finaliza y desea cancelar la descarga, obtendrá un division by zeroerror fatal. Es por eso que lo hice como final int dl_progress = ( bytes_total > 0 ? (int) ((bytes_downloaded * 100L) / bytes_total) : 0 );
KaHa6uc
17

La respuesta de Paul es correcta, pero con descargas más grandes llegará a max int bastante rápido y comenzará a obtener un progreso negativo. Usé esto para resolver el problema:

final int dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);
Justin Morris
fuente
Tienes razón; Modifiqué mi respuesta para asegurarme de que otros no cometan el mismo error.
Paul Lammertsma
4

Como dijo Paul, estás dividiendo dos números enteros, con el resultado siempre <1.

Siempre lanza tu número antes de dividir, que calcula y devuelve en punto flotante.

No olvide manejar DivByZero.

final int dl_progress = (int) ((double)bytes_downloaded / (double)bytes_total * 100f);
Dennis C
fuente
4

En caso de que alguien necesite implementar el recuperador de progreso de descarga de la pregunta de @Victor Laerte en Kotlin con RxJava, aquí tienes:

DescargarStateRetriever.kt

class DownloadStateRetriever(private val downloadManager: DownloadManager) {

    fun retrieve(id: Long) {
        var downloading = AtomicBoolean(true)

        val disposable = Observable.fromCallable {
            val query = DownloadManager.Query().setFilterById(id)
            val cursor = downloadManager.query(query)

            cursor.moveToFirst()

            val bytesDownloaded = cursor.intValue(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
            val bytesTotal = cursor.intValue(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)

            if (isSuccessful(cursor)) downloading.set(false)
            cursor.close()

            if (bytesTotal == 0) 0.toInt() else ((bytesDownloaded * 100F) / bytesTotal).toInt()
        }
                .subscribeOn(Schedulers.newThread())
                .delay(1, TimeUnit.SECONDS)
                .repeatUntil { !downloading.get() }
                .subscribe {
                    Timber.i("Subscribed to $id. progress: $it")
                }
    }

    private fun isSuccessful(cursor: Cursor) = status(cursor) == DownloadManager.STATUS_SUCCESSFUL

    private fun status(cursor: Cursor) = cursor.intValue(DownloadManager.COLUMN_STATUS)
}

He agregado extensiones al cursor para mayor claridad del código:

CursorExtensions.kt

import android.database.Cursor

fun Cursor.column(which: String) = this.getColumnIndex(which)
fun Cursor.intValue(which: String): Int = this.getInt(column(which))
fun Cursor.floatValue(which: String): Float = this.getFloat(column(which))
fun Cursor.stringValue(which: String): String = this.getString(column(which))
fun Cursor.doubleValue(which: String): Double = this.getDouble(column(which))
rahimli
fuente