在Android中实现异步任务机制有两种方式,Handler和AsyncTask。

Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制,为了简化操作,Android1.5提供了工具类android.os.AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。

AsyncTask的定义

三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。不能使用基本数据类型例如int,double等

AsyncTask的使用方法

使用AsyncTask需要自定义一个类继承自AsyncTask,里面有这样几个回调函数

  • doInBackground : 必须重写,异步操作中,后台线程需要完成的任务
  • onPreExecute :  在上一个函数也就是异步操作开始前调用,通常用来完成一些初始化工作
  • onPostExecute : 当异步操作执行完毕后系统调用这个函数,通常用来显示doInBackground函数的结果
  • onProgressUpdata:在doInBackground()方法中调动publishProgress()方法更新任务进度的时候,就会调用改方法
  • onCancelled : 调用AsyncTask.cancel()方法取消线程,当线程停止掉以后回调这个函数而不回调onPostExecute ()。

上面几个回调方法中,只有doInBackgound()方法运行的新线程中,其他的方法均运行在主线程里面,在这些方法里面进行耗时操作可能会产生ANR

使用AsyncTask的注意事项

  • 异步任务的实例必须在UI线程中创建。
  • execute(Params… params)方法(执行一个异步线程)必须在UI线程中调用。
  • 不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。
  • 不能在doInBackground(Params… params)中更改UI组件的信息。因为doInBackground运行在新线程
  • 一个任务实例只能执行一次,如果执行第二次将会抛出异常。因为线程的生命周期,消亡后不能重生,Java中线程
  • 调用AsyncTask.cancel()方法并不会直接停止线程,而是等待当前线程运行完毕去回调onCancelled()方法而不调用onPostExecute ()。

上面两行红色字体是由线程的特性决定的,线程的生命周期是不能回头的,线程不能被强行结束,一般的做法是设置标志位,然后在run方法中判断标志位,Thread.interrupted()并不能中断线程。

使用AsyncTask的demo

我们先来看看效果图,这是加载一张网络上的图片

Demo下载:360云盘下载  访问密码 3b6c 环境为:Android Stdio

AsyncTaskDemo

布局文件非常简单,两个Button以及一个ProgressBar和一个ImageView,默认的情况下ProgressBar和ImageView是不可见的。

然后就是我们继承自AsyncTask的类,MyAsyncTask extends AsyncTask<String, Void, Bitmap>,三个泛型参数前面已经说过了,一个是传入的参数,这是为String类型的,是一个url,第二个为执行的进度,没有使用到,使用Void,第三个是Bitmap,是在doInBackground中加载的网络图片。

下面这段代码是给加载按钮添加点击监听消息,点击的时候加载网络图片

测试AsyncTask.cancel()方法

关于这个方法,并不是真的取消了线程的执行,而是给正在执行的线程发送了一个中断消息,也就是Thread.interrupted();

还是上面那个demo,取消MyAsyncTask中的注释,也就是在doInBackground()返回的时候让这个函数停止10秒。

然后我们点击取消加载按钮,执行AsyncTask.cancel()方法。

在logcat中打印如下语句,我们可以很清楚的看到,并不是调用cancel()方法以后就直接停止了,而是继续等待后台线程的完成。

02-02 03:21:23.630 29914-30002/com.jay.myasynctask D/dalvikvm: GC_FOR_ALLOC freed 184K, 7% free 3543K/3780K, paused 3ms, total 3ms

02-02 03:21:24.294 29914-29914/com.jay.myasynctask V/x: 点击了取消

02-02 03:21:33.750 29914-29914/com.jay.myasynctask V/x: 取消了

AsyncTask提交的任务的执行顺序

AsyncTask默认的执行的顺序是按照任务提交的顺序,除非调用AsyncTask.executeOnExecutor才是并发执行,不过此方法可能造成数据混乱。下面一个demo是验证这一点的。(源码类似,为了篇幅,可以自行下载)

Demo下载:360云盘下载 访问密码 cc43 环境为:Android Stdio

MyAsyncTask2

如图,当点击同时开始的时候,会同时启动两个AsyncTask去更新ProgressBar,我们可以很清楚的看到,只有第一个在更新,只有当第一个任务被取消或者完成以后第二个进度条才会开始更新,这就验证我上面说的,AsyncTask默认是按照任务提交的顺序来执行的。你也可以采用这个系统提供的线程池来处理你的任务AsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),默认这个线程池是并发处理任务的,也就是不按顺序来.核心为5条,最大128条,不过这种方法使用不当可能照成数据异常,比如在这个demo中,我们使用AsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),当多次点击开始按钮,也就是启动多个任务同时执行去刷新进度,这样当每一个任务进度不同的时候,进度条的显示就会异常。

AsyncTask适合处理短时间的操作,长时间的操作,比如下载一个很大的视频,这就需要你使用自己的线程来下载,不管是断点下载还是其它的.从google官方文档你也可以看到,AsyncTasks should ideally be used for short operations (a few seconds at the most.)

参考文章: liuhe688 AsyncTask执行顺序

 

 

最后修改日期: 2016年2月2日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。