AsyncTask作为基础的多线程通信方案,在实际开发中广泛的使用。
具体的使用方式,在此不在过多说明,如果您不知道,请找度娘。
查到的资料中大多有类似的注意点:
那么大家是否想过 为什么 AsyncTask 实例需要在UI thread中创建,execute方法必须在UI thread中调用?
无他,为UI更新耳~
我们知道更新UI需要在主线程中进行,而在AsyncTask的众多方法中,有很多方法,可能涉及到UI的更新,如:onPreExecute(),onProgressUpdate(),onPostExecute(), onCancelled 。
下面我们一起来看一下对应的方法定义及注释文档
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
protected void onPreExecute() {
}
说明:
onPreExecute方法在doInBackground(一般用来写耗时流程如:Http请求)前调用,通常情况下,通过覆盖( @Override) 此方法来显示空的进度条(在需要显示进度的情况下)
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
*
* @see #publishProgress
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void onProgressUpdate(Progress... values) {
}
说明:
onProgressUpdate的作用为显示处理进度,通常情况下,在doInBackground中触发,通过调用publishProgress(会回调onProgressUpdate更新UI)推送进度,在onProgressUpdate中更新UI。
/**
* Runs on the UI thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.
*
* This method won't be invoked if the task was cancelled.
*
* @param result The result of the operation computed by {@link #doInBackground}.
*
* @see #onPreExecute
* @see #doInBackground
* @see #onCancelled(Object)
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void onPostExecute(Result result) {
}
说明:onPostExecute的作用是,对doInBackground的的结果进行后续整理,一般情况下,在处理中可能会进行UI的操作,如更新doInBackground方法中获取到的list列表数据。
/**
* Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
* {@link #onCancelled(Object)}.
*
* Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.
*
* @see #onCancelled(Object)
* @see #cancel(boolean)
* @see #isCancelled()
*/
protected void onCancelled() {
}
看了这几个方法后,您可能有如下想法:
上面的几个方法的确涉及到UI更新,但是与UI线程创建实例和调用execute有毛关系?
哈哈,你如果也这样想,那么咱们就是同路人,我们一起来看一下AsyncTask的源码。
通过源码,首先映入眼帘的是
/**
* 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);
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final InternalHandler sHandler = new InternalHandler();
看到这,大家立马知道了,AsyncTask内部采用了线程池技术, 内部通信似乎使用了Handler。
Handler? 这个好熟悉啊,在看MESSAGE_POST_RESULT,MESSAGE_POST_PROGRESS 两个常量,不难想象出,通过Handler sentMessage的流程。
private static class InternalHandler extends Handler {
@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;
}
}
}
说明:该对象,主要是将消息抛给UI线程,用于更新UI,finish和onProgressUpdate 为 AsyncTask 的方法,且均为UI操作方法。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask(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);
}
}
};
}
这里面主要有两个对象mWorker、mFuture 。
正常执行流程
创建对象
此处已经创建了用于更新UI的sHandler对象。
调用execute方法
我们看一下execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
继续
public final AsyncTask 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;
}
看到了,在 executeOnExecutor 中调用了onPreExecute,而此方法的功能为刷新UI操作,而刷新UI必须在主线程里面,所以execute 方法的调用,必须在主线程里面。
execute方法的具体执行流程是什么样的呢?
第一步:
onPreExecute()
在execute方法中触发
第二步:
调用doInBackground方法
触发流程
exec.execute(mFuture);
这个会回调mWork对象的call方法:
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
看到了吧,执行了doInBackground方法。
第三步:
回调onProgressUpdate 更新处理进度
触发流程:
如果在上一步的doInBackground方法中,需要更新处理进度,需要通过publishProgress方法来进行,
我们来跟一下publishProgress的源码。
/**
* 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 note be called if the task has been
* canceled.
*
* @param values The progress values to update the UI with.
*
* @see #onProgressUpdate
* @see #doInBackground
*/
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
将触发handler的MESSAGE_POST_PROGRESS分支,
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
第四步:
调用onPostExecute 或者 onCancelled 方法
触发流程如下:
@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);
}
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
触发handler的 MESSAGE_POST_RESULT 分支
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
其中,mTask为AsyncTask对象,故需要查找 finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
好了,到此为止,一个正常的AsyncTask执行过程已经完成。
通过执行过程的代码,可以看到onProgressUpdate, onPostExecute , onCancelled 的 触发是在Handler中抛出的,handler的创建线程,决定了是否可以执行UI更新操作。
总结:
AsyncTask实例必须UI线程创建的原因如下:
需要在主线程创建InternalHandler,以便onProgressUpdate, onPostExecute , onCancelled 可以正常更新UI。
AsyncTask实例的execute方法必须在主线程调用的原因如下:
保证 onPreExecute 正常的更新UI。