下载进度条的设计
来源:互联网 发布:复杂网络建模的研究生 编辑:程序博客网 时间:2024/06/08 06:44
摘自:
http://stackoverflow.com/questions/3028306/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog
There are many ways to download files. Following I will post most common ways; it is up to you to decide which method is better for your app.
1. Use AsyncTask
and show the download progress in a dialog
This method will allow you to execute some background processes and update the UI at the same time (in this case, we'll update a progress bar).
This is an example code:
// declare the dialog as a member field of your activityProgressDialog mProgressDialog;// instantiate it within the onCreate methodmProgressDialog = 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 firedfinal 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); }});
The AsyncTask
will look like this:
// usually, subclasses of AsyncTask are declared inside the activity class.// that way, you can easily modify the UI thread from hereprivate class DownloadTask extends AsyncTask<String, Integer, String> { private Context context; public DownloadTask(Context context) { this.context = context; } @Override protected String doInBackground(String... sUrl) { // 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); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); wl.acquire(); try { 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()) 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(); } } finally { wl.release(); } return null; }}
The method above (doInBackground
) runs always on a background thread. You shouldn't do any UI tasks there. On the other hand, theonProgressUpdate
andonPreExecute
run on the UI thread, so there you can change the progress bar:
@Override protected void onPreExecute() { super.onPreExecute(); 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) { 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(); }
For this to run, you need the WAKE_LOCK permission.
<uses-permission android:name="android.permission.WAKE_LOCK" />
2. Download from Service
The big question here is: how do I update my activity from a service?. In the next example we are going to use two classes you may not be aware of:ResultReceiver
andIntentService
. ResultReceiver
is the one that will allow us to update our thread from a service;IntentService
is a subclass ofService
which spawns a thread to do background work from there (you should know that aService
runs actually in the same thread of your app; when you extendsService
, you must manually spawn new threads to run CPU blocking operations).
Download service can look like this:
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(url.openStream()); 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); }}
Add the service to your manifest:
<service android:name=".DownloadService"/>
And the activity will look like this:
// initialize the progress dialog like in the first example// this is how you fire the downloadermProgressDialog.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);
Here is were ResultReceiver
comes to play:
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 Use Groundy library
Groundy is a library a friend and I wrote time ago. It basically helps you run pieces of code in a background service, and it is based on theResultReceiver
concept shown above. This is how the whole code would look like...
The activity where you are showing the dialog...
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
implementation used by Groundy to download the file and show the progress:
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; } }}
And just add this to the manifest:
<service android:name="com.codeslap.groundy.GroundyService"/>
It couldn't be easier I think. Just grab the latest jar from Github and you are ready to go. Keep in mind that Groundy's main purpose is to make calls to external REST apis in a background service and post results to the UI with easily. If you are doing something like that in your app, it could be really useful.
3. Use DownloadManager
class (GingerBread
and newer only)
This method is awesome, you do not have to worry about downloading the file manually, handle threads, streams, etc. GingerBread brought a new feature:DownloadManager
which allows you to download files easily and delegate the hard work to the system.
First, let's see a utility method:
/** * @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) { try { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { return false; } Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setClassName("com.android.providers.downloads.ui", "com.android.providers.downloads.ui.DownloadList"); List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; } catch (Exception e) { return false; }}
Method's name explains it all. Once you are sure DownloadManager
is available, you can do something like this:
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 appif (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 fileDownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);manager.enqueue(request);
Download progress will be showing in the notification bar.
Final thoughts
First and second methods are just the tip of the iceberg. There are lots of things you have to keep in mind if you want your app to be robust. Here is a brief list:
- You must check whether user has an internet connection available
- Make sure you have the right permissions (
INTERNET
andWRITE_EXTERNAL_STORAGE
); alsoACCESS_NETWORK_STATE
if you want to check internet availability. - Make sure the directory were you are going to download files exist and has write permissions.
- If download is too big you may want to implement a way to resume the download if previous attempts failed.
- Users will be grateful if you allow them to interrupt the download.
Unless you want to have full control over the download process, I highly recommend usingDownloadManager
which already handles most of the items listed above.
- 下载进度条的设计
- Winform 下载文件进度条设计
- android下载的进度条
- 设计你自己的进度条
- 设计你自己的进度条
- 设计你自己的进度条
- 下载进度条的问题总结
- android 下载进度条的实现
- 各种样式的进度条下载
- Js下载进度条的实现
- 播放器进度条的设计
- 播放器进度条的设计
- MFC多线程的进度条设计
- 下载进度条
- 下载进度条
- 下载进度条
- 50个创意进度条优秀设计(附PSD下载)
- Android文件下载进度条的实现
- Reporting Service 开发中的知识汇总
- C语言文件操作函数大全
- jquery+c# 分页列表模块
- CloudStack 4.0.2 vRouter导致重启后状态不正常
- 无需第三个变量实现两个数的交换
- 下载进度条的设计
- 循环遍历文件夹
- 第一篇博客
- python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变
- 3.1.5 Enhancements
- iOS AvPlayer AvAudioPlayer音频的后台播放问题
- 数据库操作:添加、插入、更新语句
- 操作系统重点双语阅读 - 上下文切换 Context Switch
- BOOL,int,float,指针变量 与“零值”比较的if语句