AsyncTask 调用过程分析

        最近看到一个AsyncTask类,能够简单的使用异步来操作UI线程,而不用我们去操作线程或者handler。

类的声明:public abstract class AsyncTask<Params, Progress, Result>;

首先是这个类的使用方法。

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
      protected Long doInBackground(URL... urls) {
          int count = urls.length;
          long totalSize = 0;
          for (int i = 0; i < count; i++) {
              totalSize += Downloader.downloadFile(urls[i]);
              publishProgress((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
      protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
          showDialog("Downloaded " + result + " bytes");
      }
  }

当我们需要使用的时候需要继承AsyncTask这个类,这个类的三个泛型参数:Params是需要传入的参数,没有极为Void;Progress是指后台完成进度的类型,主要是提供给onProgressUpdate方法来使用的;第三个参数是返回值的类型。

主要实现的4个函数(按照调用顺序):onPreExecute、doInBackground、onProgressUpdate、onPostExecute。根据函数的名字大概也知道:第一个为在后台线程执行前做的事情;第二个为后台进程做的事情;第三个是更新当前的执行进度(由doInBackground中publishprogress手动调用);最后一个为执行完毕时的方法。其中这些方法只有doInBackground方法是执行在非UI线程的,其他方法均执行在UI线程,所以就很方便的让我们在这些方法里来控制UI线程的显示等等。

启动这个Task也是非常简单,只需要在UI线程里面调用new DownloadFilesTask().execute(url1, url2, url3); 即该类的execute(params)方法。这个时候这个异步工具类就会将你需要完成的事情自动做完了。

这个Task是一个可以被中断的,具体方法是,在合适的地方调用

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }
其中参数表示是否中断被执行的任务,这个函数并没有真正的去中断一个任务,而是发出了一个信号。

在doInBackground等方法中,我们可以通过isCancelled()方法来获得当前是否被取消。

如上面例子中的:

          for (int i = 0; i < count; i++) {
              totalSize += Downloader.downloadFile(urls[i]);
              publishProgress((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
             <span style="color:#ff0000;"> if (isCancelled()) break;</span>
          }



附上一堆谷歌的介绍:

 <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
 perform background operations and publish results on the UI thread without
 having to manipulate threads and/or handlers.</p>

 <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
 and does not constitute a generic threading framework. AsyncTasks should ideally be
 used for short operations (a few seconds at the most.) If you need to keep threads
 running for long periods of time, it is highly recommended you use the various APIs
 provided by the <code>java.util.concurrent</code> package such as {@link Executor},
 {@link ThreadPoolExecutor} and {@link FutureTask}.</p>

 <p>An asynchronous task is defined by a computation that runs on a background thread and
 whose result is published on the UI thread. An asynchronous task is defined by 3 generic
 types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
 and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
 <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>

 <div class="special reference">
 <h3>Developer Guides</h3>
 <p>For more information about using tasks and threads, read the
 <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
 Threads</a> developer guide.</p>
 </div>

 <h2>Usage</h2>
 <p>AsyncTask must be subclassed to be used. The subclass will override at least
 one method ({@link #doInBackground}), and most often will override a
 second one ({@link #onPostExecute}.)</p>

 <p>Here is an example of subclassing:</p>
 <pre class="prettyprint">
 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
      protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
      }
  }
  </pre>
 
  <p>Once created, a task is executed very simply:</p>
  <pre class="prettyprint">
  new DownloadFilesTask().execute(url1, url2, url3);
  </pre>
 
  <h2>AsyncTask's generic types</h2>
  <p>The three types used by an asynchronous task are the following:</p>
  <ol>
      <li><code>Params</code>, the type of the parameters sent to the task upon
      execution.</li>
      <li><code>Progress</code>, the type of the progress units published during
      the background computation.</li>
      <li><code>Result</code>, the type of the result of the background
      computation.</li>
  </ol>
  <p>Not all types are always used by an asynchronous task. To mark a type as unused,
  simply use the type {@link Void}:</p>
  <pre>
  private class MyTask extends AsyncTask<Void, Void, Void> { ... }
  </pre>
 
  <h2>The 4 steps</h2>
  <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
  <ol>
      <li>{@link #onPreExecute()}, invoked on the UI thread before the task
      is executed. This step is normally used to setup the task, for instance by
      showing a progress bar in the user interface.</li>
      <li>{@link #doInBackground}, invoked on the background thread
      immediately after {@link #onPreExecute()} finishes executing. This step is used
      to perform background computation that can take a long time. The parameters
      of the asynchronous task are passed to this step. The result of the computation must
      be returned by this step and will be passed back to the last step. This step
      can also use {@link #publishProgress} to publish one or more units
      of progress. These values are published on the UI thread, in the
      {@link #onProgressUpdate} step.</li>
      <li>{@link #onProgressUpdate}, invoked on the UI thread after a
      call to {@link #publishProgress}. The timing of the execution is
      undefined. This method is used to display any form of progress in the user
      interface while the background computation is still executing. For instance,
      it can be used to animate a progress bar or show logs in a text field.</li>
      <li>{@link #onPostExecute}, invoked on the UI thread after the background
      computation finishes. The result of the background computation is passed to
      this step as a parameter.</li>
  </ol>
  
  <h2>Cancelling a task</h2>
  <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
  this method will cause subsequent calls to {@link #isCancelled()} to return true.
  After invoking this method, {@link #onCancelled(Object)}, instead of
  {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
  returns. To ensure that a task is cancelled as quickly as possible, you should always
  check the return value of {@link #isCancelled()} periodically from
  {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
 
  <h2>Threading rules</h2>
  <p>There are a few threading rules that must be followed for this class to
  work properly:</p>
  <ul>
      <li>The AsyncTask class must be loaded on the UI thread. This is done
      automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
      <li>The task instance must be created on the UI thread.</li>
      <li>{@link #execute} must be invoked on the UI thread.</li>
      <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
      {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
      <li>The task can be executed only once (an exception will be thrown if
      a second execution is attempted.)</li>
  </ul>
 
  <h2>Memory observability</h2>
  <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
  operations are safe without explicit synchronizations.</p>
  <ul>
      <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
      in {@link #doInBackground}.
      <li>Set member fields in {@link #doInBackground}, and refer to them in
      {@link #onProgressUpdate} and {@link #onPostExecute}.
  </ul>
 
  <h2>Order of execution</h2>
  <p>When first introduced, AsyncTasks were executed serially on a single background
  thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
  to a pool of threads allowing multiple tasks to operate in parallel. Starting with
  {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
  thread to avoid common application errors caused by parallel execution.</p>
  <p>If you truly want parallel execution, you can invoke
  {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
  {@link #THREAD_POOL_EXECUTOR}.</p>



接下来,我们看看这个类的调用过程:

这个类都是从ui线程调用它的execute方法启动的。

    /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>Note: this function schedules the task on a queue for a single background
     * thread or pool of threads depending on the platform version.  When first
     * introduced, AsyncTasks were executed serially on a single background thread.
     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
     * executed on a single thread to avoid common application errors caused
     * by parallel execution.  If you truly want parallel execution, you can use
     * the {@link #executeOnExecutor} version of this method
     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
     * on its use.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     *
     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
     * @see #execute(Runnable)
     */
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

接下来调用了:


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

      <span style="color:#ff0000;">  onPreExecute();</span>

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

这里我们可以看到;

1,一个task不能被连续启动,否则会抛出IllegalStateException的异常。

2、这些方法都有@MainThread标签说明都在主线程里面执行。

3、这里出现了三个新的对象:mWorker、mFuture和sDefaultExecutor

4、在这里(主线程中)onPreExecute方法被调用。


简单看一下三个新对象:

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    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);
            }
        }
    }
程序会调用这个对象的execute方法,这里启动了mFuture这个线程。

这个时候我们可以看看这个类的构造函数了:

    /**
     * Creates a new asynchronous task. This constructor must be invoked 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
                <span style="color:#ff0000;">Result result = doInBackground(mParams);</span>
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

        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);
                }
            }
        };
    }
另外两个对象都是在这里被创建的,其中mFuture启动(离开主线程)的时候会调用到他的callback的call方法,也就是mworker的call方法。

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                   //这里调用了call方法
                    <span style="color:#ff0000;">result = c.call();</span>
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

所以这里就调用到了mworker中的call方法,即调用doInBackground方法了。之后调用了:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
发送了一个消息MESSAGE_POST_RESULT:

                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
         <span style="color:#ff0000;">   onPostExecute(result);</span>
        }
        mStatus = Status.FINISHED;
    }
到这里调用onPostExecute方法,将状态改为FINISHED,调用结束。


如果我们要更新后台线程完成进度,那么我们会在doInBackground方法中调用publishProgress(Progress... values) ():

    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

这里讲发送一条消息,并将values传递过去。

                case MESSAGE_POST_PROGRESS:
                    result.mTask.<span style="color:#ff0000;">onProgressUpdate(result.mData);</span>
                    break;


    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

至此,这几个主要方法的调用过程结束。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值