您的位置:首页 > 产品设计 > UI/UE

为什么 AsyncTask 实例必须在UI线程里创建?

2015-09-28 20:40 826 查看
AsyncTask作为基础的多线程通信方案,在实际开发中广泛的使用。

具体的使用方式,在此不在过多说明,如果您不知道,请找度娘。

查到的资料中大多有类似的注意点:

Task的实例必须在UI thread中创建

execute方法必须在UI thread中调用

不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法

该task只能被执行一次,否则多次调用时将会出现异常

那么大家是否想过 为什么 AsyncTask 实例需要在UI thread中创建,execute方法必须在UI thread中调用?

无他,为UI更新耳~

我们知道更新UI需要在主线程中进行,而在AsyncTask的众多方法中,有很多方法,可能涉及到UI的更新,如:onPreExecute(),onProgressUpdate(),onPostExecute(), onCancelled 。

下面我们一起来看一下对应的方法定义及注释文档

onPreExecute

/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
protected void onPreExecute() {
}


说明:

onPreExecute方法在doInBackground(一般用来写耗时流程如:Http请求)前调用,通常情况下,通过覆盖( @Override) 此方法来显示空的进度条(在需要显示进度的情况下)

onProgressUpdate

/**
* 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。

onPostExecute

/**
* <p>Runs on the UI thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.</p>
*
* <p>This method won't be invoked if the task was cancelled.</p>
*
* @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列表数据。

onCancelled

/**
* <p>Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
* {@link #onCancelled(Object)}.</p>
*
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
*
* @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的流程。

InternalHandler

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 inv
cc37
oked 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);
}
}
};
}


这里面主要有两个对象mWorker、mFuture 。

正常执行流程

创建对象

此处已经创建了用于更新UI的sHandler对象。

调用execute方法

我们看一下execute方法

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}


继续

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;
}


看到了,在 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<Progress>(this, values)).sendToTarget();
}


将触发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<Result>(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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息