android中如何下载文件并显示下载进度

最近开发中遇到需要下载文件的问题,对于一般的下载来说不用考虑断点续传,不用考虑多个线程,比如下载一个apk之类的,这篇文章讨论的就是这种情形。

这里主要讨论三种方式:AsyncTask、Service和使用DownloadManager。

一、使用AsyncTask并在进度对话框中显示下载进度

这种方式的优势是你可以在后台执行下载任务的同时,也可以更新UI(这里我们用progress bar来更新下载进度)

下面的代码是使用的例子

 
 
  1. // declare the dialog as a member field of your activity
  2. ProgressDialog mProgressDialog;
  3. // instantiate it within the onCreate method
  4. mProgressDialog = new ProgressDialog(YourActivity.this);
  5. mProgressDialog.setMessage("A message");
  6. mProgressDialog.setIndeterminate(true);
  7. mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  8. mProgressDialog.setCancelable(true);
  9. // execute this when the downloader must be fired
  10. final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
  11. downloadTask.execute("the url to the file you want to download");
  12. mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
  13. @Override
  14. public void onCancel(DialogInterface dialog) {
  15. downloadTask.cancel(true);
  16. }
  17. });

DownloadTask继承自AsyncTask,按照如下框架定义,你需要将代码中的某些参数替换成你自己的。

 
 
  1. // usually, subclasses of AsyncTask are declared inside the activity class.
  2. // that way, you can easily modify the UI thread from here
  3. private class DownloadTask extends AsyncTask<String, Integer, String> {
  4. private Context context;
  5. private PowerManager.WakeLock mWakeLock;
  6. public DownloadTask(Context context) {
  7. this.context = context;
  8. }
  9. @Override
  10. protected String doInBackground(String... sUrl) {
  11. InputStream input = null;
  12. OutputStream output = null;
  13. HttpURLConnection connection = null;
  14. try {
  15. URL url = new URL(sUrl[0]);
  16. connection = (HttpURLConnection) url.openConnection();
  17. connection.connect();
  18. // expect HTTP 200 OK, so we don't mistakenly save error report
  19. // instead of the file
  20. if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
  21. return "Server returned HTTP " + connection.getResponseCode()
  22. + " " + connection.getResponseMessage();
  23. }
  24. // this will be useful to display download percentage
  25. // might be -1: server did not report the length
  26. int fileLength = connection.getContentLength();
  27. // download the file
  28. input = connection.getInputStream();
  29. output = new FileOutputStream("/sdcard/file_name.extension");
  30. byte data[] = new byte[4096];
  31. long total = 0;
  32. int count;
  33. while ((count = input.read(data)) != -1) {
  34. // allow canceling with back button
  35. if (isCancelled()) {
  36. input.close();
  37. return null;
  38. }
  39. total += count;
  40. // publishing the progress....
  41. if (fileLength > 0) // only if total length is known
  42. publishProgress((int) (total * 100 / fileLength));
  43. output.write(data, 0, count);
  44. }
  45. } catch (Exception e) {
  46. return e.toString();
  47. } finally {
  48. try {
  49. if (output != null)
  50. output.close();
  51. if (input != null)
  52. input.close();
  53. } catch (IOException ignored) {
  54. }
  55. if (connection != null)
  56. connection.disconnect();
  57. }
  58. return null;
  59. }


上面的代码只包含了doInBackground,这是执行后台任务的代码块,不能在这里做任何的UI操作,但是onProgressUpdate和onPreExecute是运行在UI线程中的,所以我们应该在这两个方法中更新progress bar。

接上面的代码:


 
 
  1. @Override
  2. protected void onPreExecute() {
  3. super.onPreExecute();
  4. // take CPU lock to prevent CPU from going off if the user
  5. // presses the power button during download
  6. PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
  7. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  8. getClass().getName());
  9. mWakeLock.acquire();
  10. mProgressDialog.show();
  11. }
  12. @Override
  13. protected void onProgressUpdate(Integer... progress) {
  14. super.onProgressUpdate(progress);
  15. // if we get here, length is known, now set indeterminate to false
  16. mProgressDialog.setIndeterminate(false);
  17. mProgressDialog.setMax(100);
  18. mProgressDialog.setProgress(progress[0]);
  19. }
  20. @Override
  21. protected void onPostExecute(String result) {
  22. mWakeLock.release();
  23. mProgressDialog.dismiss();
  24. if (result != null)
  25. Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
  26. else
  27. Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
  28. }

注意需要添加如下权限:

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


二、在service中执行下载

在service中执行下载任务的麻烦之处在于如何通知activity更新UI。下面的代码中我们将用ResultReceiver和IntentService来实现下载。ResultReceiver允许我们接收来自service中发出的广播,IntentService继承自service,这IntentService中我们开启一个线程开执行下载任务(service和你的app其实是在一个线程中,因此不想阻塞主线程的话必须开启新的线程)。

 
 
  1. public class DownloadService extends IntentService {
  2. public static final int UPDATE_PROGRESS = 8344;
  3. public DownloadService() {
  4. super("DownloadService");
  5. }
  6. @Override
  7. protected void onHandleIntent(Intent intent) {
  8. String urlToDownload = intent.getStringExtra("url");
  9. ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
  10. try {
  11. URL url = new URL(urlToDownload);
  12. URLConnection connection = url.openConnection();
  13. connection.connect();
  14. // this will be useful so that you can show a typical 0-100% progress bar
  15. int fileLength = connection.getContentLength();
  16. // download the file
  17. InputStream input = new BufferedInputStream(connection.getInputStream());
  18. OutputStream output = new FileOutputStream("/sdcard/BarcodeScanner-debug.apk");
  19. byte data[] = new byte[1024];
  20. long total = 0;
  21. int count;
  22. while ((count = input.read(data)) != -1) {
  23. total += count;
  24. // publishing the progress....
  25. Bundle resultData = new Bundle();
  26. resultData.putInt("progress" ,(int) (total * 100 / fileLength));
  27. receiver.send(UPDATE_PROGRESS, resultData);
  28. output.write(data, 0, count);
  29. }
  30. output.flush();
  31. output.close();
  32. input.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. Bundle resultData = new Bundle();
  37. resultData.putInt("progress" ,100);
  38. receiver.send(UPDATE_PROGRESS, resultData);
  39. }
  40. }

注册DownloadService

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

activity中这样调用DownloadService

 
 
  1. // initialize the progress dialog like in the first example
  2. // this is how you fire the downloader
  3. mProgressDialog.show();
  4. Intent intent = new Intent(this, DownloadService.class);
  5. intent.putExtra("url", "url of the file to download");
  6. intent.putExtra("receiver", new DownloadReceiver(new Handler()));
  7. startService(intent);

使用ResultReceiver接收来自DownloadService的下载进度通知

 
 
  1. private class DownloadReceiver extends ResultReceiver{
  2. public DownloadReceiver(Handler handler) {
  3. super(handler);
  4. }
  5. @Override
  6. protected void onReceiveResult(int resultCode, Bundle resultData) {
  7. super.onReceiveResult(resultCode, resultData);
  8. if (resultCode == DownloadService.UPDATE_PROGRESS) {
  9. int progress = resultData.getInt("progress");
  10. mProgressDialog.setProgress(progress);
  11. if (progress == 100) {
  12. mProgressDialog.dismiss();
  13. }
  14. }
  15. }
  16. }


2.1使用 Groundy library

Groundy 可以帮助你在后台service中运行一些代码,其实也是基于刚刚用到的 ResultReceiver,下面是使用Groundy的大致代码:

 
 
  1. public class MainActivity extends Activity {
  2. private ProgressDialog mProgressDialog;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7. findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
  8. public void onClick(View view) {
  9. String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();
  10. Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();
  11. Groundy.create(DownloadExample.this, DownloadTask.class)
  12. .receiver(mReceiver)
  13. .params(extras)
  14. .queue();
  15. mProgressDialog = new ProgressDialog(MainActivity.this);
  16. mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  17. mProgressDialog.setCancelable(false);
  18. mProgressDialog.show();
  19. }
  20. });
  21. }
  22. private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
  23. @Override
  24. protected void onReceiveResult(int resultCode, Bundle resultData) {
  25. super.onReceiveResult(resultCode, resultData);
  26. switch (resultCode) {
  27. case Groundy.STATUS_PROGRESS:
  28. mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));
  29. break;
  30. case Groundy.STATUS_FINISHED:
  31. Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);
  32. mProgressDialog.dismiss();
  33. break;
  34. case Groundy.STATUS_ERROR:
  35. Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
  36. mProgressDialog.dismiss();
  37. break;
  38. }
  39. }
  40. };
  41. }

其中GroundyTask的定义如下:

 
 
  1. public class DownloadTask extends GroundyTask {
  2. public static final String PARAM_URL = "com.groundy.sample.param.url";
  3. @Override
  4. protected boolean doInBackground() {
  5. try {
  6. String url = getParameters().getString(PARAM_URL);
  7. File dest = new File(getContext().getFilesDir(), new File(url).getName());
  8. DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
  9. return true;
  10. } catch (Exception pokemon) {
  11. return false;
  12. }
  13. }
  14. }

但是请记住要在activity中注册了相关service才行:

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


三、使用DownloadManager

其实这才是解决下载问题的终极方法,因为他使用起来实在是太简单了。可惜只有在GingerBread 之后才能使用。

先判断能不能使用DownloadManager:

 
 
  1. /**
  2. * @param context used to check the device version and DownloadManager information
  3. * @return true if the download manager is available
  4. */
  5. public static boolean isDownloadManagerAvailable(Context context) {
  6. try {
  7. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
  8. return false;
  9. }
  10. Intent intent = new Intent(Intent.ACTION_MAIN);
  11. intent.addCategory(Intent.CATEGORY_LAUNCHER);
  12. intent.setClassName("com.android.providers.downloads.ui", "com.android.providers.downloads.ui.DownloadList");
  13. List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent,
  14. PackageManager.MATCH_DEFAULT_ONLY);
  15. return list.size() > 0;
  16. } catch (Exception e) {
  17. return false;
  18. }
  19. }

如果能,那么只需要这样就可以开始下载一个文件了:

 
 
  1. String url = "url you want to download";
  2. DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
  3. request.setDescription("Some descrition");
  4. request.setTitle("Some title");
  5. // in order for this if to run, you must use the android 3.2 to compile your app
  6. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  7. request.allowScanningByMediaScanner();
  8. request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  9. }
  10. request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");
  11. // get download service and enqueue file
  12. DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
  13. manager.enqueue(request);

下载的进度会在消息通知中显示。

总结

前两种方法需要你考虑的东西很多,除非是你想完全控制下载的整个过程,否则用最后一种比较省事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值