当前位置: 首页 > 工具软件 > Android Astro > 使用案例 >

Android O:AsyncTask分析

鲁建茗
2023-12-01

这一篇博客主要分析一下Android O中AsyncTask相关的源码。


一、构造函数
AsyncTask是一个抽象类,实际使用时必须定义一个实现类。
在使用实现类时,我们需要创建出实例,会调用到AsyncTask的构造函数。

AsyncTask定义了几个构造函数:

    //用的最多的
    public AsyncTask() {
        this((Looper) null);
    }

    //使用了@hide标签,仅Framework和系统应用可以直接使用
    //指定回调的handler
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    //同样使用了@hide标签,仅Framework和系统应用可以直接使用
    public AsyncTask(@Nullable Looper callbackLooper) {
        //对于普通APK而言,callbackLooper为null, 于是将调用getMainHandler创建出主线程对应的Handler
        //对应系统应用等,可以指定回调的handler,即回调可以运行在非主线程
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        //WorkerRunnable实现Callable接口
        //参数类型为Params,接口函数call返回值的类型为Result
        mWorker = new WorkerRunnable<Params, Result>() {
            //容易看出,Worker负责封装传入参数,执行后得到结果
            public Result call() throws Exception {
                //记录当前AsyncTask是否执行
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //AsyncTask执行的优先级为Background
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

                    //noinspection unchecked
                    //调用接口执行任务
                    //在构造函数中,mParams还没有实际的意义
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //调用接口返回结果
                    postResult(result);
                }
                return result;
            }
        };

        //创建一个FutureTask对象,构造函数的参数为一个callable对象
        //当FutureTask执行时,会调用callable对象的call函数,在执行完毕后再回调done接口
        //容易看出FutureTask封装了上文创建的Worker
        mFuture = new FutureTask<Result>(mWorker) {
            //Worker执行完毕后,回调done接口
            @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);
                }
            }
        };
    }

从上面的代码可以看出,AsyncTask的构造函数中主要创建了以下几个对象:
发送回调信息的Handler;
封装调用参数,进行实际的处理并返回结果的Worker;
封装Worker,以便在执行后,进行收尾工作的FutureTask。

如果用命令模式来理解的话,那么AsyncTask中仅创建出了一个需要执行的命令。
这个命令还没有被添加到执行队列中,更别提实际的执行了。

在这部分的最后,我们看看上文中getMainHandler的代码:

    //注意到, 此处利用主线程的Looper构建出静态单例对象InternalHandler
    //说明对于普通APK而言,同一个进程中调用多个AsyncTask时,回调信息将由同一个Handler处理
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

二、DefaultExecutor
构造出AysncTask后,就可以调用接口开始实际的工作了。

从代码来看,AsyncTask提供了几个触发工作的接口,
我们还是以最常用的接口为例,进行分析:

    //注解说明了此接口必须在主线程中调用
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //注意到除了传入参数外,还传入了sDefaultExecutor
        return executeOnExecutor(sDefaultExecutor, params);
    }

在分析executeOnExecutor流程前,我们先看看sDefaultExecutor。

    ..............
    //静态成员,进程共享,默认指向SERIAL_EXECUTOR
    //可以调用AsyncTask的setDefaultExecutor进行设置
    //因此,设置了volatile属性,保证并发的可见性
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    ............
    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
    */
    //SERIAL_EXECUTOR指向的对象为SerialExecutor
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    ...........
    /** @hide */
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

从上述代码来看,sDefaultExecutor的类型默认为SerialExecutor。
可以通过调用setDefaultExecutor接口,来改变默认的行为。
不过该接口具有hide注解,意味着仅对Framework和系统应用可见。

因此对于普通APK而言,调用execute接口,
执行任务的线程池为默认为SerialExecutor。

现在我们来看看SerialExecutor的实现:

    //定义于AysncTask中的内部类
    ............
    private static class SerialExecutor implements Executor {
        //队列中保存所有的任务
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();

        //mActive中保存当前正在执行的任务
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //execute函数被调用时,将runable对象加入到mTasks中
            mTasks.offer(new Runnable() {
                //当该runnable被执行后,调用scheduleNext
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });

            //当有新加入的任务时,若当前没有正在处理的任务,则直接调用scheduleNext接口
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //从队列中取出第一个任务
            if ((mActive = mTasks.poll()) != null) {
                //利用THREAD_POOL_EXECUTOR执行
                //根据execute中的定义,我们知道一个runable的run方法结束后
                //才会重新调用scheduleNext
                //因此,虽然THREAD_POOL_EXECUTOR可以同时创建多个线程
                //但AsyncTask还是一个接一个的处理任务
                //如果将本函数的if改成while,就是并发处理了
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    .................

THREAD_POOL_EXECUTOR为AsyncTask加载时创建出的ThreadPoolExecutor:

    .......................
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    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;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        //根据之前博客分析ThreadPoolExecutor的代码,
        //调用allowCoreThreadTimeOut后,一旦超时,连核心线程也会被回收
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    .......................

从代码逻辑来看,AsyncTask实际上会一个接一个的处理任务,
因此THREAD_POOL_EXECUTOR实际上同一时间内仅会创建出一个后台线程。

AsyncTask采用这种方式定义线程池,是由历史原因决定的,可以参考如下注释:

 * <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>

三、executeOnExecutor
了解完默认线程池相关的内容后,我们将视线拉回到executeOnExecutor接口。
注意到这个接口为public且没有hide,因此普通APK也可以直接调用。

    //同样必须在主线程调用
    @MainThread
    //这个接口的强大在于可以指定Executor
    //当利用前文的execute接口时,该参数对应SerialExecutor,将顺序执行任务
    //直接调用这个接口时,就可以绕过SerialExecutor,并发执行任务了
    //此处传入的参数也可以直接指定为AsyncTask.THREAD_POOL_EXECUTOR
    //若使用THREAD_POOL_EXECUTOR,虽然AsyncTask可以并发处理,但所有AsyncTask仍共用线程池
    //仍然不应该执行极其耗时的任务
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //AsyncTask中定义了:private volatile Status mStatus = Status.PENDING;
        //因此每个AsyncTask对象创建后,其状态都是PENDING
        //这里就是注释叙述的,每个AsyncTask只能运行一次的原因
        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)");
            }
        }

        //AsyncTask开始运行后,状态变为RUNNING
        mStatus = Status.RUNNING;

        //回调子类的onPreExecute接口
        //仍然是@MainThread
        onPreExecute();

        //将参数赋给构造函数中创建的WorkerRunnable
        mWorker.mParams = params;
        //将构造函数中创建的FutureTask加入到线程池的WorkQueue中
        exec.execute(mFuture);

        return this;
    }

四、FutureTask
FutureTask被加入到ThreadPoolExecutor的WorkQueue后,
一旦被取出并执行,对应的run方法就会被调用:

    public void run() {
        //条件检查
        .............
        try {
            //这个callable,就是AsyncTask构造函数中,构造FutureTask时传入的WorkerRunnable对象
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //调用WorkerRunnable的call方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    //WorkerRunnable执行完毕后,才调用FutureTask的set接口
                    //set接下来会调用finishCompletion函数,最终调用FutureTask的done函数
                    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);
        }
    }

前文已经分析过,FutureTask实际执行时,调用的是WorkerRunnable的call接口。
WorkerRunnable中将调用doInBackground函数,在后台线程中完成任务,
在完成后调用postResult返回处理结果。


五、postResult

我们看看postResult函数的实现:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        //getHandler返回的是AsyncTask构造函数中创建的Handler对象
        //默认为InternalHandler,运行在主线程中
        //此处创建Message, Message中包含了后台线程的执行结果
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

我们看看InternalHandler的实现:

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

        @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
                    // 调用AsyncTask的finish函数
                    result.mTask.finish(result.mData[0]);
                    break;
                //调用AsyncTask的publishProgress时,发送的是这个消息
                case MESSAGE_POST_PROGRESS:
                    //回调更新进度
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

最后跟进AsyncTask的finish函数:

    private void finish(Result result) {
        //如果调用过cancel接口,则回调onCancelled
        if (isCancelled()) {
            onCancelled(result);
        } else {
            //正常情况下,回调onPostExecute接口
            onPostExecute(result);
        }
        //更改当前AsyncTask的状态
        mStatus = Status.FINISHED;
    }

至此,AsyncTask被处理完毕。
从执行流程可以看出,AsyncTask中前、后台线程的通信,
实际上还是依靠了Handler。


六、收尾工作
前文提到过,WorkerRunnable的run方法执行完毕后,
最终将会调用FutureTask的done函数。

对于AsyncTask的流程而言,done函数主要调用了postResultIfNotInvoked。

    private void postResultIfNotInvoked(Result result) {
        //前面的代码提到过,WorkRunnable的call被执行后,mTaskInvoked已经置为了true
        final boolean wasTaskInvoked = mTaskInvoked.get();

        //因此,正常情况下不会再进行postResult的工作
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

此处主要是为了配合AsyncTask的cancel接口。
我们看看AsyncTask的cancel接口:

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        //调用FutureTask的cancel接口
        return mFuture.cancel(mayInterruptIfRunning);
    }

跟进FutureTask的cancel接口:

    public boolean cancel(boolean mayInterruptIfRunning) {
        //条件检查
        .............

        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            //回调FutureTask的done接口,进入上文的postResultIfNotInvoked
            //最终回调掉postResult
            finishCompletion();
        }
        return true;
    }

由于默认情况下,AsyncTask是依次处理的。
因此,当某个AsyncTask加入到队列后,可能在实际处理前被cancel掉。
此时,就会触发FutureTask的cancel接口,进入到AsyncTask中定义的done函数。
最终,通过postResultIfNotInvoked函数,利用Handler触发AsyncTask的onCancelled接口。


七、总结
本篇博客中,我们分析了AsyncTask的基本概念、使用方式及源码。

虽然在实际使用时,我们并不需要对其实现方式有深刻的理解,
但如果可能的话,掌握它的实现方式,可能会得到一些额外的启发。

 类似资料: