java - 安卓ProgressDialog的使用:下载某个文件并显示进度

  显示原文与译文双语对照的内容

我正在尝试编写一个简单的应用程序来更新。 我需要一个简单的函数,可以下载一个文件并在 ProgressDialog 显示当前进展。 我知道如何执行 ProgressDialog,但不知道如何显示当前进度以及如何在第一个位置下载该文件。

时间:

下载文件有多种方法。 下面我将发布最常用的方法;由你决定哪种方法适合你的应用。

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

这里方法允许你执行一些后台进程并同时更新用户界面( 在本例中,我们将更新进度栏) 。

这是一个示例代码:


//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 ) 总是在后台线程上运行。 你不应该执行任何用户界面任务。 另一方面,onProgressUpdateonPreExecute 在用户界面线程上运行,因此你可以更改进度栏:


 @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.从服务下载

这里的问题是: 如何更新我 Activity 从服务? 在下一个示例中,我们将使用两个你可能不知道的类: ResultReceiverIntentServiceResultReceiver 是允许我们从一个服务更新线程的;IntentServiceService的子类,它生成一个线程来完成后台工作。

下载服务可以如下所示:


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

将服务添加到 Manifest:


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

Activity 将如下所示:


//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 是一个朋友和我很久以前编写的库。 它基本上帮助你在后台服务中运行代码 Fragment,它基于上面显示的ResultReceiver 概念。 这是如何整个代码会看起来像...

显示对话框的Activity 。。


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 实现使用 Groundy下载文件并显示进度:


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

并将它的添加到 Manifest:


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

我觉得我觉得。 只需从Github服务器获取最新的jar ,就可以开始了。 记住 Groundy主要的目的是让外部restapi调用后台服务和公布财报的ui。 如果你在你的应用中做类似的事情,它可能非常有用。

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

3 。使用 DownloadManager 类( 仅适用于 GingerBread 和更新版本)

这个方法很棒,你不必担心手动下载文件,处理线程,流,等等 姜饼带来了一个新特性: DownloadManager 允许你轻松下载文件并将硬盘委托给系统。

首先,让我们看看一个实用程序方法:


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

方法的名称解释了一切。 确定 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);

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

final 思想

第一和第二方法只是冰山一角。 如果你想让你的应用健壮,你需要记住很多东西。 下面是一个简短的列表:

  • 你必须检查用户是否有互联网连接
  • 请确定你有正确的权限( INTERNETWRITE_EXTERNAL_STORAGE ) ;如果你想检查互联网可用性,还需要 ACCESS_NETWORK_STATE
  • 确保目录是你要下载文件并具有写权限的目录。
  • 如果下载太大,你可能想实现一种方法,以便在上一次尝试失败时恢复下载。
  • 如果你允许他们中断下载,用户会很感激。

除非你想完全控制下载过程,否则我强烈建议使用 DownloadManager,它已经处理了上面列出的大部分项目。

如果你要从互联网下载内容,别忘了向你的Manifest 文件添加权限 !


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

是的上面的代码将. But 如果你更新你的progressbar onProgressUpdateAsynctask 和你按后退按钮或者完成你 Activity Asynctask 失去其轨道与ui. And 当你回到 Activity, 即使下载运行在后台你会看到progressbar上没有更新。 等等 OnResume() 尝试运行一个线程就像用一个定时器任务,更新你的progressbar runOnUIThreadAsynctask 运行背景值更新。


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

当你的Activity 不可见时,不要忘记销毁线程。


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

不要忘记把"/sdcard.. 。"换成新文件 ("/mnt/sdcard/...") 否则你会得到一个 FileNotFoundException

我的个人建议是使用进度Dialog对话框,然后在执行前构建,或者在 OnPreExecute() 处启动,如果你使用进度对话框的水平样式栏,则发布进度。 剩下的部分是优化 doInBackground 算法。

我发现这个博客帖子非常有用,它使用loopJ下载文件,它只有一个简单的功能,将对一些新的android开发者有所帮助。

当我开始学习android开发时,我已经学会了ProgressDialog是。 当文件下载时,可以调用setProgress方法来更新进度级别。

最好的我见过许多应用程序是他们定制这一进步的属性对话框中给一个更好的外观和感觉比股票版本进度对话框。 保持用户参与一些像青蛙,大象或者可爱猫/小狗的动画。 progress进度dialog对话框中的任何动画都会吸引用户,他们并不喜欢等待长时间。

我会在ProgressDialog上写一篇博文并在这里分享。

编辑:下载一个文件时,显示进度条

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

...