使用Android下载文件,并在ProgressDialog中显示进度

我正试图编写一个简单的应用程序,获取更新。 为此,我需要一个简单的函数,可以下载一个文件,并显示进度对话框的当前进度 。 我知道如何做ProgressDialog ,但我不知道如何显示当前的进度以及如何下载文件。

有很多方法可以下载文件。 以下我会发布最常用的方法; 由您来决定哪种方法更适合您的应用程序。

1.使用AsyncTask并在对话框中显示下载进度

此方法将允许您执行一些后台进程并同时更新UI(在这种情况下,我们将更新进度条)。

这是一个示例代码:

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

AsyncTask将如下所示:

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

上面的方法( doInBackground )总是在后台线程上运行。 你不应该在那里做任何的UI任务。 另一方面, onProgressUpdateonPreExecute在UI线程上运行,所以你可以改变进度条:

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

为了运行,你需要WAKE_LOCK权限。

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

2.从服务下载

这里最大的问题是: 如何从服务中更新我的活动? 。 在下一个示例中,我们将使用两个您可能不知道的类: ResultReceiverIntentServiceResultReceiver是允许我们从服务更新线程的那个; IntentServiceService一个子类,它产生一个线程来从那里做后台工作(你应该知道一个Service实际上在你的应用程序的同一线程中运行;当你扩展Service ,你必须手动产生新线程来运行CPU阻塞操作) 。

下载服务可以像这样:

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

将该服务添加到您的清单:

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

活动将如下所示:

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

这里是ResultReceiver来玩:

 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使用Groundy库

Groundy是一个基本上可以帮助您在后台服务中运行代码的库,它基于上面显示的ResultReceiver概念。 目前这个库已经被弃用了。 这是整个代码的样子:

您正在显示对话框的活动…

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

GroundyTask用于下载文件并显示进度的GroundyTask实现:

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

只需将其添加到清单:

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

我觉得这不是件容易的事。 只要从Github抓来最新的jar子,你就可以走了。 请记住, Groundy的主要目的是在后台服务中调用外部REST API,并轻松地将结果发布到UI。 如果你在应用程序中做类似的事情,这可能是非常有用的。

2.2使用https://github.com/koush/ion

3.使用DownloadManager类( GingerBread和更新版本)

GingerBread带来了一个新function, DownloadManager ,它允许您轻松下载文件,并委托处理线程,stream等的辛勤工作,以系统。

首先,我们来看一个实用的方法:

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

方法的名字解释了这一切。 一旦你确定DownloadManager可用,你可以做这样的事情:

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

下载进度将显示在通知栏中。

最后的想法

第一种和第二种方法只是冰山一angular。 如果您希望自己的应用程序具有强大function,则需要记住很多事项。 这里是一个简短的列表:

  • 您必须检查用户是否有可用的互联网连接
  • 确保你有正确的权限( INTERNETWRITE_EXTERNAL_STORAGE ); 也ACCESS_NETWORK_STATE如果你想检查互联网的可用性。
  • 确保目录是你要下载的文件存在,并有写权限。
  • 如果下载太大,如果以前的尝试失败,可能需要实现一种恢复下载的方式。
  • 如果你允许他们中断下载,用户将不胜感激。

除非需要详细控制下载过程,否则请考虑使用DownloadManager (3),因为它已经处理了大部分上面列出的项目。

但也要考虑到你的需求可能会改变。 例如, DownloadManager 不响应caching 。 它会盲目地多次下载同一个大文件。 事实之后没有简单的方法来修复它。 如果你从一个基本的HttpURLConnection (1,2)开始,那么你只需要添加一个HttpResponseCache 。 因此,学习基本的标准工具的初始努力可能是一个很好的投资。

如果你要从互联网上下载东西,不要忘记给清单文件添加权限!

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

是的,上面的代码可以工作。但是如果你正在更新你的progressbaronProgressUpdateAsynctask ,你回来button或完成你的活动AsyncTask失去了你的用户界面跟踪。当你回到你的活动,即使下载运行背景你将看到进度条上没有更新。 所以OnResume()尝试运行一个像runOnUIThread这样的线程与一个计时器任务更新你的progressbar ,从AsyncTask运行后台更新值。

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

不要忘记当你的活动不可见时销毁线程。

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

我修改了AsyncTask类来处理progressDialog在相同上下文中的创build。我认为下面的代码将更加可重用。 (它可以从任何活动只是传递上下文,目标文件,对话框消息)

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

我build议你使用我的Project Netroid ,它基于Volley 。 我已经添加了一些function,如多事件callback,文件下载pipe理。 这可能有一些帮助。

不要忘记用新的File (“/ mnt / sdcard / …”)replace“/ sdcard …”,否则你会得到一个FileNotFoundException

我发现这个博客文章非常有帮助,它使用loopJ来下载文件,它只有一个简单的function,会对一些新的android家伙有所帮助。

我的个人build议是使用进度对话框,并在执行前build立起来,或者在OnPreExecute()启动,如果使用进度对话框的水平样式的进度条,经常发布进度。 剩下的部分是对doInBackground的algorithm进行优化。

当我开始学习android开发的时候,我已经了解到ProgressDialog是要走的路。 ProgressDialogsetProgress方法可以在文件被下载时被调用来更新进度级别。

我在许多应用程序中看到的最好的方法是,它们自定义此进度对话框的属性,以使进度对话框的外观和感觉比股票版本更好。 很好的保持用户从事像青蛙,大象或可爱的猫/小狗一些animation。 在进度对话框中的任何animation吸引用户,他们不想被长时间等待。

我将在ProgressDialog上撰写博客文章,并尽快与大家分享。

编辑: 在Android下载文件时显示进度条

使用Android查询库,确实非常酷。您可以将其更改为使用ProgressDialog就像您在其他示例中看到的一样,这将从布局显示进度视图,并在完成后隐藏它。

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

权限

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

使用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 = "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)); } } 

我正在为我现在使用的其他解决scheme添加另一个答案,因为Android查询是如此之大,无法保持健康。 所以我搬到这个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) { ... } });