从源码看AsyncTask中看线程池中任务的执行流程

本文详细解析了Android中AsyncTask的工作原理,包括其构造方法、执行流程、线程池使用及结果返回机制。揭示了AsyncTask如何通过串行执行器与线程池配合,实现异步任务的高效管理和执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AsyncTask一直是用的比较频繁的一个类,之前有时间也看了下其实现的源码,都是一知半解,对其如何加入线程池,在线程池中如何执行任务还有点没搞清楚,今天再次阅读源码,终于是有种豁然开朗的感觉,于是赶紧趁热记录一下。

首先,简单理一下AsyncTask的使用流程:

比如在Activity的onCreate中新建一个AsyncTask对象,然后执行它的execute()方法,一般会去使用静态内部类的方式来规避内存泄露,比如这样写:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        AsyncTask task = new MyTask(this);
        task.execute();
    }

MyTask是一个继承自AsyncTask的静态内部类,一般需要实现几个重要的方法:onPreExecute、doInBackground、onPostExecute、onProgressUpdate

比如这样:

static class MyTask extends AsyncTask<String,Integer,String>{
        private WeakReference<Activity> weakAty;
        public MyTask(Activity activity){
            weakAty = new WeakReference<Activity>(activity);
        }

        @Override
        protected String doInBackground(String... params) {
            for(int i=0;i<100;i++){
                Log.i("Mytask","i="+i);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(isCancelled()){
                    break;
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            Log.i("Mytask","执行结束了");
            AsyncTaskActivity mActivity;
            if((mActivity= (AsyncTaskActivity) weakAty.get())!=null){
                mActivity.doSomething();
            }
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            Log.i("Mytask","执行了取消");
        }
    }

具体这些方法的意义不在赘述,可查看相关的文档。

所以使用AsyncTask概括来说就是,我们要继承AsyncTask实现其方法,然后在使用的地方,创建一个对象,然后执行其execute()方法,那么具体它是怎么来工作的呢?

1、先看它的构造方法,毕竟这是第一个入口:(注:所有源码都是在API-23上分析的,版本不同个别函数实现有差别)

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对象,看:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

它实现了Callable接口,并定义了一个Params成员变量,这个成员变量就是用来存储我们在执行AsyncTask的execute()方法时传入的params。

至于为什么会起名叫xxxRunnable(刚开始阅读源码的时候可能会被这个名字误导),大概是因为它要被放到线程池里面执行吧,而线程池里面执行的就是runnable对象。当然这里并不是说直接把这个mWorker放进线程池,也不能,实际上它是由mFuture对象来包装的,作为FutureTask对象的一个成员变量。

我们来看看mFuture对象,它的类型是FutureTask,看:

public class FutureTask<V> implements RunnableFuture<V> 

它实现了RunnableFuture接口,从字面意思看,可以猜测这个接口是有Runnable和Future的功能:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

果不其然,这个接口实现了双继承,同时有了Runnable和Future的功能,Runable我们知道可以传入到线程池中,也就是说mFuture这个对象可以被传入到线程池中执行,而Future呢,它有一个重要的方法get(),可以在调用get的地方阻塞,直到有结果返回。我们知道,AsyncTask内部执行的时候是需要把执行结果返回的,返回的结果我们用来更新UI。但是Runnable对象只有一个run方法,执行完成就完了,并不会返回任何的执行结果,而Callable恰恰满足了这点,它有一个call方法,会返回一个泛型结果,这不正式我们需要的吗?所以AsyncTask同时使用了这三个接口的功能。

我们继续回到FutureTask对象上,看它的构造函数:

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

传递了一个Callable对象进来,实际上就是AsyncTask构造里面创建的mWorker对象,FutureTask对象持有mWorker的引用,如此,这三个接口的功能就关联到一起了。

概括起来就是,线程池Executor加入的任务需要一个Runnable对象,在这里就是FutureTask来替代,而AsyncTask执行完成需要一个返回结果来通知UI,由于Runnable做不到,那就使用Callable来,于是就有了WorkerRunnable,而为了保证一定能得到一个结果,那就得使用阻塞的方式来保证一定可以得到一个结果,就需要使用Future的get,于是也就有了FutureTask。

对象创建完毕,接下来就要去执行任务了。我们在仔细回顾一下AsyncTask的构造方法,说明一个AsyncTask对象就只有一个mWorker和一个mFuture对象,请记住这一点。

2、执行任务

AsyncTask有两个执行任务的入口:

execute(Params... );  // 默认使用串行线程池方式执行

execute(Executor  , Params... ); // 自定义线程池执行

实际上第一个方法最终还是调用了第二个方法,只不过使用了默认的串行线程池来执行。

@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) { // 首先是判断状态,这个状态是个成员变量,一旦我们的AsyncTask对象执行了execute,这个状态就会被置为RUNNING,所以不能重复执行execute方法
            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; // execute的参数被mWorker的成员变量记住
        exec.execute(mFuture); // 使用线程池接口的execute方法执行,mFuture中包含了mWorker变量

        return this;
    }

 

看完注释后,我们来到了exec.execute(mFuture)这里,默认情况下这个exec对象就是sDefaultExecutor,接下来我们看这个对象又是什么鬼:

 

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

它被定义为一个静态的,并且被初始化为SERIAL_EXECUTOR对象,SERIAL_EXECUTOR对象也是一个静态的并且是final类型,且在其前面初始化为一个SerialEexcutor对象,看名字可以知道是个串行执行任务。sDefaultExecutor这个作为静态的对象,说明在整个应用进程的生命周期内就只有这么一个对象,所以说,在一个应用进程内,我们可以创建多个AsyncTask对象,但是所有的AsyncTask对象共用这么一个SerialEexcutor对象。我们来看看这个SerialEexcutor对象做了什么:

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

SerialEexcutor实现自Executor,实现了其方法execute,该方法传入一个Runnable对象(实际上就是我们在AsyncTask构造方法里面创建的mFuture对象),另外SerialExecutor提供了一个同步的scheduleNext方法,这个方法真正的把我们要执行的任务放到线程池中来执行。所以实际上SerialExecutor并不是一个线程池,因为它没有创建线程执行任务的能力,它只是简单的实现了Executor的方法,然后通过双端队列来把我们的任务按先进先出的序列存储起来,然后在按先后顺序取出来放到线程池中执行。我们来详细的分解一下这个过程。

首先我们看到在上面代码的scheduleNext方法中有一个静态成员变量THREAD_POOL_EXECUTOR,从字面意思我们可以知道这是一个真正的线程池,它是在SERIAL_EXECUTOR初始化前就已经初始化了的,它也是全应用内共享的,就这么一个对象。

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

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

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

我们暂且不关心这个线程池的定义,不了解的可搜索线程池的一些概念,我们只需要知道它能加入一个runnable任务,然后内部就会使用线程来执行这个任务,也就做到了异步后台执行。

了解了以上信息,我们开始从任务执行的起始点来分析一下这个整个流程。

还记得AsyncTask的执行步骤吗?不记得了可以往上翻一翻。

我们直接看executeOnExecutor方法,倒数第二行执行了这个:

 

exec.execute(mFuture);

这里的exec就是静态成员变量SERIAL_EXECUTOR,继续看SERIAL_EXECUTOR的execute方法:

 

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

首先把我们传进来的mFuture对象包裹在一个新的对象Runnable中,并把这个新的Runnable对象加入到双端队列mTask的队尾:

 

mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });

然后继续执行,假如是第一次,那么mActive肯定为空,就会去执行scheduleNext方法:

 

if (mActive == null) {
                scheduleNext();
            }

scheduleNext方法从队列mTask的队头取出一个Runnable对象,如果不为空(因为我们在前面已经加入过,所以这里必然不为空),就把这个Runnable对象加入到线程池中去执行。注意,这里的Runnable对象(暂且用mRun来表示)是我们在加入mTask的时候创建的对象,包裹了我们真正要执行的任务mFuture。直到这里,我们还是在主线程中执行(前提是我们的AsyncTask的execute方法是在主线程中执行的)。我们先不管线程池中是如何执行那个新创建的mRun对象的,假如另外创建了一个AsyncTask对象,并执行其execute方法,那么会继续调用SERIAL_EXECUTOR的execute方法,接着在调用mTask的offer的时候,又新创建一个mRun对象,把这个mRun对象存储到mTask中,继续执行,由于我们第一次执行的时候,mActive已经被赋值了:

 

//execute中的方法,第一次执行为空会去执行scheduleNext
//第二次执行的时候mActivie不为空,不再去执行shceduleNext
if (mActive == null) {
                scheduleNext();
            }
//scheduleNext中的方法,第一次执行完成后,mActive不为空了
if ((mActive = mTasks.poll()) != null)  // 这里执行了赋值

mActive对象不为空,不会执行后面的语句,也就是说再有新的AsyncTask任务创建执行,到这里都只会吧这些新的任务包裹进一个新的Runnable对象中,并存储在mTask这个队列里面。先进来的任务会放在靠近队头,后进来的依次排在队尾,就这么把任务串行的加进来了。

接着我们来看线程池是如何执行这些任务的。

假如第一次,scheduleNext肯定会被执行

 

if (mActive == null) {
                scheduleNext();
            }

mRun任务(即mActive对象)被加入到线程池中执行:

 

protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }

也就是会执行这个最外层的run方法:

 

new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            }

然后去执行我们传进来的r(即mFuture)的run方法,先不管run方法执行了什么。当执行完run方法后,会继续执行finally里面的方法,即执行了scheduleNext方法,这个方法就是去队列里面取任务执行,就这样递归的把mTask里面的任务一个个取出来执行了,当mTask空了的时候,这个递归就结束了。

我们可以看出,mActive是在线程池中的线程里执行的,而真正的任务就是r.run,所以mFuture的run是在子线程中执行的。

于是我们来看看mFuture的run执行了什么?直接看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);
                }
            }
        };
    }

没看到run啊,那走进FutureTask去看看,在其里面我们看到了run方法:

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 {
                    result = c.call(); // 首先去调用了mWorker的call方法,得到结果
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result); // 设置结果,使用get可以得到结果
            }
        } 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);
        }
    }

 

我们就只看正常情况,先调用了mFuture的成员变量mWorkder的call方法:

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); // 执行AsyncTask的doInBackgroud方法,并取得结果
                Binder.flushPendingCommands();
                return postResult(result); // 执行AsyncTask的postResult返回结果
            }
        };

在mFuture的run方法里面执行了mWorker的call方法,call方法先调用了AsyncTask的doInBackground方法,取得执行的结果,然后用postResult返回结果。可以看到,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;
    }

获取主线程的一个handler对象,把结果组装成一个AsyncTaskResult的对象发送到主线程中:

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;
            }
        }
    }
 private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

在handleMessage中调用AsyncTask的finish方法:

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

进而会回调我们所熟知的那几个方法。

我们可以看到在mWorker的call方法里面实际上就跑完了整个流程,那么在AsyncTask的构造函数里面的mFuture对象创建的时候的done方式是什么作用呢?

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

我猜想可能是做一些异常的收尾工作。我们回顾下在FutureTask的run方法里面有这么几句:

try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);

通过mWorker的call获得结果,如果出现异常,执行setException,如果获取结果成功执行set

/**
     * Sets the result of this future to the given value unless
     * this future has already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon successful completion of the computation.
     *
     * @param v the value
     */
    protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

    /**
     * Causes this future to report an {@link ExecutionException}
     * with the given throwable as its cause, unless this future has
     * already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon failure of the computation.
     *
     * @param t the cause of failure
     */
    protected void setException(Throwable t) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = t;
            U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

最终这两个都执行了finishCompletion

/**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

可以看到在这个方法里面执行了done,这个方法在FutureTask里面是个空实现,那么必然在其子类里面实现,也就是我们的mFuture对象里面实现:

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);
                }
            }
        };
private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

这里的mTaskInvoked在我们的mWorker的call回调的时候已经被置为true了,所以这里就不会再去执行了postResult方法了,除非有了异常了,没有执行call回调。

 

至此,整个任务的处理流程就理清楚了。通过追根究底的分析,终于是对AsyncTask有个比较全面的认识了。之前大部分都是一知半解,面试的时候还会被问道,SerialExecutor真的是线程池吗这种问题,如果不是很理解,那么可能就认为这是个线程池,实际上它只是一个串行任务队列的处理类而已。另外有面试官还会问线程池是怎么创建线程执行任务的,这又是另外一个比较大的分析点了~回头有时间在细细研究下。

 

ps:近期对线程池有了进一步的理解,关于线程池如何创建线程执行任务的话题可查看:

线程池是如何重复利用空闲的线程来执行任务的?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值