我正在尝试写一个简单的应用,然后应用可以进行更新。为了做成这个,我需要一个简单的功能,就是可以进行下载一个文件,同时在一个ProgressDialog显示出当前的下载进度。我知道如何做ProgressDialog,但是我不知道在同一个地方怎么既下载一个文件又显示当前的进度。
有很多下载文件的方法,接下来我将贴出最常用的一些方法;究竟哪一种对你的应用来说最实用取决于你。
这个方法可以让你进行后台处理的同时更新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 ,它已经实现了上边列出的大部分功能。
如果你要从网络上下载东西,不要忘记给你的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>
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里进行文件下载操作~