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 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 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 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 y faire de tâches d'interface utilisateur. D'autre part, onProgressUpdate
et onPreExecute
exé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: ResultReceiver
et IntentService
. ResultReceiver
est celui qui nous permettra de mettre à jour notre fil depuis un service; IntentService
est une sous-classe Service
qui génère un thread pour effectuer un travail d'arrière-plan à partir de là (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 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 ResultReceiver
venu 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 leResultReceiver
concept 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 GroundyTask
implé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.
3. Utilisez la DownloadManager
classe ( GingerBread
et plus récente seulement)
GingerBread a apporté une nouvelle fonctionnalité, DownloadManager
qui 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 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 disponible
- Assurez-vous que vous disposez des autorisations appropriées (
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 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