在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制,AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。
AsyncTask是定义异步任务在后台线程(子线程中)运行的,而结果更新UI在UI线程中,接收三个参数,Params(传入的值)Progress(进度) Result(返回的结果),有四步onPreExecute(在UI线程中执行,准备操作)doInBackground(子线程中耗时操作)onProgressUpdate (进度更新)onPostExecute(结果返回)
来看下executeOnExecutor()方法:
@MainThread(主线程中调用的)
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//这里有一些异常的判断 ,由于代码量多,我就去掉了,在这里我们不考虑异常问题
//在这里首先吧状态给位RUNNING
mStatus = Status.RUNNING;
//执行前的准备工作
onPreExecute();
//这里的params就是我们传入的值,然后赋值给了mWorker,那么我们就要看看这个是什么意思了
mWorker.mParams = params;
//用线程池执行这个future
exec.execute(mFuture);
//最终返回自己
return this;
}
首先AsyncTask是一个抽象类,必须要创建它的子类。但是在继承AsyncTask类的时候,需要指定三个泛型参数。解释分别如下:
(1)Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
(2)Progress
后台任务在执行时,如果需要在界面上显示当前进度,则就用Progress指定的泛型作为进度单位。
(3)Result
当后台任务执行完毕后,如果需要对结果进行返回,那么就用这里指定的泛型作为返回的类型。
然后一般在子类中,需要重写下面的几个回调方法,他们都是自动被调用的,不要在代码中人工调用。
(1)onPreExexute()
该方法在后台任务执行前,即doInBackground方法执行前被调用。通常在这里执行一些初始化的操作,比如显示一个进度条。
(2)doInBackground(Params...)
该方法用来执行后台任务,它的所有代码都是在子线程中操作,任务一旦完成就会用return来返回结果。当然它的参数和返回结果的类型就是上面我们所指定的Params和Result。千万注意,不要在这个
方法中执行更新UI的操作。如果需要更新UI元素,比如说反馈当前任务的进度,可以调用publishProgress(Progress...)方法来完成。
(3)onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress方法后,就会自动调用该方法,方法中的参数就是publishProgress方法传递过来的。这个方法可以对UI进行更新。
(4)onPostExecute(Result)
当后台任务执行完毕,即doInBackground执行完毕后,该方法被调用。doInBackground返回的结果就是该方法的参数,这这里可以执行任务完成后的逻辑,比如说关闭一个进度条,更新一些UI等。
需要说明的是这几个方法,只有doInBackground方法是在子线程中执行的,其他的都是在主线程中执行的。那么启动和取消这个任务的方法是什么呢?如下:
myAsyncTask.execute()其中该任务
yAsyncTask.cancel()取消该任务
Android AsyncTask注意事项
1. AsyncTask对象不可重复使用,也就是说一个AsyncTask对象只能execute()一次,否则会有异常抛出"java.lang.IllegalStateException: Cannot execute task: the task is already running"
2. 在doInBackground()中要检查isCancelled()的返回值,如果你的异步任务是可以取消的话。cancel()仅仅是给AsyncTask对象设置了一个标识位,当调用了cancel()后,发生的事情只有:AsyncTask对象的标识位变了,和doInBackground()执行完成后,onPostExecute()不会被回调了,而doInBackground()和 onProgressUpdate()还是会继续执行直到doInBackground()结束。所以要在doInBackground()中不断的检查 isCancellled()的返回值,当其返回true时就停止执行,特别是有循环的时候。如上面的例子,如果把读取数据的isCancelled() 检查去掉,图片还是会下载,进度也一直会走,只是最后图片不会放到UI上(因为onPostExecute()没被回调)!这里的原因其实很好理解,想想Java SE的Thread吧,是没有方法将其直接Cacncel掉的,那些线程取消也无非就是给线程设置标识位,然后在run()方法中不断的检查标识而已。
3. 如果要在应用程序中使用网络,一定不要忘记在AndroidManifest中声明INTERNET权限,否则会报出很诡异的异常信息,比如上面的例子,如果把INTERNET权限拿掉会抛出"UnknownHostException"。刚开始很疑惑,因为模拟器是可以正常上网的,后来Google了下才发现原来是没权限,但是疑问还是没有消除,既然没有声明网络权限,为什么不直接提示无网络权限呢?对比Java SE的Thread Thread是非常原始的类,它只有一个run()方法,一旦开始,无法停止,它仅适合于一个非常独立的异步任务,也即不需要与主线程交互,对于其他情况,比如需要取消或与主线程交互,都需添加额外的代码来实现,并且还要注意同步的问题。而AsyncTask是封装好了的,可以直接拿来用,如果你仅执行独立的异步任务,可以仅实现doInBackground()。
所以,当有一个非常独立的任务时,可以考虑使用Thread,其他时候,尽可能的用 AsyncTask。