Scarica un file con Android e mostra i progressi in un ProgressDialog


1046

Sto cercando di scrivere una semplice applicazione che viene aggiornata. Per questo ho bisogno di una semplice funzione che può scaricare un file e mostrare i progressi attuali in a ProgressDialog. So come fare ProgressDialog, ma non sono sicuro di come visualizzare i progressi correnti e come scaricare il file in primo luogo.


2
Spero che il link qui sotto possa aiutarti ... androidhive.info/2012/04/…
Ganesh Katikar,

1
Per una rapida soluzione, fai riferimento a questa risposta
Richa,

Risposte:


1873

Esistono molti modi per scaricare i file. Di seguito posterò i modi più comuni; sta a te decidere quale metodo è meglio per la tua app.

1. Utilizzare AsyncTaske mostrare l'avanzamento del download in una finestra di dialogo

Questo metodo ti permetterà di eseguire alcuni processi in background e aggiornare contemporaneamente l'interfaccia utente (in questo caso, aggiorneremo una barra di avanzamento).

importazioni:

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

Questo è un codice di esempio:

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

Il AsyncTasksarà simile a questa:

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

Il metodo above ( doInBackground) viene sempre eseguito su un thread in background. Non dovresti svolgere alcuna attività UI lì. D'altra parte, il onProgressUpdateed onPreExecuteeseguire sul thread dell'interfaccia utente, quindi lì è possibile modificare la barra di avanzamento:

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

Affinché ciò avvenga, è necessaria l'autorizzazione WAKE_LOCK.

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

2. Scarica dal servizio

La grande domanda qui è: come posso aggiornare la mia attività da un servizio? . Nel prossimo esempio useremo due classi di cui potresti non essere a conoscenza: ResultReceivere IntentService. ResultReceiverè quello che ci permetterà di aggiornare il nostro thread da un servizio; IntentServiceè una sottoclasse di Servicecui genera un thread per eseguire il lavoro in background da lì (dovresti sapere che Serviceviene effettivamente eseguito nello stesso thread della tua app; quando si estende Service, devi generare manualmente nuovi thread per eseguire operazioni di blocco della CPU).

Il servizio di download può essere simile al seguente:

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

Aggiungi il servizio al tuo manifest:

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

E l'attività sarà simile a questa:

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

Ecco dove ResultReceiverarriva il gioco:

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 Utilizzare la libreria Groundy

Groundy è una libreria che sostanzialmente ti aiuta a eseguire pezzi di codice in un servizio in background e si basa sulResultReceiverconcetto mostrato sopra. Questa libreria è obsoleta al momento. Ecco comeapparirebbel' intero codice:

L'attività in cui stai mostrando la finestra di dialogo ...

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

GroundyTaskUn'implementazione usata da Groundy per scaricare il file e mostrare lo stato di avanzamento:

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

E aggiungi questo al manifest:

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

Non potrebbe essere più facile, credo. Basta prendere l'ultimo barattolo di Github e sei pronto per partire. Tieni presente che lo scopo principale di Groundy è quello di effettuare chiamate alle API REST esterne in un servizio in background e pubblicare facilmente i risultati nell'interfaccia utente. Se stai facendo qualcosa del genere nella tua app, potrebbe essere davvero utile.

2.2 Utilizzare https://github.com/koush/ion

3. Usa DownloadManagerclasse ( GingerBreade solo più recenti)

GingerBread ha introdotto una nuova funzionalità, DownloadManagerche consente di scaricare facilmente i file e delegare il duro lavoro di gestione di thread, flussi, ecc. Al sistema.

Innanzitutto, vediamo un metodo di utilità:

/**
 * @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;
}

Il nome del metodo spiega tutto. Una volta che sei sicuro che DownloadManagersia disponibile, puoi fare qualcosa del genere:

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

L'avanzamento del download verrà visualizzato nella barra delle notifiche.

Pensieri finali

Il primo e il secondo metodo sono solo la punta dell'iceberg. Ci sono molte cose che devi tenere a mente se vuoi che la tua app sia solida. Ecco un breve elenco:

  • È necessario verificare se l'utente ha una connessione Internet disponibile
  • Assicurati di avere le autorizzazioni giuste ( INTERNETe WRITE_EXTERNAL_STORAGE); anche ACCESS_NETWORK_STATEse si desidera verificare la disponibilità di Internet.
  • Assicurati che la directory in cui stai per scaricare i file esista e abbia i permessi di scrittura.
  • Se il download è troppo grande, potresti voler implementare un modo per riprendere il download se tentativi precedenti non sono riusciti.
  • Gli utenti saranno grati se permetti loro di interrompere il download.

A meno che non sia necessario il controllo dettagliato del processo di download, prendere in considerazione l'utilizzo di DownloadManager(3) perché gestisce già la maggior parte degli elementi sopra elencati.

Ma considera anche che le tue esigenze possono cambiare. Ad esempio, DownloadManager nessuna cache di risposta . Scarica ciecamente lo stesso file di grandi dimensioni più volte. Non c'è modo semplice per risolverlo dopo il fatto. Dove se inizi con un basic HttpURLConnection(1, 2), allora tutto ciò che ti serve è aggiungere un HttpResponseCache. Quindi lo sforzo iniziale di apprendere gli strumenti standard di base può essere un buon investimento.

Questa classe è stata deprecata a livello di API 26. ProgressDialog è una finestra di dialogo modale che impedisce all'utente di interagire con l'app. Invece di utilizzare questa classe, è necessario utilizzare un indicatore di avanzamento come ProgressBar, che può essere incorporato nell'interfaccia utente dell'app. In alternativa, è possibile utilizzare una notifica per informare l'utente sullo stato di avanzamento dell'attività. Per maggiori dettagli Link


8
DownloadManager fa parte del sistema operativo, il che significa che sarà sempre disponibile in GB + e non può essere disinstallato.
Cristian,

17
C'è un problema nella risposta di Cristian. Perché il codice in " 1. Usa AsyncTask e mostra l'avanzamento del download in una finestra di dialogo " fa connection.connect (); quindi InputStream input = new BufferedInputStream (url.openStream ()); il codice effettua 2 connessioni al server. Sono riuscito a cambiare questo comportamento aggiornando il codice come segue InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL

99
Vorrei che la documentazione Android fosse così concisa.
Lou Morda,

12
Suggerito ai close()flussi ( inpute output) finallyinvece di try, altrimenti, se in precedenza viene generata un'eccezione close(), sono presenti flussi non chiusi in giro.
Fatta il

32
Non codificare / sdcard/utilizzare Environment.getExternalStorageDirectory()invece.
Nima G,

106

Non dimenticare di aggiungere permessi al tuo file manifest se stai scaricando materiale da Internet!

<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>

1
Sono abbastanza sicuro che non hai bisogno di READ_PHONE_STATE e sicuramente non hai bisogno di WRITE_EXTERNAL_STORAGE; è un'autorizzazione pericolosa che può essere evitata utilizzando Storage Access Framework.
Ripristina Monica

32

Sì, il codice sopra funzionerà. Ma se stai aggiornando progressbarin onProgressUpdatedi Asynctask e premi il pulsante indietro o finisci la tua attività AsyncTaskperde la sua traccia con l'interfaccia utente. E quando torni alla tua attività, anche se il download è in esecuzione in background vedrai nessun aggiornamento sulla barra di avanzamento. Quindi, OnResume()prova a eseguire un thread come runOnUIThreadcon un'attività timer che aggiorna ur progressbarcon i valori che si aggiornano dallo AsyncTasksfondo in esecuzione.

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

Non dimenticare di distruggere il thread quando la tua attività non è visibile.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}

1
Questo è esattamente il mio problema. Puoi dirmi come eseguire un'attività timer per aggiornare la barra di avanzamento? Come ottenere l'aggiornamento dei valori dall'AsyncTask in esecuzione dietro
user1417127

2
okk porta una variabile globale o statica su dove aggiornare i tuoi valori da asynctask ... oppure puoi inserirla in DataBase per sicurezza ... quindi l'applicazione di chiusura non ostacolerà ... E quando riavvii l'attività in cui desideri aggiornare il tuo L'interfaccia utente esegue il thread dell'interfaccia utente ... vedi esempio di seguito
foglio

L'interfaccia utente dovrebbe avere un nuovo riferimento ... Come nel caso del ProgressBar appena inizializzato
foglio

@sheetal, ma funziona benissimo senza il tuo codice! Perché?! Il mio dispositivo è Xperia P con Android 4.0.4. Ho definito una variabile booleana statica che onPreExecute la imposta su true e onPostExecute la imposta su false. Mostra che stiamo scaricando o meno, quindi possiamo verificare se la variabile è uguale a true, mostra la finestra di dialogo della barra di avanzamento precedente.
Behzad,

@sheetal il tuo codice è poco oscuro, puoi dirmi qualche consiglio?
iSun

17

Ti consiglierei di usare il mio progetto Netroid , basato su Volley . Ho aggiunto alcune funzionalità come la richiamata multi-eventi, la gestione del download dei file. Questo potrebbe essere di qualche aiuto.


2
Supporta il download di più file? Sto lavorando a un progetto che consente all'utente di scaricare pianificato. In un momento specifico (ad esempio 12 AM), il servizio scaricherà tutti i collegamenti selezionati dall'utente in precedenza. Intendo che il servizio di cui ho bisogno deve essere in grado di mettere in coda i collegamenti per il download e quindi scaricarli tutti (uno per uno o in parallelo, dipende dall'utente). Grazie
Hoang Trinh,

@piavgh sì, tutto ciò che volevi era soddisfare, puoi controllare il mio apk di esempio, includeva una demo di gestione del download dei file.
VinceStyling,

Ottima biblioteca! Funziona bene, anche se sto riscontrando problemi durante il download di contenuti da HTTPS Url, come se si possa impostare il proprio SSLSocketFactoryaggiungendo un HostnameVerifiernon è possibile e ho bisogno di tale verificatore. Ha sollevato un problema al riguardo.
DarkCygnus

link per Volley. -> Non trovato
Rumit Patel

14

Ho modificato la AsyncTaskclasse per gestire la creazione progressDialognello stesso contesto. Penso che il seguente codice sarà più riutilizzabile. (può essere chiamato da qualsiasi attività, basta passare contesto, file di destinazione, messaggio di dialogo)

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

Non dimenticare di aggiungere l'autorizzazione Blocco attivazione <usi-autorizzazione android: nome = "android.permission.WAKE_LOCK" />
Hitesh Sahu

8

Non dimenticare di sostituire "/ sdcard ..." con un nuovo file ("/ mnt / sdcard / ...") altrimenti otterrai un FileNotFoundException


Nella parte 2 La finestra di dialogo Download dal servizio non può visualizzare l'incremento di avanzamento. restituisce direttamente 100, quindi se 100 controlla direttamente setprogress 100 e progredisce, come incrementare progress ?? mostra solo 0 progress ma effettivamente scarica in esecuzione
Nirav Mehta

mostra solo lo 0% su 100 solo altri funzionano correttamente
Nirav Mehta

14
Non farlo! C'è Environment.getExternalStorageDirectory().getAbsolutePath()per ottenere un percorso per sdcard. Inoltre, non dimenticare di verificare se è installata la memoria esterna - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa

8

Ho trovato questo post sul blog molto utile, sta usando loopJ per scaricare il file, ha solo una funzione semplice, sarà utile per alcuni nuovi ragazzi Android.


7

Mentre stavo iniziando a imparare lo sviluppo Android, avevo imparato che ProgressDialogè la strada da percorrere. Esiste il setProgressmetodo ProgressDialogche può essere invocato per aggiornare il livello di avanzamento mentre il file viene scaricato.

La cosa migliore che ho visto in molte app è che personalizzano gli attributi di questa finestra di dialogo di avanzamento per dare un aspetto migliore alla finestra di dialogo di avanzamento rispetto alla versione di serie. Buono a mantenere l'utente impegnato con alcune animazioni come rana, elefante o simpatici gatti / cuccioli. Qualsiasi animazione con nella finestra di dialogo di avanzamento attira gli utenti e non si sentono come essere attesi a lungo.



5

Il mio consiglio personale è quello di utilizzare la finestra di dialogo Progressi e compilare prima dell'esecuzione, o iniziare a OnPreExecute(), pubblicare i progressi spesso se si utilizza lo stile orizzontale della barra di avanzamento della finestra di dialogo dei progressi. La parte rimanente è di ottimizzare l'algoritmo di doInBackground.


5

Usa la libreria di query Android, davvero molto bella. Puoi cambiarla per usarla ProgressDialogcome vedi in altri esempi, questa mostrerà la vista dello stato dal tuo layout e la nasconderà dopo il completamento.

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

Il fatto è che ho smesso di usarlo, perché non è mantenuto, quindi non supponiamo che questa sia una buona risposta.
Renetik,

3

Sto aggiungendo un'altra risposta per un'altra soluzione che sto usando ora perché Android Query è così grande e non mantenuta per rimanere in salute. Quindi sono passato a questo https://github.com/amitshekhariitbhu/Fast-Android-Networking .

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

2

permessi

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

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

1

È possibile osservare l'avanzamento del download manager utilizzando LiveData e coroutine, vedere l'essenziale di seguito

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

}

Potete fornire un esempio usecase per questa classe?
Karim Karimov,


0

Possiamo usare la coroutine e il responsabile del lavoro per scaricare file in kotlin.

Aggiungi una dipendenza in build.gradle

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

Classe WorkManager

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

Inizia a scaricare tramite work manager nella classe di attività

 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

                                    }
                                }
                            }
                        }
                    })

        }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.