AsyncTask 源码分析

wxuande 2019-06-21

AsyncTask 分析

位置:\frameworks\base\core\java\android\os\AsyncTask.java

AsyncTask简介

在Android中,所有的UI更新操作必须要在主线程中进行,而耗时操作(如访问网络、加载图片、IO操作等)需要在子线程中进行,线程间的交互需要借助Android的异步消息处理机制,也就是Handler。但是为了更方便地让子线程与主线程交互,更新UI元素,Android系统提供了轻量级的异步任务类AsyncTask。

AsyncTask的用法

AsyncTask的声明如下:

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象类,同时也是一个泛型类。在使用时需要创建子类去继承它,并指定相应的泛型类型参数。三个参数的含义

  • Params
    执行AsyncTask时需要传入的参数,用于执行后台任务。

  • Progress
    显示任务进度单位。

  • Result
    任务执行完毕后返回的结果

一个简单的AsyncTask使用示例:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
                int downloadPercent = doDownload();
                publishProgress(downloadPercent);
                if (downloadPercent >= 100) {
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        progressDialog.setMessage("当前下载进度:" + values[0] + "%");
    }

    @Override
    protected void onPostExecute(Boolean result) {
        progressDialog.dismiss();
        if (result) {
            Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
        }
    }
}
  1. onPreExecute
    此方法会在后台任务执行前被调用,用于进行一些准备工作,在主线程执行。

  2. doInBackground
    此方法为要执行的后台任务,在子线程中执行。不能进行UI操作。

  3. onProgressUpdate
    显示任务执行进度,在doInBackground中调用publishProgress(Progress... values),values会传入此方法中,进行进度显示。在主线程执行。

  4. onPostExecute
    当后台任务执行完毕后,通过return语句返回执行结果,执行结果传入此方法作为参数

任务的启动:

new DownloadTask().execute();

AsyncTask源码分析

基于Android 6.0源码

首先看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,mWorker是WorkerRunnable抽象类,实现了Callable接口。在mWorker的call()方法中,mTaskInvoked.set(true)表示当前任务已被调用过,然后设置当前线程的优先级。接着调用AsyncTask的doInBackground(mParams)方法执行后台任务,并将执行结果存入result传入postResult方法。因此,要执行的后台任务包含在了AsyncTask的mWorker的call()方法中。
然后是mFuture,mFuture是一个FutureTask类对象,在构造方法中传入mWorker作为参数。mFuture其实是一个封装了mWorker的FutureTask对象,FutureTask类实现了FutureRunnable接口,通过这个接口可以方便的取消后台任务以及获取后台任务的执行结果,具体可参考:Java并发编程:Callable、Future和FutureTask
由上分析可知:后台任务在mWorker的call()方法中,当call()被执行时后台任务doInBackground也就会被执行了。那么何时才会被执行?因为mWorker又封装在了mFuture中,因此当mFuture对象被提交到AsyncTask包含的线程池执行时,call方法就会被调用,自定义的后台任务也就开始执行了。

要启动某一个任务,就需要调用该任务的execute()方法,下面看execute()方法的源码:

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

在执行execute时传入params参数,这个参数会一层一层传递到doInBackground方法中,方法中调用了executeOnExecutor并将其返回值返回,传入了两个参数,其中sDefaultExecutor是AsyncTask默认的线程池。

下面看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方法中,首先判断任务的状态,当任务正在运行或已经结束时会抛出异常。
然后调用了AsyncTask的 onPreExecute() 方法,因此前面自定义的AsyncTask类中,onPreExecute()方法会第一个被执行。
然后将传入的params赋值给mWorker的mParams变量
然后执行exec的execute()方法,传入mFuture作为参数,exec就是前面传进来的sDefaultExecutor
接下来看看sDefaultExecutor

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    ……
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

sDefaultExecutor是一个静态的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);
            }
        }
    }

SerialExecutor类是一个串行线程池,其中定义了一个mTasks任务缓存队列。
在execute方法中,mTasks.offer向缓存队列添加了一个Runnable任务,任务中run方法执行了r.run(),其实就是调用前面传入的mFuture对象的run方法,而mFuture的run方法内部会调用mWorker的call方法,然后就会调用doInBackground方法,因此后台任务就开始执行了。
提交到任务缓存队列中的任务什么时候会被执行呢?
SerialExecutor中定义了一个Runnable对象mActive,表示当前正在执行的任务,当第一次执行execute时,mActive肯定是null,因此进入if语句执行scheduleNext(),取出第一个任务赋给mActive并判断是否为null,然后调用THREAD_POOL_EXECUTOR.execute()方法传入mActive参数,执行取出的mActive引用的任务对象。此时后台任务真正开始。
当有新的任务被执行时,同样会offer()方法将传入的Runnable添加到队列的尾部,在后续操作判断mActive就是非null,则不会进入if语句中。
但是,第一个任务被执行时,在Runnable的run方法,中finally中调用了scheduleNext()方法,因此每当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果启动了很多任务,同一时刻只会有一个线程正在执行,其余的均保存在任务队列中,处于等待状态。

由上可知,真正执行任务的是THREAD_POOL_EXECUTOR

public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

THREAD_POOL_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);
  • 核心线程数corePoolSize为CPU数加一;

  • 最大线程数maximumPoolSize为CPU数的二倍加一;

  • 存活时间为1秒;

  • 任务缓存队列为LinkedBlockingQueue,最多缓存128个任务。

前面说了,当子线程执行任务时,其实是执行mFuture对象的run方法,也就是执行mWorker对象的call()方法
下面再看一下mWorker对象的call()方法:

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

doInBackground的执行结果保存在result,作为参数传入postResult()方法
postResult():

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

这里通过AsyncTask类内部的Handler对象发出一条MESSAGE_POST_RESULT消息,消息内保存了AsyncTaskResult对象,该对象封装了任务的执行结果。
下面看一下这个Handler的相关源码:

private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
    
    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;
            }
        }
    }

在主线程创建一个sHandler对象

收到MESSAGE_POST_PROGRESS消息后,会调用onProgressUpdate方法,MESSAGE_POST_PROGRESS消息通过publishProgress方法发送,我们常会在doInBackground方法中调用这个方法,用于回传任务执行进度。

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

当sHandler收到MESSAGE_POST_RESULT消息后,会调用finish方法

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

在finish方法中,首先判断任务的状态,然后执行onPostExecute方法,最后设置任务状态为FINISHED,当前的AsyncTask任务全部执行完毕。

多线程问题

通过以上分析,AsyncTask涉及到两个线程池:SerialExecutor、ThreadPoolExecutor。

  1. SerialExecutor是串行线程池,使用ArrayDeque队列管理任务(Runnable对象)。它同时只能执行一个任务,多个任务要等待上一个任务执行完毕才能继续执行下一个。它在AsyncTask中的作用是保证任务按顺序地一个接一个串行执行

  2. ThreadPoolExecutor是真正执行任务的线程池,若要实现并行执行任务,则可自定义线程池,传入executeOnExecutor(Executor exec, Params... params)方法作为线程池对象。如:

Executor exec = new ThreadPoolExecutor(15, 200, 10,  
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());  
new DownloadTask().executeOnExecutor(exec,params);

相关推荐