Téléchargez un fichier avec Android et affichez la progression dans un ProgressDialog

1047

J'essaie d'écrire une application simple qui est mise à jour. Pour cela, j'ai besoin d'une fonction simple qui peut télécharger un fichier et afficher la progression actuelle dans un fichier ProgressDialog. Je sais comment faire le ProgressDialog, mais je ne sais pas comment afficher la progression actuelle et comment télécharger le fichier en premier lieu.

Tom Leese
la source
2
J'espère que le lien ci-dessous peut vous aider ... androidhive.info/2012/04/…
Ganesh Katikar
1
Pour une solution rapide, reportez-vous à cette réponse
Richa
stackoverflow.com/a/43069239/3879214 vérifier cette réponse
Anu Martin

Réponses:

1874

Il existe de nombreuses façons de télécharger des fichiers. Après, je publierai les moyens les plus courants; c'est à vous de décider quelle méthode est la meilleure pour votre application.

1. Utilisez AsyncTasket affichez la progression du téléchargement dans une boîte de dialogue

Cette méthode vous permettra d'exécuter certains processus d'arrière-plan et de mettre à jour l'interface utilisateur en même temps (dans ce cas, nous mettrons à jour une barre de progression).

Importations:

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

Ceci est un exemple de code:

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

Le AsyncTaskressemblera à ceci:

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

La méthode ci-dessus ( doInBackground) s'exécute toujours sur un thread d'arrière-plan. Vous ne devriez pas y faire de tâches d'interface utilisateur. D'autre part, onProgressUpdateet onPreExecuteexécutez sur le thread d'interface utilisateur, vous pouvez donc y changer la barre de progression:

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

Pour que cela fonctionne, vous avez besoin de l'autorisation WAKE_LOCK.

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

2. Téléchargement à partir du service

La grande question ici est: comment mettre à jour mon activité depuis un service? . Dans l'exemple suivant, nous allons utiliser deux classes que vous ne connaissez peut-être pas: ResultReceiveret IntentService. ResultReceiverest celui qui nous permettra de mettre à jour notre fil depuis un service; IntentServiceest une sous-classe Servicequi génère un thread pour effectuer un travail d'arrière-plan à partir de là (vous devez savoir qu'un Services'exécute réellement dans le même thread de votre application; lorsque vous étendez Service, vous devez générer manuellement de nouveaux threads pour exécuter les opérations de blocage du processeur).

Le service de téléchargement peut ressembler à ceci:

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

Ajoutez le service à votre manifeste:

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

Et l'activité ressemblera à ceci:

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

Voici est ResultReceivervenu jouer:

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 Utiliser la bibliothèque Groundy

Groundy est une bibliothèque qui vous aide essentiellement à exécuter des morceaux de code dans un service d'arrière-plan, et elle est basée sur leResultReceiverconcept illustré ci-dessus. Cette bibliothèque est obsolète pour le moment. Voici à quoiressemblerait tout le code:

L'activité où vous affichez la boîte de dialogue ...

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

Une GroundyTaskimplémentation utilisée par Groundy pour télécharger le fichier et montrer la progression:

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

Et ajoutez simplement ceci au manifeste:

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

Ça ne pourrait pas être plus facile je pense. Prenez le dernier pot de Github et vous êtes prêt à partir. Gardez à l'esprit que le but principal de Groundy est d'appeler des API REST externes dans un service d'arrière-plan et de publier facilement les résultats dans l'interface utilisateur. Si vous faites quelque chose comme ça dans votre application, cela pourrait être vraiment utile.

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

3. Utilisez la DownloadManagerclasse ( GingerBreadet plus récente seulement)

GingerBread a apporté une nouvelle fonctionnalité, DownloadManagerqui vous permet de télécharger facilement des fichiers et de déléguer le dur travail de gestion des threads, des flux, etc. au système.

Voyons d'abord une méthode utilitaire:

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

Le nom de la méthode explique tout cela. Une fois que vous êtes sûr qu'il DownloadManagerest disponible, vous pouvez faire quelque chose comme ceci:

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

La progression du téléchargement s'affichera dans la barre de notification.

Dernières pensées

Les première et deuxième méthodes ne sont que la pointe de l'iceberg. Il y a beaucoup de choses que vous devez garder à l'esprit si vous voulez que votre application soit robuste. Voici une brève liste:

  • Vous devez vérifier si l'utilisateur dispose d'une connexion Internet disponible
  • Assurez-vous que vous disposez des autorisations appropriées ( INTERNETet WRITE_EXTERNAL_STORAGE); aussi ACCESS_NETWORK_STATEsi vous voulez vérifier la disponibilité d'Internet.
  • Assurez-vous que le répertoire où vous allez télécharger les fichiers existe et dispose des autorisations d'écriture.
  • Si le téléchargement est trop volumineux, vous souhaiterez peut-être mettre en œuvre un moyen de reprendre le téléchargement si les tentatives précédentes ont échoué.
  • Les utilisateurs vous seront reconnaissants si vous leur permettez d'interrompre le téléchargement.

Sauf si vous avez besoin d'un contrôle détaillé du processus de téléchargement, envisagez d'utiliser DownloadManager(3) car il gère déjà la plupart des éléments répertoriés ci-dessus.

Mais considérez également que vos besoins peuvent changer. Par exemple, DownloadManager ne met pas en cache la réponse . Il téléchargera aveuglément plusieurs fois le même gros fichier. Il n'y a pas de moyen facile de le réparer après coup. Où si vous commencez avec une base HttpURLConnection(1, 2), alors tout ce dont vous avez besoin est d'ajouter une HttpResponseCache. Ainsi, l'effort initial d'apprentissage des outils standard de base peut être un bon investissement.

Cette classe est déconseillée au niveau de l'API 26. ProgressDialog est une boîte de dialogue modale qui empêche l'utilisateur d'interagir avec l'application. Au lieu d'utiliser cette classe, vous devez utiliser un indicateur de progression comme ProgressBar, qui peut être intégré dans l'interface utilisateur de votre application. Vous pouvez également utiliser une notification pour informer l'utilisateur de l'avancement de la tâche. Pour plus de détails Lien

Cristian
la source
8
DownloadManager fait partie du système d'exploitation, ce qui signifie qu'il sera toujours disponible en Go + et ne pourra pas être désinstallé.
Cristian
17
Il y a un problème dans la réponse de Cristian. Parce que le code à " 1. Utilisez AsyncTask et affichez la progression du téléchargement dans une boîte de dialogue " fait connection.connect (); alors InputStream input = new BufferedInputStream (url.openStream ()); le code établit 2 connexions au serveur. J'ai réussi à changer ce comportement en mettant à jour le code comme suit InputStream input = new BufferedInputStream (connection.getInputStream ());
nLL
99
Je souhaite que la documentation Android soit aussi concise.
Lou Morda
12
Suggéré aux close()flux ( inputet output) dans finallyau lieu de try, sinon si une exception est levée avant close(), vous avez des flux non fermés qui traînent.
Pang
32
Ne pas coder en dur / sdcard/utiliser à la Environment.getExternalStorageDirectory()place.
Nima G
106

N'oubliez pas d'ajouter des autorisations à votre fichier manifeste si vous allez télécharger des trucs sur 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>
Mnightmare
la source
1
Je suis sûr que vous n'avez pas besoin de READ_PHONE_STATE et que vous n'avez certainement pas besoin de WRITE_EXTERNAL_STORAGE; c'est une permission dangereuse qui peut être évitée en utilisant le Storage Access Framework.
Rétablir Monica
32

Oui , le code ci - dessus fonctionnera si vous .Mais mettez à jour votre progressbardans onProgressUpdatede Asynctask et vous appuyez sur le bouton arrière ou terminer votre activité AsyncTaskdesserre sa piste avec votre interface utilisateur .Et quand vous revenez à votre activité, même si le téléchargement est en cours d' exécution en arrière - plan , vous verrez pas de mise à jour sur la barre de progression. Donc, OnResume()essayez d'exécuter un thread comme runOnUIThreadavec une tâche de minuterie qui met à jour ur progressbaravec des valeurs de mise à jour à partir de l' AsyncTaskarrière-plan en cours d'exécution.

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

N'oubliez pas de détruire le fil lorsque votre activité n'est pas visible.

private void destroyRunningThreads() {
    if (background != null) {
        background.interrupt();
        background=null;
    }
}
Sheetal
la source
1
C'est exactement mon problème. Pouvez-vous me dire comment effectuer une tâche de minuteur pour mettre à jour la barre de progression? Comment obtenir la mise à jour des valeurs à partir de la tâche AsyncTask exécutée derrière
user1417127
2
okk prenez une variable globale ou statique pour savoir où mettre à jour vos valeurs à partir de asynctask ... ou u pouvez l'insérer dans DataBase pour un côté sûr..pour que la fermeture de l'application ne gêne pas..Et lorsque vous redémarrez l'activité où vous voulez mettre à jour votre ur L'interface utilisateur exécute le thread d'interface utilisateur ... voir l'exemple ci
Sheetal
L'interface utilisateur devrait avoir la nouvelle référence..Comme le ProgressBar nouvellement initalisé dans mon cas
2012
@sheetal, mais cela fonctionne très bien sans votre code! Pourquoi?! Mon appareil est Xperia P avec Android 4.0.4. J'ai défini une variable booléenne statique à laquelle onPreExecute la définit sur true et onPostExecute la définit sur false. Cela montre que nous téléchargeons ou non, afin que nous puissions vérifier si la variable est égale à true, afficher la boîte de dialogue précédente de la barre de progression.
Behzad
@sheetal votre code est peu obscurant, pouvez-vous me donner quelques conseils?
iSun
17

Je vous recommande d'utiliser mon Project Netroid , il est basé sur Volley . Je lui ai ajouté des fonctionnalités telles que le rappel multi-événements, la gestion du téléchargement de fichiers. Cela pourrait être utile.

VinceStyling
la source
2
Prend-il en charge le téléchargement de fichiers multiples? Je travaille sur un projet qui permet à l'utilisateur de télécharger des programmes planifiés. À une heure précise (12 h par exemple), le service téléchargera tout le lien que l'utilisateur a sélectionné auparavant. Je veux dire que le service dont j'ai besoin doit pouvoir mettre en file d'attente les liens de téléchargement, puis les télécharger tous (un par un ou en parallèle, selon l'utilisateur). Merci
Hoang Trinh
@piavgh oui, tout ce que vous vouliez était de satisfaire, vous pouvez consulter mon exemple d'apk, il comprenait une démo de gestion de téléchargement de fichiers.
VinceStyling
Grande bibliothèque! Fonctionne bien, même si j'ai des problèmes pour télécharger du contenu à partir de l'URL HTTPS, car bien que l'on puisse définir votre propre SSLSocketFactoryajout, il HostnameVerifiern'est pas possible et j'ai besoin d'un tel vérificateur. A soulevé un problème à ce sujet.
DarkCygnus
lien pour Volley. -> Introuvable
Rumit Patel
14

J'ai modifié la AsyncTaskclasse pour gérer la création de progressDialogdans le même contexte. Je pense que le code suivant sera plus réutilisable. (il peut être appelé depuis n'importe quelle activité juste passer le contexte, le fichier cible, le message de dialogue)

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();
    }
}
Le syrien
la source
N'oubliez pas d'ajouter l'autorisation de verrouillage Wake <uses-permission android: name = "android.permission.WAKE_LOCK" />
Hitesh Sahu
8

N'oubliez pas de remplacer "/ sdcard ..." par un nouveau fichier ("/ mnt / sdcard / ...") sinon vous obtiendrez une FileNotFoundException

ENSATE
la source
Dans la partie 2, la boîte de dialogue Progression du téléchargement ne peut pas afficher l'incrément de progression. il retourne directement 100, donc si 100 il vérifie directement setprogress 100 et progresse, comment incrémenter la progression ?? il n'affiche que 0 progression mais télécharge en cours d'exécution
Nirav Mehta
il affiche seulement 0% sur 100 uniquement les autres travaux correctement
Nirav Mehta
14
Ne fais pas ça! Il y a Environment.getExternalStorageDirectory().getAbsolutePath()pour obtenir un chemin vers sdcard. N'oubliez pas non plus de vérifier si le stockage externe est monté - Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)( Mannaz )
naXa
8

J'ai trouvé ce billet de blog très utile, son utilisation de loopJ pour télécharger le fichier, il n'a qu'une seule fonction simple, sera utile à certains nouveaux gars Android.

chintan adatiya
la source
7

Alors que je commençais à apprendre le développement Android, j'avais appris que c'était ProgressDialogla voie à suivre. Il existe une setProgressméthode ProgressDialogqui peut être invoquée pour mettre à jour le niveau de progression lors du téléchargement du fichier.

Le meilleur que j'ai vu dans de nombreuses applications est qu'elles personnalisent les attributs de cette boîte de dialogue de progression pour donner une meilleure apparence à la boîte de dialogue de progression que la version stock. Bon pour garder l'utilisateur engagé avec une animation comme la grenouille, l'éléphant ou les chats / chiots mignons. Toute animation avec dans la boîte de dialogue de progression attire les utilisateurs et ils n'ont pas envie d'attendre longtemps.

Sandeep
la source
5

Mon conseil personnel est d'utiliser la boîte de dialogue de progression et de créer avant l'exécution, ou de commencer à OnPreExecute()publier souvent la progression si vous utilisez le style horizontal de la barre de progression de la boîte de dialogue de progression. La partie restante est d'optimiser l'algorithme de doInBackground.

Raju yourPepe
la source
5

Utilisez la bibliothèque de requêtes Android, très cool en effet.Vous pouvez la changer pour l'utiliser ProgressDialogcomme vous le voyez dans d'autres exemples, celle-ci affichera la vue de progression de votre mise en page et la masquera une fois terminée.

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
la source
Le truc, c'est que j'ai cessé de l'utiliser, parce que ce n'est pas entretenu, alors ne supposez plus que c'est une bonne réponse.
Renetik
3

J'ajoute une autre réponse pour une autre solution que j'utilise maintenant parce qu'Android Query est si gros et non entretenu pour rester en bonne santé. J'ai donc déménagé sur ce 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) {
            ...
        }
    });
Renetik
la source
2

Autorisations

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

Utilisation de 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
la source
1

Vous pouvez observer la progression du gestionnaire de téléchargement en utilisant LiveData et coroutines, voir l'essentiel ci-dessous

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
la source
Pouvez-vous fournir un exemple d'utilisation pour cette classe?
Karim Karimov
0

Nous pouvons utiliser la coroutine et le gestionnaire de travaux pour télécharger des fichiers dans kotlin.

Ajouter une dépendance dans 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)
        }
    }

Commencez à télécharger via Work Manager dans la classe d'activité

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