AsyncTask机制详解

Posted stone_you

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask机制详解相关的知识,希望对你有一定的参考价值。

  AsyncTask是android提供的一个轻量级异步任务机制,使用AsyncTask可以方便的执行异步任务,并将结果更新到main thread。AsyncTask中是通过Handler机制来让work thread和main thread通信的。如果你对Handler还不了解的话,可以通过我的这篇博客来了解Android的Handler机制。Android 异步消息处理机制
  在这篇文章中我们将了解AsyncTask的基本用法以及从源码的角度来分析AsyncTask机制,首先我们来了解下开发过程中AsyncTask的使用

AsyncTask的基本用法

AsyncTask类的基本方法:

public abstract class AsyncTask<Params, Progress, Result> {
/**
     * Override this method to perform a computation on a background thread. The
     * specified parameters are the parameters passed to {@link #execute}
     * by the caller of this task.
     *
     * This method can call {@link #publishProgress} to publish updates
     * on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return A result, defined by the subclass of this task.
     *
     * @see #onPreExecute()
     * @see #onPostExecute
     * @see #publishProgress
     */
    @WorkerThread
    protected abstract Result doInBackground(Params... params);

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

    /**
     * <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"})
    @MainThread
    protected void onPostExecute(Result result) {
    }

    /**
     * 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"})
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }
}

  从上面AsyncTask的代码可以看到,AsyncTask是一个抽象类,我们如果想要使用的话必须先创建它的子类,并实现它的抽象方法doInBackground,根据功能需要我们还可以重写onPreExecute、onPostExecute、onProgressUpdate。从注释我们就可以知道onPreExecute、onPostExecute、onProgressUpdate这几个方法是运行在main thread,doInBackground是运行在workthread中的。我们把耗时的操作放在doInBackground进行,当doInBackground执行完之后会把结果返回给onPostExecute,我们可以在onPostExecute做一些更新UI的操作。onPreExecute在doInBackground之前执行,用于执行准备工作,onProgressUpdate用来更新后台任务的执行进度。
  AsyncTask有三个泛型参数,分别是Params, Progress, Result。Params是指后台任务运行的参数,用于在doInBackground使用,Progress是用来指示后台任务执行进度的单位,Result是后台任务的返回结果,在doInBackground方法中返回,交给onPostExecute处理。这些参数不需要时可以用Void代替。
  在本文中我们自定义一个简单的AsyncTask类,代码定义如下:

public class TestAsyncTask extends AsyncTask<Void, Integer, Void> {
    private String taskName;

    public TestAsyncTask(String name) {
        super();
        taskName = name;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.v("stone", "task = " + taskName + " onPostExecute in " + Thread.currentThread().toString());
    }

    @Override
    protected Void doInBackground(Void... params) {
        Log.v("stone", "task = " + taskName + " doInBackground in " + Thread.currentThread().toString());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.toString();
        }
        return null;
    }

}

//AsyncTask的两种启动方式
new TestAsyncTask("task_" + i).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new TestAsyncTask("tasktesk_" + i).execute();

  到目前为止我我们已经大致了解了AsyncTask的使用,以及启动后台任务的两种方式,execute和executeOnExecutor。在executeOnExecutor中我们使用了AsyncTask自定义的THREAD_POOL_EXECUTOR,它是一个ThreadPoolExecutor。这两种后台任务的启动方式有什么区别呢?一个应用中最多可以new多个AsyncTask呢?多个任务是并行处理还是串行处理呢?最多同时有多少个任务在处理呢?下面我们将从源码的角度来回答上面的问题,由于AsyncTask从出现到现在已经有了多次改动,下面我的分析将是基于API 23的源码来进行的,请各位注意不同API版本的AsyncTask实现是有差异的。

AsyncTask机制详解

  通过上文我们已经知道了AsyncTask的简单用法,下面我将从源码的角度来分析,一个AsyncTask从创建到执行的过程。首先一个任何AsyncTask子类的创建都会调用AsyncTask的默认构造函数AsyncTask(),下面我们来看一下这个方法的定义:

     public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                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);
                }
            }
        };
    }

  代码很简单,初始化了mWorker 和mFuture 这两个对象,我们暂时先不管立面的具体实现,只要知道这个mFuture 会交给Executor去执行。现在AsyncTask对象已经创建好了,我们来看AsyncTask的执行过程。我们先来分析execute()方法,该方法的定义如下:

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

  execute的代码实际上直接调用了executeOnExecutor方法,并且传入了一个叫做sDefaultExecutor的Executor对象。接着我们来看下executeOnExecutor方法的定义:

    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()方法,这就解释了为什么onPreExecute方法是最新被调用的。我们把运行参数params赋给mWorker对象,mWorker实现了callable接口。把mFuture交给我们传入的Executor来执行。对面我们前文提到了AsyncTask的两种启动方式,他们执行方式的不同是因为传入的Executor不同导致的。我们前面提到过execute方法传给executeOnExecutor的是一个sDefaultExecutor对象,通过源码来看一下sDefaultExecutor是什么:

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

  我们看到sDefaultExecutor 被赋值为SERIAL_EXECUTOR,我们来看一下SERIAL_EXECUTOR是如何定义的:
  

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

  SERIAL_EXECUTOR 是一个SerialExecutor对象,我们来看一下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启动AsyncTask任务,会把任务一个一个添加到mTasks中,在执行完一个任务后才会去执行下一个任务,mTasks的大小没有限制,所以理论上通过execute启动AsyncTask任务这种方式可以创建无数个task,并且所有的task是串行执行的。这个runnable参数就是我们在AsyncTask构造函数中初始化的mFuture。我们接着再来看一下构造函数的源码:
  

     public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                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);
                }
            }
        };
    }

  我们看到在初始化mWorker 对象时,在它的call方法里面调用了doInBackground方法,执行完doInBackground之后会调用postResult(result)来传递doInBackground的返回结果。我们来分析下postResult方法看看结果是如何传递的。
  

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

  postResult方法中主要是生成了一个MESSAGE_POST_RESULT消息,并将消息发送给了target handler。现在我们就要来看看这个Handler对象是什么,就知道是把消息发送到了哪个线程中。我们来看一下getHandler()方法的实现:
  

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

  看到这里,我们知道这个handler是一个InternalHandler对象,我们接着来看一下InternalHandler的源码实现:
  

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

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

  看到这里已经很清楚,这个handler是绑定了主线程loop的handler,所以接下来的工作就从工作线程切换到主线程中去执行。在handleMessage中可以看到MESSAGE_POST_RESULT会导致调用result.mTask.finish(result.mData[0])。这个就是AsyncTask的finish方法,我们来看一下finish方法的实现:
  

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

  finish方法里面会做判断,如果 isCancelled()返回的是true,就会去执行onCancelled,否则就会调用onPostExecute方法。至此我们就走完了execute方式的启动流程。通过以上的分析,我们可以知道这种方式下AsyncTask的对象可以创建任意多个并执行,且是顺序执行的。
  对于executeOnExecutor启动方式,他的执行方式和传入的Executor相关,每个任务的执行逻辑和execute启动方式是一样的。executeOnExecutor方式中我们传入的参数是AsyncTask.THREAD_POOL_EXECUTOR,这是AsyncTask默认帮我们配置的Executor。我们来具体看下这个Executor的配置情况:
  

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 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);

  这个ThreadPoolExecutor配置如下:核心线程数为CPU核个数+1,最大线程数是2倍的CPU核个数+1,任务排队队列大小为128,所以假设我们的手机CPU核的个数为8,在核心线程数为9,最大线程数为17。所以根据ThreadPoolExecutor的特点我们可以知道,最大的任务数为128+17,超过这个数量就会抛出RejectedExecutionException。在排队队列未满之前,最多有9个线程在运行,当排队队列满了之后,最多有17个线程在运行。

以上是关于AsyncTask机制详解的主要内容,如果未能解决你的问题,请参考以下文章

如何在切换片段时停止 AsyncTask?

Asynctask结果显示重新创建片段后

片段中的 Asynctask 未到达 onPostExecute

在 AsyncTask 中将新的 TextView 设置为片段

Android 多线程-----AsyncTask详解

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段