AsyncTask的简单理解
AsyncTask里面有两个线程池ThreadPoolExecutor
和SerialExecutor
还有一个Handler
。,其中SerialExecutor
用来给线程排队,真正执行的是ThreadPoolExecutor
,Handler
用于线程之间的切换。给AsyncTask排队的容器是一个单向队列ArrayDeque<Runnable>
,他保存了所有将要执行的线程。先来看一下熟悉的execute
方法
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
他调用了executeOnExecutor方法,同时把用于排队的线程池和在doInBackground需要用到的参数传进去。
@MainThread
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;
}
这个方法执行在主线程,他调用了onPreExecute()
,所以说onPreExecute()
在主线程调用。然后他使用了exec.execute(mFuture);
来排队,这里的exec就是上面传过来的sDefaultExecutor
,也就是用于排队的线程池。那么他的参数mFuture
是什么呢。他是一个FutureTask
简而言之就是监控一条线程的执行状态。他在构造器里面进行初始化,我们来到构造器。
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() {
......
}
};
}
上面代码中WorkerRunnable
是一个抽象类,他封装了Params[],WorkerRunnable
也实现了Callable<Result>
接口,用于把这条线程放到FutureTask
里面进行执行。FutureTask
会自动调用WorkerRunnable
里面重写的call()
方法,而在这个call()
正有我们熟悉的doInBackground(mParams);
doInBackground();
如何处理结果
当运行结束后,返回一个result,由于他运行在子线程里面,需要把结果提交到主线程来显示,所以他会调用postResult(result);
来显示结果。线程之间的切换用的是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;
}
}
}
上面是Handler如果处理结果。运行到result.mTask.finish(result.mData[0]);
他会执行
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
看到onPostExecute(result)
了吧,他就是用于接受结果的回调方法。由于用到了Handler切换线程,所以他运行在主线程。
如何更新进度
我们都知道在子线程里面更新进度需要调用publishProgress
方法。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
首先在消息池里面获取到MESSAGE_POST_PROGRESS
消息。然后把AsyncTaskResult<Progress>
的实例放到里面。这个AsyncTaskResult<Progress>
是什么呢?
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
他是一个静态类,构造器里面封装的AsyncTask和需要被更新的值。
然后Handler
会把它发送到what为MESSAGE_POST_PROGRESS
的消息中,用来更新进度。
线程池是如何工作的。
1.排队线程池,先看代码
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);
}
}
}
他也是一个静态类,实现了Executor
,并重写了execute方法。里面维护这一个ArrayDeque
他是一个单向的队列。这里用到了他的两个方法。offer()
和poll()
,其中offer()
方法是向队列最后面插入一条数据mTasks,poll()
是从队列的最前面取出一条数据并将其删除。
他在插入的同时新建一个Runable
同时执行了传入这条线程的run()
方法。这里你也许要问,为什么一Runable
要套一个Runable
呢,既然是排队那么为什么要运行run()
方法呢?
1、如果这个Runable
如果不包含一个Runable
,那么这个传进来的Runable
就会直接执行的,注意,是执行在主线程。另外一个线程池没有起到作用。
2、这里虽然运行了run()
方法真的线程也没有执行,里面线程虽然调用了run()
方法,仅仅是调用而已,外面的Runable没有执行,里面的run()
方法就不会执行。
排队之后他会执行scheduleNext();
方法,这个方法是启动分线程的关键。他先从队列中取出Runable
,然后调用ThreadPoolExecutor
的execute()
方法来真正执行线程。
线程池的参数
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);
自己看吧,谷歌写的很明白