java - remove - downloadmanager android dev




Télécharger un fichier avec Android, et montrant la progression dans un ProgressDialog (8)

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 montrer la progression actuelle dans un ProgressDialog . Je sais comment faire le ProgressDialog , mais je ne suis pas sûr comment afficher la progression actuelle et comment télécharger le fichier en premier lieu.


Alors que je commençais à apprendre le développement Android, j'avais appris que ProgressDialog est la voie à suivre. Il existe la méthode setProgress de ProgressDialog qui peut être invoquée pour mettre à jour le niveau de progression au fur et à mesure que le fichier est téléchargé.

Le meilleur que j'ai vu dans de nombreuses applications, c'est qu'ils 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 à garder l'utilisateur engagé avec une animation de grenouille, d'éléphant ou de chats / chiots mignons. Toute animation avec dans le dialogue de progression attire les utilisateurs et ils n'ont pas envie d'attendre longtemps.

Je vais écrire un article sur ProgressDialog et partager ici bientôt.

Edit: Afficher la barre de progression lors du téléchargement d'un fichier dans Android


Il y a plusieurs façons de télécharger des fichiers. Après je posterai les manières les plus communes; C'est à vous de décider quelle méthode est la meilleure pour votre application.

1. Utilisez AsyncTask et affichez 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).

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

L' 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 thread d'arrière-plan. Vous ne devriez pas faire de tâches d'interface utilisateur là-bas. D'un autre côté, onProgressUpdate et onPreExecute s'exécutent sur le thread de l'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écharger depuis le service

La grande question ici est: comment puis-je mettre à jour mon activité à partir d'un service? . Dans l'exemple suivant, nous allons utiliser deux classes dont vous n'êtes peut-être pas conscient: ResultReceiver et IntentService . ResultReceiver est celui qui nous permettra de mettre à jour notre thread à partir d'un service; IntentService est une sous-classe de Service qui génère un thread pour effectuer un travail en arrière-plan (vous devez savoir qu'un Service s'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). .

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 {
            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());
            OutputStream output = new FileOutputStream("/sdcard/BarcodeScanner-debug.apk");

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

            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 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");
            mProgressDialog.setProgress(progress);
            if (progress == 100) {
                mProgressDialog.dismiss();
            }
        }
    }
}

2.1 Utiliser la bibliothèque Groundy

Groundy est une bibliothèque qui vous aide essentiellement à exécuter des parties de code dans un service d'arrière-plan, et elle est basée sur le concept ResultReceiver présenté ci-dessus. Cette bibliothèque est obsolète pour le moment. Voici à quoi ressemblerait le code:

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

Une implémentation de GroundyTask utilisée par Groundy pour télécharger le fichier et montrer les progrès:

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 simplement le dernier pot de Github et vous êtes prêt à partir. Gardez à l'esprit que le but principal de Groundy est de faire des appels vers 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 très utile.

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

3. Utilisez la classe DownloadManager ( GingerBread et plus récent uniquement)

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

Voyons d'abord une méthode d'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 que 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'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
  • Assurez-vous que vous disposez des autorisations WRITE_EXTERNAL_STORAGE ( INTERNET et WRITE_EXTERNAL_STORAGE ); aussi ACCESS_NETWORK_STATE si vous voulez vérifier la disponibilité d'Internet.
  • Assurez-vous que le répertoire où vous allez télécharger des fichiers existe et dispose d'autorisations 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.

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

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


J'ai trouvé this blog très utile, Son utilisation loopJ pour télécharger le fichier, il a seulement une fonction simple, sera utile à certains nouveaux gars android.


J'ajoute une autre réponse pour l'autre solution que j'utilise maintenant parce que Android Query est si grand et pas maintenu pour rester en bonne santé. Donc je suis passé à cette 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) {
            ...
        }
    });

Mon conseil personnel est d'utiliser Progress Dialog et de construire avant l'exécution, ou initier à OnPreExecute() , publier des progrès souvent si vous utilisez le style horizontal de la barre de progression de la boîte de dialogue de progression. Le reste est d'optimiser l'algorithme de doInBackground .


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

Oui, le code ci-dessus fonctionnera. Mais si vous mettez à jour votre onProgressUpdate de progressbar dans onProgressUpdate de Asynctask et que vous appuyez sur le bouton Précédent ou terminez votre activité, AsyncTask perd sa trace avec votre interface utilisateur. Et lorsque vous revenez à votre activité En arrière-plan, vous ne verrez aucune mise à jour sur la barre de progression. Donc, sur OnResume() essayez d'exécuter un thread comme runOnUIThread avec une tâche timer qui met à jour votre AsyncTask progressbar avec les valeurs mises à jour à partir de l' AsyncTask -plan AsyncTask 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);
    }
};

Ne pas oublier de détruire le fil quand votre activité n'est pas visible.

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

Utilisez la bibliothèque Android Query, très cool en effet. Vous pouvez la changer pour utiliser ProgressDialog comme vous le voyez dans d'autres exemples, celui-ci montrera l'état d'avancement de votre mise en page et le cachera après l'achèvement.

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






android-asynctask