Laden Sie eine Datei mit Android herunter und zeigen Sie den Fortschritt in einem ProgressDialog an

1046

Ich versuche eine einfache Anwendung zu schreiben, die aktualisiert wird. Dazu benötige ich eine einfache Funktion, die eine Datei herunterladen und den aktuellen Fortschritt in a anzeigen kannProgressDialog . Ich weiß, wie das ProgressDialoggeht, bin mir aber nicht sicher, wie ich den aktuellen Fortschritt anzeigen und wie ich die Datei überhaupt herunterladen soll.

Tom Leese
quelle
2
Ich hoffe, der folgende Link kann Ihnen helfen ... androidhive.info/2012/04/…
Ganesh Katikar
1
Für eine schnelle Lösung beziehen Sie sich auf diese Antwort
Richa
stackoverflow.com/a/43069239/3879214 Überprüfen Sie diese Antwort
Anu Martin

Antworten:

1873

Es gibt viele Möglichkeiten, Dateien herunterzuladen. Im Folgenden werde ich die häufigsten Wege veröffentlichen. Es liegt an Ihnen, zu entscheiden, welche Methode für Ihre App besser ist.

1. Verwenden Sie AsyncTaskden Download-Fortschritt und zeigen Sie ihn in einem Dialogfeld an

Mit dieser Methode können Sie einige Hintergrundprozesse ausführen und gleichzeitig die Benutzeroberfläche aktualisieren (in diesem Fall aktualisieren wir einen Fortschrittsbalken).

Importe:

import android.os.PowerManager;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;

Dies ist ein Beispielcode:

// declare the dialog as a member field of your activity
ProgressDialog mProgressDialog;

// instantiate it within the onCreate method
mProgressDialog = new ProgressDialog(YourActivity.this);
mProgressDialog.setMessage("A message");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(true);

// execute this when the downloader must be fired
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");

mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {

    @Override
    public void onCancel(DialogInterface dialog) {
        downloadTask.cancel(true); //cancel the task
    }
});

Das AsyncTaskwird so aussehen:

// usually, subclasses of AsyncTask are declared inside the activity class.
// that way, you can easily modify the UI thread from here
private class DownloadTask extends AsyncTask<String, Integer, String> {

    private Context context;
    private PowerManager.WakeLock mWakeLock;

    public DownloadTask(Context context) {
        this.context = context;
    }

    @Override
    protected String doInBackground(String... sUrl) {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(sUrl[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();

            // expect HTTP 200 OK, so we don't mistakenly save error report
            // instead of the file
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return "Server returned HTTP " + connection.getResponseCode()
                        + " " + connection.getResponseMessage();
            }

            // this will be useful to display download percentage
            // might be -1: server did not report the length
            int fileLength = connection.getContentLength();

            // download the file
            input = connection.getInputStream();
            output = new FileOutputStream("/sdcard/file_name.extension");

            byte data[] = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {
                    input.close();
                    return null;
                }
                total += count;
                // publishing the progress....
                if (fileLength > 0) // only if total length is known
                    publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }
        } catch (Exception e) {
            return e.toString();
        } finally {
            try {
                if (output != null)
                    output.close();
                if (input != null)
                    input.close();
            } catch (IOException ignored) {
            }

            if (connection != null)
                connection.disconnect();
        }
        return null;
    }

Die Methode oben ( doInBackground) wird immer in einem Hintergrundthread ausgeführt. Sie sollten dort keine UI-Aufgaben ausführen. Auf der anderen Seite wird das onProgressUpdateund onPreExecuteauf dem UI-Thread ausgeführt, sodass Sie dort den Fortschrittsbalken ändern können:

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // take CPU lock to prevent CPU from going off if the user 
        // presses the power button during download
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
             getClass().getName());
        mWakeLock.acquire();
        mProgressDialog.show();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        // if we get here, length is known, now set indeterminate to false
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.setProgress(progress[0]);
    }

    @Override
    protected void onPostExecute(String result) {
        mWakeLock.release();
        mProgressDialog.dismiss();
        if (result != null)
            Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
        else
            Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
    }

Damit dies ausgeführt werden kann, benötigen Sie die Berechtigung WAKE_LOCK.

<uses-permission android:name="android.permission.WAKE_LOCK" />

2. Vom Service herunterladen

Die große Frage hier ist: Wie aktualisiere ich meine Aktivität von einem Dienst aus? . Im nächsten Beispiel werden wir zwei Klassen verwenden, die Ihnen möglicherweise nicht bekannt sind: ResultReceiverund IntentService. ResultReceiverist derjenige, der es uns ermöglicht, unseren Thread von einem Dienst aus zu aktualisieren; IntentServiceist eine Unterklasse, Servicedie einen Thread erzeugt, um von dort aus Hintergrundarbeiten auszuführen (Sie sollten wissen, dass ein ServiceThread tatsächlich im selben Thread Ihrer App ausgeführt wird; wenn Sie ihn erweitern Service, müssen Sie manuell neue Threads erzeugen, um CPU-Blockierungsvorgänge auszuführen).

Der Download-Service kann folgendermaßen aussehen:

public class DownloadService extends IntentService {
    public static final int UPDATE_PROGRESS = 8344;

    public DownloadService() {
        super("DownloadService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {

        String urlToDownload = intent.getStringExtra("url");
        ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
        try {

            //create url and connect
            URL url = new URL(urlToDownload);
            URLConnection connection = url.openConnection();
            connection.connect();

            // this will be useful so that you can show a typical 0-100% progress bar
            int fileLength = connection.getContentLength();

            // download the file
            InputStream input = new BufferedInputStream(connection.getInputStream());

            String path = "/sdcard/BarcodeScanner-debug.apk" ;
            OutputStream output = new FileOutputStream(path);

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;

                // publishing the progress....
                Bundle resultData = new Bundle();
                resultData.putInt("progress" ,(int) (total * 100 / fileLength));
                receiver.send(UPDATE_PROGRESS, resultData);
                output.write(data, 0, count);
            }

            // close streams 
            output.flush();
            output.close();
            input.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        Bundle resultData = new Bundle();
        resultData.putInt("progress" ,100);

        receiver.send(UPDATE_PROGRESS, resultData);
    }
}

Fügen Sie den Service Ihrem Manifest hinzu:

<service android:name=".DownloadService"/>

Und die Aktivität wird so aussehen:

// initialize the progress dialog like in the first example

// this is how you fire the downloader
mProgressDialog.show();
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url", "url of the file to download");
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
startService(intent);

Hier ist wo ResultReceiverkommt zu spielen:

private class DownloadReceiver extends ResultReceiver{

    public DownloadReceiver(Handler handler) {
        super(handler);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {

        super.onReceiveResult(resultCode, resultData);

        if (resultCode == DownloadService.UPDATE_PROGRESS) {

            int progress = resultData.getInt("progress"); //get the progress
            dialog.setProgress(progress);

            if (progress == 100) {
                dialog.dismiss();
            }
        }
    }
}

2.1 Verwenden Sie die Groundy-Bibliothek

Groundy ist eine Bibliothek, mit der Sie im Wesentlichen Codeteile in einem Hintergrunddienst ausführen können. Sie basiert auf demResultReceiveroben gezeigten Konzept. Diese Bibliothek istderzeit veraltet . Sowürdeder gesamte Code aussehen:

Die Aktivität, in der Sie den Dialog anzeigen ...

public class MainActivity extends Activity {

    private ProgressDialog mProgressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();
                Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();
                Groundy.create(DownloadExample.this, DownloadTask.class)
                        .receiver(mReceiver)
                        .params(extras)
                        .queue();

                mProgressDialog = new ProgressDialog(MainActivity.this);
                mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                mProgressDialog.setCancelable(false);
                mProgressDialog.show();
            }
        });
    }

    private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            switch (resultCode) {
                case Groundy.STATUS_PROGRESS:
                    mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));
                    break;
                case Groundy.STATUS_FINISHED:
                    Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);
                    mProgressDialog.dismiss();
                    break;
                case Groundy.STATUS_ERROR:
                    Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
                    mProgressDialog.dismiss();
                    break;
            }
        }
    };
}

Eine GroundyTaskImplementierung, mit der Groundy die Datei herunterlädt und den Fortschritt anzeigt :

public class DownloadTask extends GroundyTask {    
    public static final String PARAM_URL = "com.groundy.sample.param.url";

    @Override
    protected boolean doInBackground() {
        try {
            String url = getParameters().getString(PARAM_URL);
            File dest = new File(getContext().getFilesDir(), new File(url).getName());
            DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
            return true;
        } catch (Exception pokemon) {
            return false;
        }
    }
}

Und fügen Sie dies einfach dem Manifest hinzu:

<service android:name="com.codeslap.groundy.GroundyService"/>

Einfacher geht es nicht, denke ich. Nehmen Sie einfach das neueste Glas von Github und schon kann es losgehen. Beachten Sie, dass der Hauptzweck von Groundy darin besteht, externe REST-APIs in einem Hintergrunddienst aufzurufen und die Ergebnisse problemlos an die Benutzeroberfläche zu senden. Wenn Sie so etwas in Ihrer App tun, kann dies sehr nützlich sein.

2.2 Verwenden Sie https://github.com/koush/ion

3. Verwenden Sie DownloadManagerclass ( GingerBreadund nur neuere)

GingerBread brachte eine neue Funktion mit, DownloadManagermit der Sie Dateien einfach herunterladen und die harte Arbeit des Umgangs mit Threads, Streams usw. an das System delegieren können.

Schauen wir uns zunächst eine Dienstprogrammmethode an:

/**
 * @param context used to check the device version and DownloadManager information
 * @return true if the download manager is available
 */
public static boolean isDownloadManagerAvailable(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        return true;
    }
    return false;
}

Der Name der Methode erklärt alles. Sobald Sie sicher sind, dass DownloadManageres verfügbar ist, können Sie Folgendes tun:

String url = "url you want to download";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Some descrition");
request.setTitle("Some title");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    request.allowScanningByMediaScanner();
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");

// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

Der Download-Fortschritt wird in der Benachrichtigungsleiste angezeigt.

Abschließende Gedanken

Erste und zweite Methode sind nur die Spitze des Eisbergs. Es gibt viele Dinge, die Sie beachten müssen, wenn Ihre App robust sein soll. Hier ist eine kurze Liste:

  • Sie müssen überprüfen, ob der Benutzer über eine Internetverbindung verfügt
  • Stellen Sie sicher, dass Sie über die richtigen Berechtigungen ( INTERNETund WRITE_EXTERNAL_STORAGE) verfügen . auch ACCESS_NETWORK_STATEwenn Sie die Internetverfügbarkeit überprüfen möchten.
  • Stellen Sie sicher, dass das Verzeichnis, in das Sie Dateien herunterladen möchten, vorhanden ist und über Schreibberechtigungen verfügt.
  • Wenn der Download zu groß ist, möchten Sie möglicherweise eine Möglichkeit implementieren, den Download fortzusetzen, wenn frühere Versuche fehlgeschlagen sind.
  • Benutzer sind dankbar, wenn Sie ihnen erlauben, den Download zu unterbrechen.

Wenn Sie keine detaillierte Kontrolle über den Download-Prozess benötigen, sollten Sie DownloadManager(3) verwenden, da die meisten der oben aufgeführten Elemente bereits verarbeitet werden.

Bedenken Sie aber auch, dass sich Ihre Bedürfnisse ändern können. Zum Beispiel DownloadManager wird keine Antwort zwischengespeichert . Es wird dieselbe große Datei mehrmals blind heruntergeladen. Es gibt keine einfache Möglichkeit, das Problem nachträglich zu beheben. Wenn Sie mit einem Basic HttpURLConnection(1, 2) beginnen, müssen Sie lediglich ein hinzufügen HttpResponseCache. Die anfängliche Anstrengung, die grundlegenden Standardwerkzeuge zu erlernen, kann daher eine gute Investition sein.

Diese Klasse wurde in API-Stufe 26 nicht mehr unterstützt. ProgressDialog ist ein modaler Dialog, der verhindert, dass der Benutzer mit der App interagiert. Anstatt diese Klasse zu verwenden, sollten Sie eine Fortschrittsanzeige wie ProgressBar verwenden, die in die Benutzeroberfläche Ihrer App eingebettet werden kann. Alternativ können Sie eine Benachrichtigung verwenden, um den Benutzer über den Fortschritt der Aufgabe zu informieren. Für weitere Details Link

Cristian
quelle
8
DownloadManager ist Teil des Betriebssystems, dh es ist immer in GB + verfügbar und kann nicht deinstalliert werden.
Cristian
17
In Cristians Antwort liegt ein Problem. Weil der Code unter " 1. Verwenden Sie AsyncTask und zeigen Sie den Download-Fortschritt in einem Dialogfeld an " connection.connect (); dann InputStream input = new BufferedInputStream (url.openStream ()); Code stellt 2 Verbindungen zum Server her. Ich habe es geschafft, dieses Verhalten zu ändern, indem ich den Code wie folgt aktualisiert habe. InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL
99
Ich wünschte, Android-Dokumentation wäre so prägnant.
Lou Morda
12
Vorgeschlagen , um close()die Ströme ( inputund output) in finallystatt try, andernfalls , wenn eine Ausnahme vor geworfen wird close(), haben Sie nicht geschlossene Ströme rumhängen.
Pang
32
sdcard/Verwenden Sie Environment.getExternalStorageDirectory()stattdessen keinen Hardcode .
Nima G
106

Vergessen Sie nicht, Ihrer Manifestdatei Berechtigungen hinzuzufügen, wenn Sie Inhalte aus dem Internet herunterladen möchten!

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloandroid"
    android:versionCode="1"
    android:versionName="1.0">

        <uses-sdk android:minSdkVersion="10" />

        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
        <uses-permission android:name="android.permission.INTERNET"></uses-permission>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
        <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>

        <application 
            android:icon="@drawable/icon" 
            android:label="@string/app_name" 
            android:debuggable="true">

        </application>

</manifest>
Albtraum
quelle
1
Ich bin mir ziemlich sicher, dass Sie READ_PHONE_STATE nicht benötigen und dass Sie WRITE_EXTERNAL_STORAGE definitiv nicht benötigen. Dies ist eine gefährliche Berechtigung, die mit dem Storage Access Framework vermieden werden kann.
Stellen Sie Monica
32

Ja, der obige Code funktioniert. Wenn Sie jedoch Ihr progressbarIn onProgressUpdateof aktualisieren Asynctask und die Zurück-Taste drücken oder Ihre Aktivität beenden, AsyncTaskverliert dies den Überblick über Ihre Benutzeroberfläche. Und wenn Sie zu Ihrer Aktivität zurückkehren, wird dies angezeigt, auch wenn der Download im Hintergrund ausgeführt wird Kein Update in der Fortschrittsanzeige. Also auf OnResume()Versuch einen Thread wie läuft runOnUIThreadmit einer Timer - Aufgabe , dass Updates ur progressbarmit Werten aus der Aktualisierung im AsyncTaskHintergrund laufenden.

private void updateProgressBar(){
    Runnable runnable = new updateProgress();
    background = new Thread(runnable);
    background.start();
}

public class updateProgress implements Runnable {
    public void run() {
        while(Thread.currentThread()==background)
            //while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(1000); 
                Message msg = new Message();
                progress = getProgressPercentage();        
                handler.sendMessage(msg);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (Exception e) {
        }
    }
}

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        progress.setProgress(msg.what);
    }
};

Vergiss nicht, den Thread zu zerstören, wenn deine Aktivität nicht sichtbar ist.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
Blatt
quelle
1
Das ist genau mein Problem. Können Sie mir sagen, wie eine Timer-Aufgabe zum Aktualisieren der Fortschrittsanzeige ausgeführt wird? So erhalten Sie eine Aktualisierung der Werte von der dahinter laufenden
AsyncTask
2
okk nimm eine globale oder statische Variable, um deine Werte von asynctask zu aktualisieren ... oder du kannst sie aus Sicherheitsgründen in DataBase einfügen, damit das Schließen der Anwendung nicht behindert wird. Und wenn du die Aktivität neu startest, wo du ur aktualisieren willst UI führen den UI-Thread aus
..
Die Benutzeroberfläche sollte die neue Referenz haben. Wie die neu initialisierte ProgressBar in meinem Fall
Blatt
@sheetal, aber es funktioniert gut ohne Ihren Code! Warum?! Mein Gerät ist Xperia P mit Android 4.0.4. Ich habe eine statische boolesche Variable definiert, die von onPreExecute auf true und von onPostExecute auf false gesetzt wird. Es zeigt an, ob wir herunterladen oder nicht, sodass wir überprüfen können, ob die Variable gleich true ist. Zeigen Sie den vorherigen Fortschrittsbalken an.
Behzad
@sheetal Ihr Code ist wenig dunkel, können Sie mir einen Rat geben?
iSun
17

Ich würde Ihnen empfehlen, mein Project Netroid zu verwenden . Es basiert auf Volley . Ich habe einige Funktionen hinzugefügt, wie z. B. Rückruf für mehrere Ereignisse und Verwaltung des Dateidownloads. Dies könnte hilfreich sein.

VinceStyling
quelle
2
Unterstützt es das Herunterladen mehrerer Dateien? Ich arbeite an einem Projekt, mit dem Benutzer geplante Downloads durchführen können. Zu einem bestimmten Zeitpunkt (z. B. 12 Uhr) lädt der Dienst alle zuvor vom Benutzer ausgewählten Links herunter. Ich meine, der Dienst, den ich brauche, muss in der Lage sein, Download-Links in die Warteschlange zu stellen und dann alle herunterzuladen (einzeln oder parallel, abhängig vom Benutzer). Vielen Dank
Hoang Trinh
@piavgh ja, alles was du wolltest war befriedigen, du kannst meine Beispiel-Apk auschecken, sie enthielt eine Demo zum Verwalten von Dateidownloads.
VinceStyling
Tolle Bibliothek! Funktioniert gut, obwohl ich Probleme beim Herunterladen von Inhalten von HTTPS-URLs habe, da es nicht möglich ist SSLSocketFactory, ein eigenes Hinzufügen von A festzulegen, HostnameVerifierund ich einen solchen Verifizierer benötige. Hat diesbezüglich ein Problem aufgeworfen.
DarkCygnus
Link für Volley. -> Nicht gefunden
Rumit Patel
14

Ich habe die AsyncTaskKlasse geändert , um die Erstellung progressDialogim selben Kontext zu handhaben. Ich denke, der folgende Code ist wiederverwendbarer. (Es kann von jeder Aktivität aus aufgerufen werden, indem nur der Kontext, die Zieldatei oder die Dialognachricht übergeben werden.)

public static class DownloadTask extends AsyncTask<String, Integer, String> {
    private ProgressDialog mPDialog;
    private Context mContext;
    private PowerManager.WakeLock mWakeLock;
    private File mTargetFile;
    //Constructor parameters :
    // @context (current Activity)
    // @targetFile (File object to write,it will be overwritten if exist)
    // @dialogMessage (message of the ProgresDialog)
    public DownloadTask(Context context,File targetFile,String dialogMessage) {
        this.mContext = context;
        this.mTargetFile = targetFile;
        mPDialog = new ProgressDialog(context);

        mPDialog.setMessage(dialogMessage);
        mPDialog.setIndeterminate(true);
        mPDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mPDialog.setCancelable(true);
        // reference to instance to use inside listener
        final DownloadTask me = this;
        mPDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                me.cancel(true);
            }
        });
        Log.i("DownloadTask","Constructor done");
    }

    @Override
    protected String doInBackground(String... sUrl) {
        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(sUrl[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();

            // expect HTTP 200 OK, so we don't mistakenly save error report
            // instead of the file
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return "Server returned HTTP " + connection.getResponseCode()
                        + " " + connection.getResponseMessage();
            }
            Log.i("DownloadTask","Response " + connection.getResponseCode());

            // this will be useful to display download percentage
            // might be -1: server did not report the length
            int fileLength = connection.getContentLength();

            // download the file
            input = connection.getInputStream();
            output = new FileOutputStream(mTargetFile,false);

            byte data[] = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {
                    Log.i("DownloadTask","Cancelled");
                    input.close();
                    return null;
                }
                total += count;
                // publishing the progress....
                if (fileLength > 0) // only if total length is known
                    publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }
        } catch (Exception e) {
            return e.toString();
        } finally {
            try {
                if (output != null)
                    output.close();
                if (input != null)
                    input.close();
            } catch (IOException ignored) {
            }

            if (connection != null)
                connection.disconnect();
        }
        return null;
    }
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // take CPU lock to prevent CPU from going off if the user
        // presses the power button during download
        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                getClass().getName());
        mWakeLock.acquire();

        mPDialog.show();

    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        // if we get here, length is known, now set indeterminate to false
        mPDialog.setIndeterminate(false);
        mPDialog.setMax(100);
        mPDialog.setProgress(progress[0]);

    }

    @Override
    protected void onPostExecute(String result) {
        Log.i("DownloadTask", "Work Done! PostExecute");
        mWakeLock.release();
        mPDialog.dismiss();
        if (result != null)
            Toast.makeText(mContext,"Download error: "+result, Toast.LENGTH_LONG).show();
        else
            Toast.makeText(mContext,"File Downloaded", Toast.LENGTH_SHORT).show();
    }
}
Der Syrer
quelle
Vergessen Sie nicht, Wake-Lock-Berechtigung hinzuzufügen <Verwendungsberechtigung android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

Vergessen Sie nicht, "/ sdcard ..." durch eine neue Datei ("/ mnt / sdcard / ...") zu ersetzen, da Sie sonst eine FileNotFoundException erhalten

ENSATE
quelle
In Teil 2 Download vom Dienst Der Fortschrittsdialog kann kein Fortschrittsinkrement anzeigen. it direct return 100, also wenn 100 it direct check setprogress 100 und progress over, wie man den Fortschritt erhöht? Es zeigt nur 0 progress an, aber tatsächlich wird der Download ausgeführt
Nirav Mehta
es zeigt nur 0% von 100 nur andere Arbeit richtig
Nirav Mehta
14
TU es nicht! Es gibt Environment.getExternalStorageDirectory().getAbsolutePath()einen Pfad zur SD-Karte. Vergessen Sie auch nicht zu überprüfen, ob der externe Speicher gemountet ist - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa
8

Ich fand diesen Blog-Beitrag sehr hilfreich. Es verwendet loopJ zum Herunterladen von Dateien. Es hat nur eine einfache Funktion und wird einigen neuen Android-Leuten helfen.

Chintan Adatiya
quelle
7

Während ich anfing, Android-Entwicklung zu lernen, hatte ich gelernt, dass dies ProgressDialogder richtige Weg ist. Es gibt eine setProgressMethode, mit der ProgressDialogdie Fortschrittsstufe beim Herunterladen der Datei aktualisiert werden kann.

Das Beste, was ich in vielen Apps gesehen habe, ist, dass sie die Attribute dieses Fortschrittsdialogs anpassen, um dem Fortschrittsdialog ein besseres Erscheinungsbild zu verleihen als der Standardversion. Gut, um den Benutzer mit Animationen wie Frosch, Elefant oder niedlichen Katzen / Welpen zu beschäftigen. Jede Animation im Fortschrittsdialog zieht Benutzer an und sie haben nicht das Gefühl, lange warten zu müssen.

Sandeep
quelle
5

Mein persönlicher Rat ist, den Fortschrittsdialog zu verwenden und vor der Ausführung aufzubauen oder den OnPreExecute()Fortschritt häufig zu veröffentlichen, wenn Sie den horizontalen Fortschrittsbalken des Fortschrittsdialogs verwenden. Der verbleibende Teil ist die Optimierung des Algorithmus von doInBackground.

Raju yourPepe
quelle
5

Verwenden Sie die Android Query-Bibliothek, die in der Tat sehr cool ist. Sie können sie so ändern, ProgressDialogwie Sie es in anderen Beispielen sehen. Diese zeigt die Fortschrittsansicht Ihres Layouts an und blendet sie nach Abschluss aus.

File target = new File(new File(Environment.getExternalStorageDirectory(), "ApplicationName"), "tmp.pdf");
new AQuery(this).progress(R.id.progress_view).download(_competition.qualificationScoreCardsPdf(), target, new AjaxCallback<File>() {
    public void callback(String url, File file, AjaxStatus status) {
        if (file != null) {
            // do something with file  
        } 
    }
});
Renetik
quelle
Die Sache ist, ich habe aufgehört, es zu benutzen, weil es nicht gewartet wird. Nehmen Sie also nicht an, dass dies keine gute Antwort mehr ist.
Renetik
3

Ich füge eine weitere Antwort für eine andere Lösung hinzu, die ich jetzt verwende, weil Android Query so groß und nicht gewartet ist, um gesund zu bleiben. Also bin ich zu diesem https://github.com/amitshekhariitbhu/Fast-Android-Networking gewechselt .

    AndroidNetworking.download(url,dirPath,fileName).build()
      .setDownloadProgressListener(new DownloadProgressListener() {
        public void onProgress(long bytesDownloaded, long totalBytes) {
            bar.setMax((int) totalBytes);
            bar.setProgress((int) bytesDownloaded);
        }
    }).startDownload(new DownloadListener() {
        public void onDownloadComplete() {
            ...
        }

        public void onError(ANError error) {
            ...
        }
    });
Renetik
quelle
2

Berechtigungen

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission 
   android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Verwenden von HttpURLConnection

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class DownloadFileUseHttpURLConnection extends Activity {

ProgressBar pb;
Dialog dialog;
int downloadedSize = 0;
int totalSize = 0;
TextView cur_val;
String dwnload_file_path =  
"http://coderzheaven.com/sample_folder/sample_file.png";
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) findViewById(R.id.b1);
    b.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
             showProgress(dwnload_file_path);

                new Thread(new Runnable() {
                    public void run() {
                         downloadFile();
                    }
                  }).start();
        }
    });
}

void downloadFile(){

    try {
        URL url = new URL(dwnload_file_path);
        HttpURLConnection urlConnection = (HttpURLConnection)   
 url.openConnection();

        urlConnection.setRequestMethod("GET");
        urlConnection.setDoOutput(true);

        //connect
        urlConnection.connect();

        //set the path where we want to save the file           
        File SDCardRoot = Environment.getExternalStorageDirectory(); 
        //create a new file, to save the downloaded file 
        File file = new File(SDCardRoot,"downloaded_file.png");

        FileOutputStream fileOutput = new FileOutputStream(file);

        //Stream used for reading the data from the internet
        InputStream inputStream = urlConnection.getInputStream();

        //this is the total size of the file which we are downloading
        totalSize = urlConnection.getContentLength();

        runOnUiThread(new Runnable() {
            public void run() {
                pb.setMax(totalSize);
            }               
        });

        //create a buffer...
        byte[] buffer = new byte[1024];
        int bufferLength = 0;

        while ( (bufferLength = inputStream.read(buffer)) > 0 ) {
            fileOutput.write(buffer, 0, bufferLength);
            downloadedSize += bufferLength;
            // update the progressbar //
            runOnUiThread(new Runnable() {
                public void run() {
                    pb.setProgress(downloadedSize);
                    float per = ((float)downloadedSize/totalSize) *     
                    100;
                    cur_val.setText("Downloaded " + downloadedSize +  

                    "KB / " + totalSize + "KB (" + (int)per + "%)" );
                }
            });
        }
        //close the output stream when complete //
        fileOutput.close();
        runOnUiThread(new Runnable() {
            public void run() {
                // pb.dismiss(); // if you want close it..
            }
        });         

    } catch (final MalformedURLException e) {
        showError("Error : MalformedURLException " + e);        
        e.printStackTrace();
    } catch (final IOException e) {
        showError("Error : IOException " + e);          
        e.printStackTrace();
    }
    catch (final Exception e) {
        showError("Error : Please check your internet connection " +  
e);
    }       
}

void showError(final String err){
    runOnUiThread(new Runnable() {
        public void run() {
            Toast.makeText(DownloadFileDemo1.this, err,  
      Toast.LENGTH_LONG).show();
        }
    });
}

void showProgress(String file_path){
    dialog = new Dialog(DownloadFileDemo1.this);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(R.layout.myprogressdialog);
    dialog.setTitle("Download Progress");

    TextView text = (TextView) dialog.findViewById(R.id.tv1);
    text.setText("Downloading file from ... " + file_path);
    cur_val = (TextView) dialog.findViewById(R.id.cur_pg_tv);
    cur_val.setText("Starting download...");
    dialog.show();

     pb = (ProgressBar)dialog.findViewById(R.id.progress_bar);
     pb.setProgress(0);
            pb.setProgressDrawable(
      getResources().getDrawable(R.drawable.green_progress));  
  }
}
vishnuc156
quelle
1

Sie können den Fortschritt des Download-Managers mithilfe von LiveData und Coroutinen beobachten ( siehe unten)

https://gist.github.com/FhdAlotaibi/678eb1f4fa94475daf74ac491874fc0e

data class DownloadItem(val bytesDownloadedSoFar: Long = -1, val totalSizeBytes: Long = -1, val status: Int)

class DownloadProgressLiveData(private val application: Application, private val requestId: Long) : LiveData<DownloadItem>(), CoroutineScope {

    private val downloadManager by lazy {
        application.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    }

    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    override fun onActive() {
        super.onActive()
        launch {
            while (isActive) {
                val query = DownloadManager.Query().setFilterById(requestId)
                val cursor = downloadManager.query(query)
                if (cursor.moveToFirst()) {
                    val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
                    Timber.d("Status $status")
                    when (status) {
                        DownloadManager.STATUS_SUCCESSFUL,
                        DownloadManager.STATUS_PENDING,
                        DownloadManager.STATUS_FAILED,
                        DownloadManager.STATUS_PAUSED -> postValue(DownloadItem(status = status))
                        else -> {
                            val bytesDownloadedSoFar = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
                            val totalSizeBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
                            postValue(DownloadItem(bytesDownloadedSoFar.toLong(), totalSizeBytes.toLong(), status))
                        }
                    }
                    if (status == DownloadManager.STATUS_SUCCESSFUL || status == DownloadManager.STATUS_FAILED)
                        cancel()
                } else {
                    postValue(DownloadItem(status = DownloadManager.STATUS_FAILED))
                    cancel()
                }
                cursor.close()
                delay(300)
            }
        }
    }

    override fun onInactive() {
        super.onInactive()
        job.cancel()
    }

}
Fahad Alotaibi
quelle
Können Sie ein Anwendungsbeispiel für diese Klasse angeben?
Karim Karimov
0

Wir können die Coroutine und den Arbeitsmanager zum Herunterladen von Dateien in Kotlin verwenden.

Fügen Sie eine Abhängigkeit in build.gradle hinzu

    implementation "androidx.work:work-runtime-ktx:2.3.0-beta01"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"

WorkManager-Klasse

    import android.content.Context
    import android.os.Environment
    import androidx.work.CoroutineWorker
    import androidx.work.WorkerParameters
    import androidx.work.workDataOf
    import com.sa.chat.utils.Const.BASE_URL_IMAGE
    import com.sa.chat.utils.Constants
    import kotlinx.coroutines.delay
    import java.io.BufferedInputStream
    import java.io.File
    import java.io.FileOutputStream
    import java.net.URL

    class DownloadMediaWorkManager(appContext: Context, workerParams: WorkerParameters)
        : CoroutineWorker(appContext, workerParams) {

        companion object {
            const val WORK_TYPE = "WORK_TYPE"
            const val WORK_IN_PROGRESS = "WORK_IN_PROGRESS"
            const val WORK_PROGRESS_VALUE = "WORK_PROGRESS_VALUE"
        }

        override suspend fun doWork(): Result {

            val imageUrl = inputData.getString(Constants.WORK_DATA_MEDIA_URL)
            val imagePath = downloadMediaFromURL(imageUrl)

            return if (!imagePath.isNullOrEmpty()) {
                Result.success(workDataOf(Constants.WORK_DATA_MEDIA_URL to imagePath))
            } else {
                Result.failure()
            }
        }

        private suspend fun downloadMediaFromURL(imageUrl: String?): String? {

            val file = File(
                    getRootFile().path,
                    "IMG_${System.currentTimeMillis()}.jpeg"
            )

            val url = URL(BASE_URL_IMAGE + imageUrl)
            val connection = url.openConnection()
            connection.connect()

            val lengthOfFile = connection.contentLength
            // download the file
            val input = BufferedInputStream(url.openStream(), 8192)
            // Output stream
            val output = FileOutputStream(file)

            val data = ByteArray(1024)
            var total: Long = 0
            var last = 0

            while (true) {

                val count = input.read(data)
                if (count == -1) break
                total += count.toLong()

                val progress = (total * 100 / lengthOfFile).toInt()

                if (progress % 10 == 0) {
                    if (last != progress) {
                        setProgress(workDataOf(WORK_TYPE to WORK_IN_PROGRESS,
                                WORK_PROGRESS_VALUE to progress))
                    }
                    last = progress
                    delay(50)
                }
                output.write(data, 0, count)
            }

            output.flush()
            output.close()
            input.close()

            return file.path

        }

        private fun getRootFile(): File {

            val rootDir = File(Environment.getExternalStorageDirectory().absolutePath + "/AppName")

            if (!rootDir.exists()) {
                rootDir.mkdir()
            }

            val dir = File("$rootDir/${Constants.IMAGE_FOLDER}/")

            if (!dir.exists()) {
                dir.mkdir()
            }
            return File(dir.absolutePath)
        }
    }

Starten Sie den Download über den Arbeitsmanager in der Aktivitätsklasse

 private fun downloadImage(imagePath: String?, id: String) {

            val data = workDataOf(WORK_DATA_MEDIA_URL to imagePath)
            val downloadImageWorkManager = OneTimeWorkRequestBuilder<DownloadMediaWorkManager>()
                    .setInputData(data)
                    .addTag(id)
                    .build()

            WorkManager.getInstance(this).enqueue(downloadImageWorkManager)

            WorkManager.getInstance(this).getWorkInfoByIdLiveData(downloadImageWorkManager.id)
                    .observe(this, Observer { workInfo ->

                        if (workInfo != null) {
                            when {
                                workInfo.state == WorkInfo.State.SUCCEEDED -> {
                                    progressBar?.visibility = View.GONE
                                    ivDownload?.visibility = View.GONE
                                }
                                workInfo.state == WorkInfo.State.FAILED || workInfo.state == WorkInfo.State.CANCELLED || workInfo.state == WorkInfo.State.BLOCKED -> {
                                    progressBar?.visibility = View.GONE
                                    ivDownload?.visibility = View.VISIBLE
                                }
                                else -> {
                                    if(workInfo.progress.getString(WORK_TYPE) == WORK_IN_PROGRESS){
                                        val progress = workInfo.progress.getInt(WORK_PROGRESS_VALUE, 0)
                                        progressBar?.visibility = View.VISIBLE
                                        progressBar?.progress = progress
                                        ivDownload?.visibility = View.GONE

                                    }
                                }
                            }
                        }
                    })

        }
Kinjal
quelle