【转载自 http://blog.csdn.net/yanbober 】
1 背景
Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点。前面我们分析了Handler异步机制原理(不了解的可以阅读我的《Android异步消息处理机制详解及源码分析》文章),这里继续分析Android的另一个异步机制AsyncTask的原理。
当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大。为了提高性能我们使用AsyncTask实现异步处理(其实也是线程和handler组合实现),因为其内部使用了java提供的线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数,还有一些针对性的对池的优化操作。所以说AsyncTask是Android为我们提供的方便编写异步任务的工具类。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
2 实例演示
先看下使用AsyncTask模拟下载的效果图:
看下代码,如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new TestAsyncTask(this).execute();
}
static final class TestAsyncTask extends AsyncTask<Void, Integer, Boolean> {
//如上三个泛型参数从左到右含义依次为:
//1. 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
//2. 后台任务执行时,如果需要在界面上显示当前的进度,则使用这个。
//3. 当任务执行完毕后,如果需要对结果进行返回,则使用这个。
private Context mContext = null;
private ProgressDialog mDialog = null;
private int mCount = 0;
public TestAsyncTask(Context context) {
mContext = context;
}
//在后台任务开始执行之间调用,用于进行一些界面上的初始化操作
protected void onPreExecute() {
super.onPreExecute();
mDialog = new ProgressDialog(mContext);
mDialog.setMax(100);
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.show();
}
//这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务
protected Boolean doInBackground(Void... params) {
while (mCount < 100) {
publishProgress(mCount);
mCount += 20;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
//当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mDialog.setProgress(values[0]);
}
//当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean && mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
}
}
可以看见Android帮我们封装好的AsyncTask还是很方便使用的,咱们不做过多说明。接下来直接分析源码。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
3 Android5.1.1(API 22)AsyncTask源码分析
通过源码可以发现AsyncTask是一个抽象类,所以我们在在上面使用时需要实现它。
那怎么下手分析呢?很简单,我们就依据上面示例的流程来分析源码,具体如下。
3-1 AsyncTask实例化源码分析
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
看见注释没有,AsyncTask的实例化只能在UI线程中。然后整个构造函数就只初始化了两个AsyncTask类的成员变量(mWorker和mFuture)。mWorker
为匿名内部类的实例对象WorkerRunnable(实现了Callable接口),mFuture为匿名内部类的实例对象FutureTask,传入了mWorker作为形参(重写了
FutureTask类的done方法)。
3-2 AsyncTask的execute方法源码分析
正如上面实例一样,得到AsyncTask实例化对象之后就执行了execute方法,所以看下execute方法的源码,如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看见,execute调运了executeOnExecutor方法,executeOnExecutor方法除过传入了params形参以外,还传入了一个static的SerialExecutor对象
(SerialExecutor实现了Executor接口)。继续看下executeOnExecutor源码,如下:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
首先判断AsyncTask异步任务的状态,当处于RUNNING和FINISHED时就报IllegalStateException非法状态异常。由此可以看见一个AsyncTask的
execute方法只能被调运一次。接着看见17行onPreExecute();没有?看下这个方法源码,如下:
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
protected void onPreExecute() {
}
空方法,而且通过注释也能看见,这不就是我们AsyncTask中第一个执行的方法吗?是的。
回过头继续往下看,看见20行exec.execute(mFuture);代码没?exec就是形参出入的上面定义的static SerialExecutor对象(SerialExecutor实现了Executor接口),所以execute就是SerialExecutor静态内部类的方法喽,在执行execute方法时还传入了AsyncTask构造函数中实例化的第二个成员变量mFuture。我们来看下SerialExecutor静态内部类的代码,如下:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
在源码中可以看见,SerialExecutor在AsyncTask中是以常量的形式被使用的,所以在整个应用程序中的所有AsyncTask实例都会共用同一个
SerialExecutor对象。接着可以看见,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的最后,然后判断mActive对象是不是等于null,第一次运行是null,然后调用scheduleNext()方法,在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如果再有新的任务被执行时就等待上一个任务执行完毕后才会得到执行,所以说同一时刻只会有一个线程正在执行,其余的均处于等待状态,这就是SerialExecutor类的核心作用。
我们再来看看上面用到的THREAD_POOL_EXECUTOR与execute,如下:
public abstract class AsyncTask<Params, Progress, Result> {
......
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
......
}
看见没有,实质就是在一个线程池中执行,这个THREAD_POOL_EXECUTOR线程池是一个常量,也就是说整个App中不论有多少AsyncTask都只有这
一个线程池。也就是说上面SerialExecutor类中execute()方法的所有逻辑就是在子线程中执行,注意SerialExecutor的execute方法有一个Runnable参数
,这个参数就是mFuture对象,所以我们看下FutureTask类的run()方法,如下源码:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
看见没有?第7行的c = callable;其实就是AsyncTask构造函数中实例化FutureTask对象时传入的参数mWorker。12行看见result = c.call();
没有?
其实就是调运WorkerRunnable类的call方法,所以我们回到AsyncTask构造函数的WorkerRunnable匿名内部内中可以看见如下:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
看见没有?在postResult()方法的参数里面,我们可以看见doInBackground()方法。所以这验证了我们上面例子中使用的AsyncTask,首先在主线程执
行onPreExecute方法,接着在子线程执行doInBackground方法,所以这也就是为什么我们可以在doInBackground()方法中去处理耗时操作的原因了
,接着等待doInBackground方法耗时操作执行完毕以后将返回值传递给了postResult()方法。所以我们来看下postResult这个方法的源码,如下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
先看下这个getHandler拿到的是哪个Handler吧,如下:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
看见没有,拿到的是MainLooper,也就是说在在UI线程中的Handler(不清楚的请阅读《Android异步消息处理机制详解及源码分析》文章)。所以
上面的方法其实就是将子线程的数据发送到了UI来处理,也就是通过MESSAGE_POST_RESULT在handleMessage来处理。所以我们继续看
handleMessage中的result.mTask.finish(result.mData[0]);就会发现finish的代码如下:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
看见没有?依据返回值true与false回调AsyncTask的onPostExecute或者onCancelled方法。
到此是不是会好奇onProgressUpdate方法啥时候调运的呢?继续往下看可以发现handleMessage方法中的MESSAGE_POST_PROGRESS不就是回调我们UI Thread中的onProgressUpdate方法吗?那怎么样才能让他回调呢?追踪MESSAGE_POST_PROGRESS消息你会发现如下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
额,没意思了。这不就是我们上面例子中的在子线程的doInBackground耗时操作中调运通知回调onProgressUpdate的方法么。
看见没有,AsyncTask的实质就是Handler异步消息处理机制(不清楚的请阅读《Android异步消息处理机制详解及源码分析》文章),只是对线程做了优化处理和封装而已。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
4 为当年低版本AsyncTask的臭名正身
接触Android比较久的可能都知道,在Android 3.0之前是并没有SerialExecutor这个类的(上面有分析)。那些版本的代码是直接创建了指定大小的线程池常量来执行task的。其中MAXIMUM_POOL_SIZE = 128;,所以那时候如果我们应用中一个界面需要同时创建的AsyncTask线程大于128(批量获取数据,譬如照片浏览瀑布流一次加载)程序直接就挂了。所以当时的AsyncTask因为这个原因臭名昭著。
回过头来看看现在高版本的AsyncTask,是不是没有这个问题了吧?因为现在是顺序执行的。而且更劲爆的是现在的AsyncTask还直接提供了客户化实现Executor接口功能,使用如下方法执行AsyncTask即可使用自定义Executor,如下:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
......
return this;
}
可以看出,在3.0以上版中AsyncTask已经不存在那个臭名昭著的Bug了,所以可以放心使用了,妈妈再也不用担心我的AsyncTask出Bug了。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
5 AsyncTask与Handler异步机制对比
前面文章也分析过Handler了,这里也分析了AsyncTask,现在把他们两拽一起来比较比较。具体如下:
AsyncTask是对Handler与Thread的封装。
AsyncTask在代码上比Handler要轻量级别,但实际上比Handler更耗资源,因为AsyncTask底层是一个线程池,而Handler仅仅就是发送了一个消息队列。但是,如果异步任务的数据特别庞大,AsyncTask线程池比Handler节省开销,因为Handler需要不停的new Thread执行。
AsyncTask的实例化只能在主线程,Handler可以随意,只和Looper有关系。
6 AsyncTask总结
到此整个Android的AsyncTask已经分析完毕,相信你现在对于AsyncTask会有一个很深入的理解与认识了。
什么是 AsyncTask
AsyncTask 是一种轻量级异步任务类,内部封装了 Thread 和 Handler,但是它不适合做特别耗时的后台任务(建议采用线程池)。
实现异步通常有两种方式(现在很火的 RxJava 也是一种实现异步的方式):
- Thread 配合 Handler
- AsyncTask
前者控制精细,但代码臃肿,且异步任务较多时不易控制,因此 Android 提供了 AsyncTask 类来实现异步,AsyncTask 是对 Handler,Thread 和线程池的良好封装。
AsyncTask 的具体使用不再赘述。
解析 AsyncTask
AsyncTask 内部线程池的配置
既然 AsyncTask 内部封装了线程池,那么我们可以很清楚的看到线程池的配置:
– 核心线程数 = CPU核心数 + 1
– 最大线程数 = 2 * CPU核心数 + 1
– keepAliveTime = 1 s
– 任务队列长度为 128
为什么必须在主线程中加载 AsyncTask?
首先这句话不是绝对的,我们看一下不同版本 AsyncTask 的源码就知道为什么了。
先上结论:API 16 以前必须在主线程加载 AsyncTask,API 16 以后就不用了。
首先,所有版本的 AsyncTask 都是在成员位置直接声明并创建了 Handler 对象,它是一个静态变量,也就是说它是在类加载的时候创建的:
1
2
3
|
private static final InternalHandler sHandler = new
InternalHandler();
|
API 16 以前的 AsyncTask
在 Android 4.1(API 16)之前,如果是在子线程加载的 AsyncTask,那么就会导致静态的 Handler 对象创建并初始化(如上),而 Handler 默认采用的是当前线程的 Looper,于是 Handler 用了子线程的 Looper,处理消息时也就无法切换到主线程,而是被 Handler post 到创建它的那个线程执行(即子线程)。
所以那个年代,我们不能在子线程加载 AsyncTask。
API 16 以后,API 22 之前的 AsyncTask
在 API 16 以后,API 22 之前,Google 进行了改动:在 ActivityThread 的 main 函数里面(App启动的入口),直接调用了 AsyncTask 的 init() 方法,该方法代码如下:
1
2
3
4
|
public static void init() {
sHandler.getLooper();
}
|
main 函数运行在主线程,这样就确保了 AsyncTask 类是在主线程加载。因此 AsyncTask 的 Handler 用的也是主线程的 Looper。
所以在 API 16 以后,我们是可以在子线程中加载 AsyncTask的。但是不管我们需不需要 AsyncTask,它都会在我们 App 启动时就加载,那如果我们完全没有用到它,岂不是很浪费资源?
API 22 之后
在 API 22 之后 Google 又换了一种实现:
删除了 AsyncTask 的 init 方法,main 函数中也不再调用它。
在 AsyncTask 成员变量位置仅仅声明了静态的 Handler,并没有创建对象:
1
2
|
private static InternalHandler sHandler; // 还去掉了final关键字
|
在 Handler 的实现中,添加了一个无参构造:
1
2
3
4
|
public InternalHandler() {
super(Looper.getMainLooper());
}
|
在 Handler 的实现中,添加了 getHandler() 方法:在需要 Handler 时会调用此方法。
1
2
3
4
5
6
7
8
9
|
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
|
可以看到,这样既保证了 Handler 采用主线程的 Looper 构建,又使得 AsyncTask 在需要时才被加载。
以上,Google 在 API 16 以后通过不同的实现,确保了线程的正常切换。注意一点,onPreExecute()不受 Handler 影响,运行在执行 execute 方法的线程,原因我们后面分析。
AsyncTask 是并行执行的吗?
既然封装了线程池,那么 AsyncTask 执行任务是并行的吗?
结论是:在Android 1.6 之前,是串行执行,从 Android 1.6 开始采用线程池处理并行任务。在 3.0 以后默认串行执行,但提供了 executeOnExecutor()方法并行执行。
为什么默认不并行?
因为 AsyncTask 内部的线程池是静态、单例的,所以在同一进程中所有用到 AsyncTask 的地方都是同一个线程池,如果我们创建了多个 AsyncTask 且在其中访问了共同资源,并且没做同步处理,那么并行时就会出现同步问题。Google 可能觉得这很麻烦,为了避免各种问题,便在 3.0 以后改成了默认串行执行。
如果我们需要并行执行,并自己处理了同步问题,那么可以使用 executeOnExecutor(AsyncTask.THREADPOOLEXECUTOR, Params…..)来并行执行任务,第一个参数这里用的是 AsyncTask 自带的线程池,当然也可以传入自己写的线程池。注意,这个方法是API 11 引入的。
AsyncTask 的缺点
通过以上分析,我们可以发现 AsyncTask 具有如下缺点:
容易崩溃。AsyncTask里面的线程池的核心线程数是 CPU + 1 个(4.4以前是5个),最大线程数是 CPU * 2 + 1 个,任务缓存队列长度为 128。任务缓存满时,使用默认的 Handler 处理(即抛异常)。因为同一个进程里所有用到 AsyncTask 的地方都是同一个线程池,导致任务缓存队列就容易满,一旦满了,再往里面添任务,就抛异常挂掉了。
资源浪费。在 AsyncTask 全部执行完毕之后,进程中还是会常驻 corePoolSize 个线程
必须在主线程中加载,不然在API 16以下完蛋
默认是串行执行任务。API 11 以后才能通过 executeOnExecutor 方法来并行执行
实际使用中我还发现了一些缺点:
一个 AsyncTask 对象只能执行一次,多次执行就报错。
必须在主线程执行 execute 方法,不然会导致 onPreExecute 不在主线程执行
出现错误只能在 doInBackground 里 try…catch。后续处理也很麻烦
cancel 方法不好用。调用 cancel 后只是不会去执行 onPostExecute 和 onProgressUpdate,而任务依然在 doInBackGround 中执行。调用cancel(true)虽然会使任务尽早结束,但是如果 doInBackground() 中有不可打断的方法就失效了,比如BitmapFactory.decodeStream()。
结果丢失。屏幕旋转等原因造成 AsyncTask 销毁并重建时,正在运行的 AsyncTask 持有的是之前的 Activity 实例,这样任务执行完后 onPostExecute 方法就没用了。
AsyncTask 工作原理
分析工作原理,那我们就从执行任务的起点:execute 方法开始分析,该方法就一行代码:
1
2
|
return executeOnExecutor(sDefaultExecutor, params);
|
可以看到这里使用了 AsyncTask 自带的线程池。这里先提一下:sDefaultExecutor 是个串行的线程池,一个进程的所有 AsyncTask 都在该线程池排队执行。
再来看看 executeOnExecutor 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw …..
case FINISHED:
throw …..
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
|
可以看到,该方法先进行了一些状态上的判断和更新,如果该任务正在执行或已经完成,都会抛异常。然后就调用了 onPreExecute 方法。这段代码充分解释了为什么必须在主线程执行 execute 方法,以及为什么一个 AsyncTask 对象只能执行一次。
然后它又调用了线程池的 execute 执行任务,并传入了 mFuture 对象(这是一个 FutureTask 对象)。
接下来再看看这个线程池的 execute 方法是如何执行的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
|
可以看到,传进来的 mFuture 被当做 Runnable 使用。这里的 mTasks 就是任务队列,调用 offer 方法将 mFuture 对象插入到任务队列尾部。如果没有正在执行的任务,就调用 scheduleNext 执行下一个任务,finally 语句块确保了每个任务执行完后自动执行下一个任务。这段代码证明了 AsyncTask 默认串行执行任务。
等等,这个 sDefaultExecutor 只是给任务进行排队了啊,并没有真正的执行任务!
那我们再进入 scheduleNext 方法看看是怎么回事。
1
2
|
THREAD_POOL_EXECUTOR.execute(mActive);
|
该方法内部调用了 THREADPOOLEXECUTOR 这个线程池来执行,这个线程池才是真正执行任务的,我们再来看看这个线程池的构造。
1
2
|
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
|
可以看到该线程池的配置:CPU数 + 1 的核心线程数,128 长度的工作队列等等。
任务的执行就这么开始了。我们再来看看 AsyncTask 的构造方法,了解一下那个 mFuture 是个啥。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
|
构造 mFuture 时传入了 mWorker 对象,而通过查看 FutureTask 源码可以看到,它的 run 方法会调用这个 mWorker 的 call 方法,线程池最终执行任务时就是调用的这个 call 方法。
再来看看这个 mWorker 的 call 方法是如何执行的:先执行 mTaskInvoked.set(true);将该任务标记为已经被调用。然后执行 doInBackGround 传入参数并执行任务。然后将返回值作为参数,调用 postResult 方法。postResult 方法不再贴代码了,它内部就是创建了一个 Message,将当前 AsyncTask 对象和形参 result 包装起来作为 obj,然后调用 Handler 发送消息。
运行在主线程的 Handler 接收到这个消息时会调用 AsyncTask 的 finish 方法。
1
2
3
4
5
6
7
8
9
|
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
|
该方法根据 AsyncTask 是否被取消,取消就执行 onCancelled(),没取消就执行 onPostExecute(),然后更新状态为 finish。至此 AsyncTask 的整个执行流程就走完了。
哦对了,还有 onProgressUpdate 去哪了?。。因为我们更新进度都是在 doInBackGround 方法中手动调用 publishProgress 方法来更新的,那么我们看一下这个方法:
1
2
3
4
5
6
7
|
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
|
很简单,就是在任务未被取消时创建 Message,将当前 AsyncTask 对象和形参 progress 包装起来作为 obj,然后用 Handler 发送。运行在主线程的 Handler 接收到该消息后就调用了抽象的 onProgressUpdate 方法。
优化 AsyncTask
这是我平常使用 AsyncTask 时对其进行的一些优化(现在已经不怎么用它了),是直接在最新的 API AsyncTask 类基础上进行的优化:
- 在 executeOnExecutor 方法中对 exec.execute(mFuture); 进行 try…catch
1
2
3
4
5
6
7
8
|
try {
exec.execute(mFuture);
} catch (RejectedExecutionException e) {
// Report.report()
// report this exception to crash backend;
e.printStackTrace();
}
|
- 取消任务队列的大小限制: 这里不传入参数表示队列长度为 Integer 的上限,可以视为无上限了。
1
2
|
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>();
|
- 适当提高 keepAliveTime