在记录本文之前有参考下面大神的文章,本文也加入了一些自己的见解。旨在记录和学习一些Android知识。欢迎大家一起来讨论。
别再傻傻得认为AsyncTask只可以在主线程中创建实例和调用execute方法
一、抛出几个问题
1.AsyncTask是否只能在主线程中创建并执行呢?
2.AsyncTask中的onPreExecute()、doInBackground(String... params)、onProgressUpdate(Integer... values)、onPostExecute(String s)都执行在UI线程中吗?如果不是分别都执行在哪些线程中呢?
3.AsyncTask中的doInBackground(String... params)为什么不能直接调用doInBackground(String... params)方法而是通过publishProgress(i)?
4.如何取消正在执行的AsyncTask。
二、源码翱翔
下面我们就进行源码翱翔了。
本文代码基于安卓8.1的源码。
AsyncTask.java (frameworks\base\core\java\android\os)
1.AsyncTask有三个构造方法,因为带参数的函数为hide,所以我们平时调用不到,我们可以看见平时使用的无参数构造方法,其实调用的是public AsyncTask(@Nullable Handler handler) 方法传入的looper对象是null。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
//因为callbackLooper为空,所以mHandler=getMainHandler();
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//调用postResult方法送出结果
postResult(result);
}
return result;
}
};
//FutureTask创建线程
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);
}
}
};
}
2.在第三个构造方法中,因为callbackLooper为空,所以mHandler=getMainHandler();
该方法为AsyncTask.class线程安全做了同步,保证一个进程中所有AsyncTask实例只有一个Handler实例,且只有应用在第一次实例化AsyncTask时才会创建Handler对象。
知识点来了,1首先InternalHandler里面用的是Looper.getMainLooper()主线程里面的Looper,2.mHandler和sHandler都是一样的,都是利用主线程的Looper,自然回调也应该在主线程。这两点可以帮助我们后面确认一些方法是运行在哪些线程中的
//单例模式
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
3.AsyncTask的执行
使用AsyncTask的execute方法来执行,我们来看一下源码
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
executeOnExecutor方法传入了参数和一个线程池sDefaultExecutor。sDefaultExecutor是一个串行任务队列线程池。这也就是为什么调用excute()方法,会串行执行任务了。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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);
}
}
}
从ArrayDeque队列中取出任务后放入THREAD_POOL_EXECUTOR执行。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
//线程池的创建
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
我们在来看之前的方法
@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;
//AsyncTask中创建的mFuture
exec.execute(mFuture);
return this;
}
最开始会判断mStatus的状态,如果RUNNING或者FINISHED都会抛出异常的。所以一个AsyncTask只能调用execute方法一次。然后调用onPreExecute()方法,(所以execute()方法在哪个线程调用,onPreExecute()就会在哪个线程执行)并且将传入的params传给mWorker中,然后调用线程池的execute方法传入mFuture(mFuture中传入的mWorker里面会执行doInBackground()方法,所以doInBackground会在新创建的线程中执行)(这也就是为什么doInBackground可以执行耗时操作)。任务执完成后发送消息。Handler接收到根据message.what的类型分别调用finish方法和onProgressUpdate方法。调用finish方法再会根据是否取消状态来调用onPostExecute和onCancelled方法,至此工作流程结束。
4.发送执行结果postResult。这里面会发送一个消息,值得注意的这个getHandler()方法,mHandler和sHandler都是一样的,都是利用主线程的Looper,自然回调也应该在主线程。这两点可以帮助我们后面确认一些方法是运行在哪些线程中的
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//主线程的Handler
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
//前面有分析
private Handler getHandler() {
return mHandler;
}
5.接受消息MESSAGE_POST_RESULT,这个我们在之前也说过1首先InternalHandler里面用的是Looper.getMainLooper()主线程里面的Looper。
sHandler = new InternalHandler(Looper.getMainLooper();
重要知识点来了,所以onProgressUpdate()和onPostExecute()运行在主线程中可以刷新UI,
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
//主线程
onPostExecute(result);
}
//更新状态
mStatus = Status.FINISHED;
}
6.因为doInBackground执行在新创建的线程中,如果想操作在主线程操的onProgressUpdate只能使用publishProgress。里面也是使用主线程的Looper,自然回调也应该在主线程,所以调用主线中的onProgressUpdate。
/**
* This method can be invoked from {@link #doInBackground} to
* publish updates on the UI thread while the background computation is
* still running. Each call to this method will trigger the execution of
* {@link #onProgressUpdate} on the UI thread.
*
* {@link #onProgressUpdate} will not be called if the task has been
* canceled.
*
* @param values The progress values to update the UI with.
*
* @see #onProgressUpdate
* @see #doInBackground
*/
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
三、总结
1.所以AsyncTask是Handler+线程池构造成的。
2.AsyncTask是可以在子线程中声明,并执行的。因为AsyncTask里面使用的Handler都是主线程的,也接受不到子线程的。
3.onPreExecute()的执行线程跟调用excute()方法所在的线程有关。
doInBackground(String... params)执行的线程是AsyncTask新创建的线程
onProgressUpdate(Integer... values)主线程中,可刷新UI
onPostExecute(String s)主线程中,可刷新UI
4.AsyncTask中的doInBackground(String... params)为什么不能直接调用doInBackground(String... params)方法而是通过publishProgress(i)?
这个在文中有解答。
5. 如何取消正在执行的AsyncTask。
调用cancel(true)方法,但是从cancel源码可知AsyncTask的cancel调用了FutrueTask的cancel,归根结底是调用了线程的interrupt()方法,而interrupt()方法不会真正的中断一个正在运行的线程,而是发出中断请求。当然我们可以在自己的代码中加入判断操作来中断操作。
@Override
protected String doInBackground(String... params) {
String str = "执行完毕";
for (int i = 0; i <= 20 ; i++) {
//加入判断
if (isCancelled()) {
break;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return str;
}