欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中通过AsyncTask类来制作炫酷进度条的实例教程

程序员文章站 2024-02-29 20:42:46
asynctask (api level 3,所以几乎所有目前在市面上流通的 android 版本皆可使用) 是除 thread 外的另一种选择,android 团队鼓励...

asynctask (api level 3,所以几乎所有目前在市面上流通的 android 版本皆可使用)
是除 thread 外的另一种选择,android 团队鼓励主执行绪(ui thread) 专注于操作 & 画面的流畅呈现,
其余工作 (如网络资料传输、档案/磁碟/资料存取) 最好都在背景执行;
thread 通常要搭配 handler 使用,而 asynctask 用意在简化背景执行 thread 程序码的撰写。

如果您预期要执行的工作能在几秒内完成,就可以选择使用 asynctask,若执行的时间很长,
android 则强烈建议采用 executor, threadpoolexecutor and futuretask。

要使用 asynctask,必定要建立一个继承自 asynctask 的子类别,并传入 3 项资料:

  • params -- 要执行 doinbackground() 时传入的参数,数量可以不止一个
  • progress -- doinbackground() 执行过程中回传给 ui thread 的资料,数量可以不止一个
  • rsesult -- 传回执行结果

若您没有参数要传入,则填入 void (注意 v 为大写)。

asynctask 的运作有 4 个阶段:

  • onpreexecute -- asynctask 执行前的准备工作,例如画面上显示进度表,
  • doinbackground -- 实际要执行的程序码就是写在这里,
  • onprogressupdate -- 用来显示目前的进度,
  • onpostexecute -- 执行完的结果 - result 会传入这里。

除了 doinbackground,其他 3 个 method 都是在 ui thread 呼叫


炫酷进度条实例
我们以一个实例来说明,“点击按钮开始下载qqandroid安装包,然后显示一个对话框来反馈下载进度”。我们先初始化一个对话框,由于要显示进度,我们用github上面一个能够显示百分比的进度条 numberprogressbar,启动任务的按钮我们使用 circlebutton,一个有酷炫动画的按钮,github上面有很多非常好的开源项目,当然炫酷的控件是其中一部分了,后面有机会,会去学习一些比较流行的控件它们的实现原理,今天就暂且拿来主义了~~。

1.先初始化进度条提示对话框。

 builder = new alertdialog.builder(
     mainactivity.this);
 layoutinflater inflater = layoutinflater.from(mainactivity.this);
 mdialogview = inflater.inflate(r.layout.progress_dialog_layout, null);
 mnumberprogressbar = (numberprogressbar)mdialogview.findviewbyid(r.id.number_progress_bar);
 builder.setview(mdialogview);
 mdialog = builder.create();

2.设置按钮点击事件。

 findviewbyid(r.id.circle_btn).setonclicklistener(new view.onclicklistener(){
   @override
   public void onclick(view v) {
     dismissdialog();
     mnumberprogressbar.setprogress(0);
     mytask = new myasynctask();
     mytask.execute(qqdownloadurl);
   }
 });

3.downloadasynctask实现,有点长。

private class downloadasynctask extends asynctask<string , integer, string> {

 @override
 protected void onpreexecute() {
   super.onpreexecute();
   mdialog.show();

 }

 @override
 protected void onpostexecute(string avoid) {
   super.onpostexecute(avoid);
   dismissdialog();
 }

 @override
 protected void onprogressupdate(integer... values) {
   super.onprogressupdate(values);

   mnumberprogressbar.setprogress(values[0]);
 }

 @override
 protected void oncancelled(string avoid) {
   super.oncancelled(avoid);
   dismissdialog();
 }

 @override
 protected void oncancelled() {
   super.oncancelled();
   dismissdialog();
 }

 @override
 protected string doinbackground(string... params) {
   string urlstr = params[0];
   fileoutputstream output = null;
   try {
     url url = new url(urlstr);
     httpurlconnection connection = (httpurlconnection)url.openconnection();
     string qqapkfile = "qqapkfile";
     file file = new file(environment.getexternalstoragedirectory() + "/" + qqapkfile);
     if (file.exists()) {
       file.delete();
     }
     file.createnewfile();
     inputstream input = connection.getinputstream();
     output = new fileoutputstream(file);
     int total = connection.getcontentlength();
     if (total <= 0) {
       return null;
     }
     int plus = 0;
     int totalread = 0;
     byte[] buffer = new byte[4*1024];
     while((plus = input.read(buffer)) != -1){
       output.write(buffer);
       totalread += plus;
       publishprogress(totalread * 100 / total);
       if (iscancelled()) {
         break;
       }
     }
     output.flush();
   } catch (malformedurlexception e) {
     e.printstacktrace();
     if (output != null) {
       try {
         output.close();
       } catch (ioexception e2) {
         e2.printstacktrace();
       }
     }
   } catch (ioexception e) {
     e.printstacktrace();
     if (output != null) {
       try {
         output.close();
       } catch (ioexception e2) {
         e2.printstacktrace();
       }
     }
   } finally {
     if (output != null) {
       try {
         output.close();
       } catch (ioexception e) {
         e.printstacktrace();
       }
     }
   }
   return null;
 }
}

这样一个简单的下载文件文件就基本实现了,到目前为止谈不上技巧,但是现在我们有一个问题,就是如果我们的activity正在后台执行一个任务,可能耗时较长,那用户可能会点击返回退出activity或者退出app,那么后台任务不会立即退出,如果asynctask内部有activity中成员变量的引用,还会造成activity的回收延时,造成一段时间内的内存泄露,所以我们需要加上下面的第四步处理。

4.onpause中判断应用是否要退出,从而决定是否取消asynctask执行。

@override
protected void onpause() {
 super.onpause();
 if (mytask != null && isfinishing()) {
   mytask.cancel(false);
 }
}

这样我们的异步任务就会在activity退出时,也随之取消任务执行,顺利被系统销毁回收,第四步很多时候会被遗漏,而且一般也不会有什么致命的问题,但是一旦出问题了,就很难排查,所以遵循编码规范还是有必要的。