查看源码 AsyncTask
只是对 Thread
和 Handler
的一个封装,其中线程是使用线程池技术。
基本概念
1 | public abstract class AsyncTask<Params, Progress, Result> { |
3 个泛型参数
AsyncTask <Params, Progress, Result>
Params
指定的是我们传递给异步任务执行时的参数的类型。注意:这里是可变长参数,如果只传递了一个参数,使用时为params[0]
。Progress
指定的是我们的异步任务在执行的时候将执行的进度返回给UI
线程的参数的类型。注意:这里也是可变长参数,如果只传递了一个参数,使用时为progress[0]
。Result
指定的是异步任务执行完后返回给UI
线程的结果的类型。
我们在定义一个类继承 AsyncTask
类的时候,必须指定好这三个泛型的类型,如果都不指定的,则将其写成 Void
。
4 个执行步骤
onPreExecute()
UI Thread
当中执行,这个方法是在执行异步任务之前的时候执行,我们可以在异步任务执行前做UI
提示。doInBackground(Params... params)
这个方法就是来处理异步任务的方法,执行耗时操作。这个方法也是必须要实现的抽象方法。onProgressUpdate(Progess... values)
UI Thread
当中执行,用来更新进度条等。onPostExecute(Result... result)
UI Thread
当中执行,当异步任务执行完之后,将doInBackground
结果返回给这个方法来更新UI
。
2 种执行方式
“执行”必须在主线程中调用,而后台线程的执行方式可以为串行或者并行执行。
- 串行:
execute(Params... params)/execute(Runnable runnable)
- 并行:
executeOnExecutor(Executor exec, Params... params)
注意:各 SDK
版本 execute
默认执行方式不统一,1.5 中顺序执行,1.6 到 2.3 中并行执行,3.0 以后又改回串行执行,并添加并行执行接口 executeOnExecutor
。
注意事项
- 必须在
UI
线程中加载和创建,以及调用execute
- 不能做特别耗时的操作,建议只几秒内的异步任务
- 一个
AsyncTask
对象只能被执行一次,即只能调用一次execute
,否则会抛出异常报错:Caused by: java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
- 不能在程序中主动调用 4 个步骤
代码示例
1 | // 初始化AsyncTask及执行 |
源码解析
线程池
1 | private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); |
参数及返回结果封装
1 | private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { |
在构造方法中,传进来的参数 Params
被包装成到 mWorker
中,它是一个 Callable
。同时在 mWorker
中定义了返回结果类型 Result
,并在 call
中调用了回调方法 doInBackground
,执行具体的后台任务。最后 mWorker
包装到 FutureTask
中,当执行完毕后,通过 get()
获取执行的结果,并通知 UI
去更新。
任务执行
1 |
|
不管是串行还是并行,最终都调用了 executeOnExecutor
,这里才真正的把参数传递进来,参数赋值给 mWorker
,根据构造方法中参数封装的分析,mFuture
携带了参数和返回值类型,此时只需要调用执行器执行即可 exec.execute(mFuture);
。
串行执行
示例:mClassifierAsyncTask.execute(bitmap);
,串行执行源码分析:
1 | // 1. 串行执行,使用的都是 sDefaultExecutor |
并行执行
示例:mClassifierAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmaps);
,并行执行源码见任务执行。
1 | /** |
查看默认线程池定义,THREAD_POOL_EXECUTOR
是 static final
类型的,可以作为并行 Executor
来使用,或者用户也可以自定义。
存在的问题
Activity
屏幕旋转或销毁时,如果 AsyncTask
没有执行完毕就会存在内存泄露。特别是屏幕旋转时 AsyncTask
没有执行完毕,会导致屏幕异常。