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

AsyncTask作为基础的多线程通信方案,在实际开发中广泛的使用。
具体的使用方式,在此不在过多说明,如果您不知道,请找度娘。
查到的资料中大多有类似的注意点:

  1. Task的实例必须在UI thread中创建
  2. execute方法必须在UI thread中调用
  3. 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
  4. 该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
    /**
     * 

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列表数据。

  • onCancelled
    /**
     * 

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的流程。

  • 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 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(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(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。

你可能感兴趣的:(Android,随笔)