2 yiranwujixian yiranwujixian 于 2012.09.21 09:55 提问

用android下载一个文件,在ProgressDialog显示下载进度

我正在尝试写一个简单的应用,然后应用可以进行更新。为了做成这个,我需要一个简单的功能,就是可以进行下载一个文件,同时在一个ProgressDialog显示出当前的下载进度。我知道如何做ProgressDialog,但是我不知道在同一个地方怎么既下载一个文件又显示当前的进度。

3个回答

liangchichexin
liangchichexin   2012.09.21 10:38
已采纳

有很多下载文件的方法,接下来我将贴出最常用的一些方法;究竟哪一种对你的应用来说最实用取决于你。

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

这个方法可以让你进行后台处理的同时更新UI(在这种情况下,我们将更新一个进度条)
这是实例代码:

// 声明对话框是你activity的成员字段
ProgressDialog mProgressDialog;

// 举例说明在onCreate中的方法
mProgressDialog = new ProgressDialog(YourActivity.this);
mProgressDialog.setMessage("A message");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

DownloadFile downloadFile = new DownloadFile();
downloadFile.execute("the url to the file you want to download");

AsyncTask像这样:

// 通常,AsyncTask子类在activity类中进行声明
// 这样,你可以很容易在这里修改UI线程
private class DownloadFile extends AsyncTask<String, Integer, String> {
    @Override
    protected String doInBackground(String... sUrl) {
        try {
            URL url = new URL(sUrl[0]);
            URLConnection connection = url.openConnection();
            connection.connect();
            //这将是有用的,这样你可以显示一个典型的0-100%的进度条
            int fileLength = connection.getContentLength();

            // 下载文件
            InputStream input = new BufferedInputStream(url.openStream());
            OutputStream output = new FileOutputStream("/sdcard/file_name.extension");

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;
                publishProgress((int) (total * 100 / fileLength));
                output.write(data, 0, count);
            }

            output.flush();
            output.close();
            input.close();
        } catch (Exception e) {
        }
        return null;
    }

上边的方法(doInBackground)一直在一个后台线程中进行。你不需要做任何UI的任务。另一方面,onProgressUpdate和onPreExecute在UI线程上运行,因此你可以改变进度条

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressDialog.show();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        super.onProgressUpdate(progress);
        mProgressDialog.setProgress(progress[0]);
    }
}

一旦文件已经下载完,如果你想要执行一些代码(例如mProgressDialog.dismiss()),你也可能想要重写onPostExecute方法。

2.从服务器下载

这个大的问题是:我如何从服务器上更新我的activity?在下个示例中,我们将使用两个你可能没有太注意过的类:ResultReceiver和IntentService. ResultReceiver允许我们从服务器上更新我们的线程的类,IntentService 是Service的一个子类,从那,它可以产生一个线程来处理后台任务(你应该知道,你的应用的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();
           // 这将是有用的,这样你可以显示一个典型的0-100%的进度条
           int fileLength= connection.getContentLength();
           // 下载文件
           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;
               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

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

Activity将像这样

// 像第一个例子那样初始化进度条

//这是你怎么启动下载器
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();
            }
        }
    }
}

3.使用DownloadManager 类(GingerBread ,而且仅仅是最新的)
这个方法是可怕的,你不用担心手动下载文件,处理线程,数据流,等等。GingerBread有一个新的特点,DownloadManager可以让你很容易的下载文件,然后将辛苦的工作给系统做。

/**
 * @ param上下文用来检查设备的版本和DownloadManager信息
 * @如果下载管理器可以用,则返回true
 */
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");
// 如果为了让它运行,你必须用android3.2编译你的应用程序
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");
// 获得下载服务和队列文件
DownloadManager manager= (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

下载进度条将在通知栏显示
结语
第一个和第二个方法仅仅是冰山一角。如果你想要你的app是健壮的,这有很多事情需要你记住。这是一个简短的列表:

  • 你必须检查用户是否有可用的网络链接。

  • 确定你有权限((INTERNET and
    WRITE_EXTERNAL_STORAGE);如果你想要检查网络是否可用也可以用ACCESS_NETWORK_STATE

  • 确保你要下载的文件的目录存在,有读写权限

  • 如果下载包太大,之前的下载失败了,你可能需要实现一种方法来恢复下载

  • 如果你允许用户中断下载,他们会很感激你。

除非你已经完全控制了下载过程,否则我强烈建议你使用DownloadManager ,它已经实现了上边列出的大部分功能。

franzhong
franzhong   2012.09.21 16:49
public class EX04_17 extends Activity 
{ 
  private TextView mTextView01;
  private Button mButton01; 
  private ProgressBar mProgressBar01; 
  public int intCounter=0; 
  /* 自定义Handler讯息代码,用以作为识别事件处理 */ 
  protected static final int GUI_STOP_NOTIFIER = 0x108; 
  protected static final int GUI_THREADING_NOTIFIER = 0x109;
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState)
{ 
super.onCreate(savedInstanceState); 
setContentView(R.layout.main); 
mButton01 = (Button)findViewById(R.id.myButton1);
mTextView01 = (TextView)findViewById(R.id.myTextView1); 
/* 设定ProgressBar widget对象 */ 
mProgressBar01 = (ProgressBar)findViewById(R.id.myProgressBar1); 
/* 调用setIndeterminate方法指派indeterminate模式为false */
mProgressBar01.setIndeterminate(false); 
/* 当按下按钮后,开始线程工作 */ 
mButton01.setOnClickListener(new Button.OnClickListener()
{

@Override
public void onClick(View v) 
{ 
  // TODO Auto-generated method stub 
  /* 按下按钮让ProgressBar显示 */ 
  mTextView01.setText(R.string.str_progress_start);
  /* 将隐藏的ProgressBar显示出来 */ 
  mProgressBar01.setVisibility(View.VISIBLE);
  /* 指定Progress为最多100 */ 
  mProgressBar01.setMax(100); 
  /* 初始Progress为0 */
  mProgressBar01.setProgress(0); 
  /* 起始一个线程 */ 
  new Thread(new Runnable() 
  { 
    public void run()
    { 
      /* 预设0至9,共执行10次的循环叙述 */ 
      for (int i=0;i<10;i++) 
      { 
        try 
        {
          /* 成员变量,用以识别加载进度 */ 
          intCounter = (i+1)*20; 
          /* 每执行一次循环,即暂停1秒 */ 
          Thread.sleep(1000); 
          /* 当Thread执行5秒后显示执行结束 */ 
          if(i==4)
          {
            /* 以Message对象,传递参数给Handler */ 
            Message m = new Message(); 
            /* 以what属性指定User自定义 */ 
            m.what = EX04_17.GUI_STOP_NOTIFIER;
            EX04_17.this.myMessageHandler.sendMessage(m); 
            break; 
            } 
          else 
          {
            Message m = new Message(); 
            m.what = EX04_17.GUI_THREADING_NOTIFIER;
            EX04_17.this.myMessageHandler.sendMessage(m); 
            }
          } 
        catch(Exception e)
        { 
          e.printStackTrace(); 
          }
        } 
      }
    }
  ).start(); 
  } 
}); 
} 


/* Handler建构之后,会聆听传来的讯息代码 */ 
  Handler myMessageHandler = new Handler() 
  { 
    // @Override
    public void handleMessage(Message msg)
    {
      switch (msg.what) 
      { 
      /* 当取得识别为 离开线程时所取得的讯息 */ 
      case EX04_17.GUI_STOP_NOTIFIER:
        /* 显示执行终了 */ 
        mTextView01.setText(R.string.str_progress_done); 
        /* 设定ProgressBar Widget为隐藏 */ 
        mProgressBar01.setVisibility(View.GONE); 
        Thread.currentThread().interrupt();
        break;
        /* 当取得识别为 持续在线程当中时所取得的讯息 */
        case EX04_17.GUI_THREADING_NOTIFIER:
          if(!Thread.currentThread().isInterrupted()) 
          {
            mProgressBar01.setProgress(intCounter); 
            /* 将显示进度显示于TextView当中 */ 
            mTextView01.setText ( getResources().getText(R.string.str_progress_start)+
                "("+Integer.toString(intCounter)+"%)\n"+
                "Progress:"+
                Integer.toString(mProgressBar01.getProgress())+
                "\n"+"Indeterminate:"+
                Boolean.toString(mProgressBar01.isIndeterminate()) ); 
            } 
          break;
          } 
      super.handleMessage(msg);
      } 
    }; 
    }

在线程的try里进行文件下载操作~

yiranwujixian
yiranwujixian 非常感谢您的回答,也解决了我的问题,不过还是第一个答案更详细。
5 年多之前 回复
niangzhi
niangzhi   2012.09.21 13:15

如果你要从网络上下载东西,不要忘记给你的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>
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!