1103 votes

Télécharger un fichier avec Android, et afficher la progression dans un ProgressDialog

J'essaie d'écrire une application simple qui est mise à jour. Pour ce faire, j'ai besoin d'une fonction simple qui peut télécharger un fichier et le mettre à jour. montrer la progression actuelle dans un 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.

2 votes

J'espère que le lien ci-dessous pourra vous aider... androidhive.info/2012/04/

1 votes

Pour une solution rapide voir cette réponse

0 votes

stackoverflow.com/a/43069239/3879214 vérifier cette réponse

1927voto

Cristian Points 92147

Il existe de nombreuses façons de télécharger des fichiers. Vous trouverez ci-dessous les méthodes les plus courantes ; c'est à vous de décider quelle méthode est la meilleure pour votre application.

1. Utilisez AsyncTask et afficher la progression du téléchargement dans une boîte de dialogue

Cette méthode vous permettra d'exécuter certains processus en 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;

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

El AsyncTask ressemblera à 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 fil d'arrière-plan. Vous ne devriez pas y effectuer de tâches d'interface utilisateur. D'autre part, le onProgressUpdate y onPreExecute s'exécutent sur le thread de l'interface utilisateur, ce qui permet de modifier 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 la permission WAKE_LOCK.

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

2. Télécharger à partir de Service

La grande question ici est : comment mettre à jour mon activité à partir d'un service ? . Dans l'exemple suivant, nous allons utiliser deux classes que vous ne connaissez peut-être pas : ResultReceiver y IntentService . ResultReceiver est celui qui nous permettra de mettre à jour notre fil de discussion à partir d'un service ; IntentService est une sous-classe de Service qui génère un thread pour effectuer le travail de fond à partir de là (vous devez savoir qu'un Service s'exécute en fait dans le même thread de votre application ; lorsque vous étendez Service vous devez manuellement créer de nouveaux threads pour exécuter les opérations de blocage du CPU).

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 où étaient ResultReceiver vient 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 de Groundy

Groundy est une bibliothèque qui vous aide à exécuter des morceaux de code dans un service d'arrière-plan. ResultReceiver concept présenté ci-dessus. Cette bibliothèque est déprécié en ce moment. C'est ainsi que le ensemble du site Le code serait le suivant :

L'activité où vous montrez le 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;
            }
        }
    };
}

A GroundyTask 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 ceci au manifeste :

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

Ça ne pourrait pas être plus facile, je pense. Il suffit de prendre le dernier bocal à partir de Github et vous êtes prêt à partir. Gardez à l'esprit que Groundy L'objectif principal de l'application est d'appeler des apis REST externes dans un service d'arrière-plan et d'afficher facilement les résultats sur l'interface utilisateur. Si vous faites quelque chose comme ça dans votre application, cela peut être très utile.

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

3. Utilisez DownloadManager classe ( GingerBread et plus récents seulement)

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

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. Une fois que vous êtes sûr DownloadManager est 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'affiche dans la barre de notification.

Dernières réflexions

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

  • Vous devez vérifier si l'utilisateur dispose d'une connexion Internet.
  • Assurez-vous que vous avez les bonnes permissions ( INTERNET y WRITE_EXTERNAL_STORAGE ) ; également ACCESS_NETWORK_STATE si vous voulez vérifier la disponibilité de l'internet.
  • Assurez-vous que le répertoire dans lequel vous allez télécharger les fichiers existe et qu'il a les droits d'écriture.
  • Si le téléchargement est trop important, vous pouvez mettre en place 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.

À moins que vous n'ayez besoin d'un contrôle détaillé du processus de téléchargement, envisagez d'utiliser l'option DownloadManager (3) parce qu'il gère déjà la plupart des éléments énumérés ci-dessus.

Mais considérez également que vos besoins peuvent changer. Par exemple, DownloadManager ne fait pas de mise en cache des réponses . Il téléchargera aveuglément le même gros fichier plusieurs fois. 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), il suffit alors d'ajouter un HttpResponseCache . L'effort initial d'apprentissage des outils standard de base peut donc être un bon investissement.

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

0 votes

Cristian, je sais que ce sujet est présent depuis un moment mais je me demandais si vous pouviez me montrer comment faire pour que la barre de progression s'affiche. J'ai utilisé l'exemple que vous avez donné et cela fonctionne bien (téléchargements de fichiers) mais je n'ai jamais de barre de progression. Merci beaucoup. Mike

0 votes

N'y a-t-il pas une confusion entre ProgressDialog et ProgressBar dans l'exemple ? Lorsque je l'essaie, le compilateur se plaint.

0 votes

J'ai quelques problèmes avec https urls après conexion.connect(); . Pendant le débogage, j'ai vérifié qu'il y a un drapeau dans le fichier conecion objet nommé connected qui est réglé sur false . Avez-vous une idée de la façon de résoudre ce problème ? Ou quelles sont les étapes supplémentaires à suivre pour réussir les téléchargements https ? tks !

112voto

Mnightmare Points 603

N'oubliez pas d'ajouter des permissions à votre fichier manifeste si vous devez télécharger des choses depuis l'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>

2 votes

Je suis pratiquement 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.

34voto

sheetal Points 1061

Oui, le code ci-dessus fonctionnera. Mais si vous mettez à jour votre progressbar en onProgressUpdate de Asynctask et vous appuyez sur le bouton retour ou vous terminez votre activité AsyncTask Et lorsque vous retournez à votre activité, même si le téléchargement est en cours en arrière-plan, vous ne verrez aucune mise à jour dans la barre de progression. Ainsi, le OnResume() essayez d'exécuter un fil comme runOnUIThread avec une tâche de temporisation qui met à jour ur progressbar avec des valeurs mises à jour à partir de la AsyncTask le fond de commerce.

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 de discussion lorsque votre activité n'est pas visible.

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

1 votes

C'est exactement mon problème. Pouvez-vous me dire comment faire une tâche de minuterie pour mettre à jour la barre de progression ? Comment obtenir la mise à jour des valeurs à partir de la tâche AsyncTask qui s'exécute en arrière-plan ?

2 votes

Okk prenez une variable globale ou statique où mettre à jour vos valeurs à partir de l'asynchronisme... ou vous pouvez l'insérer dans la base de données pour un côté sûr... de sorte que la fermeture de l'application ne sera pas entravée... Et quand vous redémarrez l'activité où vous voulez mettre à jour votre IU, exécutez le thread IU... voir l'exemple ci-dessous

0 votes

L'interface utilisateur devrait avoir la nouvelle référence, comme la barre de progression nouvellement initialisée dans mon cas.

20voto

VinceStyling Points 896

Je vous recommande d'utiliser mon projet Netroid C'est basé sur Volley-ball . J'y ai ajouté quelques fonctionnalités telles que le rappel de plusieurs événements, la gestion du téléchargement de fichiers. Cela pourrait vous être utile.

2 votes

Prend-il en charge le téléchargement de plusieurs fichiers ? Je travaille sur un projet qui permet à l'utilisateur de télécharger des fichiers programmés. À une heure précise (12 h par exemple), le service téléchargera tous les liens que l'utilisateur a sélectionnés auparavant. Je veux dire que le service dont j'ai besoin doit être capable de mettre en file d'attente les liens de téléchargement, puis de les télécharger tous (un par un ou en parallèle, selon l'utilisateur). Merci pour votre aide.

0 votes

@piavgh oui, tout ce que vous vouliez c'était être satisfait, vous pouvez vérifier mon apk échantillon, il incluait une démo de gestion de téléchargement de fichiers.

0 votes

Super bibliothèque ! Fonctionne bien, bien que j'aie des problèmes pour télécharger du contenu à partir d'URL HTTPS, car même si l'on peut définir ses propres URL, il n'y a pas de problème. SSLSocketFactory l'ajout d'un HostnameVerifier n'est pas possible et j'ai besoin d'un tel vérificateur. A soulevé une numéro à ce sujet.

10voto

chintan adatiya Points 146

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X