Zeigen Sie den Download-Fortschritt innerhalb der Aktivität mit DownloadManager an

89

Ich versuche, den gleichen Fortschritt zu reproduzieren, den DownloadManager in der Benachrichtigungsleiste meiner App anzeigt, aber mein Fortschritt wird nie veröffentlicht. Ich versuche es mit runOnUiThread () zu aktualisieren, aber aus irgendeinem Grund wurde es nicht aktualisiert.

Mein Download:

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();

meine statusMessage-Methode:

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);
}

Mein Protokoll funktioniert einwandfrei, während mein Download ausgeführt wird und "Download in Bearbeitung!" und wenn es abgeschlossen ist "Download abgeschlossen!", aber das gleiche passiert nicht bei meinem Fortschritt, warum? Ich brauche wirklich Hilfe, andere Logiken dafür werden sehr geschätzt

Victor Laerte
quelle
Könnte es sein, dass Ihre Datei einfach zu klein ist und der Download abgeschlossen ist, bevor der Fortschritt veröffentlicht wird? Was gibt die Download-Abfrage in Ihrer Aufgabe zurück? Wenn die Aufgabe erst nach einiger Zeit ausgeführt wird, haben Sie möglicherweise eine andere lang laufende Operation in Ihrem Hauptthread.
Paul Lammertsma
Ich habe den Code aktualisiert. Können Sie jetzt einen Blick darauf werfen? Und was die Dateilänge betrifft, die nicht zu klein ist, kann ich den Download-Fortschritt in der Benachrichtigungsleiste sehen
Victor Laerte

Antworten:

62

Sie teilen zwei ganze Zahlen:

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

Da bytes_downloadedist kleiner als bytes_total, (bytes_downloaded / bytes_total)wird 0 sein, und Ihr Fortschritt wird daher immer 0 sein.

Ändern Sie Ihre Berechnung in

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

den Fortschritt in ganzen (wenn auch bodengebundenen) Perzentilen zu erhalten.

Paul Lammertsma
quelle
@AZ_ Danke für deinen Beitrag. Ich würde vorschlagen, dass Sie Ihre eigene Antwort mit der ausgefeilteren Lösung hinzufügen.
Paul Lammertsma
Es ist in Ordnung, ich möchte keine weitere Antwort auf die bereits akzeptierte Antwort geben, da dies für Benutzer schwierig sein wird. Sie können sich dafür entscheiden, meine Bearbeitung nicht zu akzeptieren :)
AZ_
1
Wenn Ihre Aktivität endet und Sie den Download abbrechen möchten, wird ein division by zeroschwerwiegender Fehler angezeigt. Deshalb habe ich es so gemacht final int dl_progress = ( bytes_total > 0 ? (int) ((bytes_downloaded * 100L) / bytes_total) : 0 );
KaHa6uc
17

Pauls Antwort ist richtig, aber bei größeren Downloads werden Sie ziemlich schnell auf max int treffen und einen negativen Fortschritt erzielen. Ich habe dies verwendet, um das Problem zu lösen:

final int dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);
Justin Morris
quelle
Du hast recht; Ich habe meine Antwort geändert, um sicherzustellen, dass andere nicht denselben Fehler machen.
Paul Lammertsma
4

Wie Paul sagte, teilen Sie zwei ganze Zahlen, wobei das Ergebnis immer <1 ist.

Wirf immer deine Zahl vor dem Teilen, die im Gleitkomma berechnet und zurückgegeben wird.

Vergessen Sie nicht, mit DivByZero umzugehen.

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

Falls jemand die Implementierung des Download Progress Retrievers aus der Frage von @Victor Laerte in Kotlin mit RxJava benötigt, klicken Sie hier:

DownloadStateRetriever.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)
}

Ich habe dem Cursor Erweiterungen hinzugefügt, um die Codeklarheit zu verbessern:

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
quelle