美文网首页程序员
【Android】深入解析 AsyncTask 源码

【Android】深入解析 AsyncTask 源码

作者: Tyhoo_Wu | 来源:发表于2020-10-31 10:30 被阅读0次

    前言:AsyncTaskAndroid SDK 中提供的一个用于执行异步任务的框架,在 Android 兴起的早期被广泛使用,但如今在 API 30 被弃用,取而代之的是 java.util.concurrentKotlin 协程。虽然它存在着一些不足,但是我们还是可以尝试了解一下它的实现原理以及存在的不足。

    注:本文以 API 29 的 AsyncTask 源码进行解析。

    功能概述

    首先,让我们来简单地了解一下它的设计初衷:

    AsyncTask的设计初衷是能够帮助用户方便地完成异步任务的线程调度,它对用户提供了如下几个接口:

    public abstract class AsyncTask<Params, Progress, Result> {
        @WorkerThread
        protected abstract Result doInBackground(Params... params);
    
        @MainThread
        protected void onPreExecute() {
        }
    
        @MainThread
        protected void onPostExecute(Result result) {
        }
    
        @MainThread
        protected void onProgressUpdate(Progress... values) {
        }
    
        @MainThread
        protected void onCancelled(Result result) {
            onCancelled();
        }
    }
    

    首先看到它的三个范型参数:

    • Params:代表传递给它的参数类型
    • Progress:代表用于反馈进度的数据类型
    • Result:代表异步执行的结果类型

    用户可以通过实现doInBackground方法来编写在异步线程所要进行的处理;通过onPostExecute方法的重写实现对异步请求结果的获取;通过onPreExecute实现了在doInBackground之前进行一些预处理;通过onProgressUpdate实现对进度的监听;通过onCancelled实现对任务中断的监听。

    当我们编写一个AsyncTask后,只需要调用它的execute方法即可,而背后的线程调度的过程都会由它替我们完成,看上去是十分美好的。

    同时从上面的代码中可以看到,每个方法都有被@MainThread的注解标注,这些注解主要的作用是用来标注这个方法运行所处的线程。可以看出,只有doInBackground是在异步线程进行执行。


    创建

    让我们看看它的创建过程,来到它的构造函数:

    public AsyncTask() {
        this((Looper) null);
    }
    

    它的无参构造函数转调到了它的有参构造函数,这个构造函数需要以Looper作为一个参数:

    public AsyncTask(@Nullable Looper callbackLooper) {
        // 根据传递进来的 Looper 来构建了一个 Handler,若没有指定 Looper 或指定的 Looper 是主线程的 Looper,
        // 则指定内置的 InternalHandler 对消息进行处理(见附1),否则构造一个对应的 Handler。
        // 可以看出 AsyncTask 不是一定回到主线程的。
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
               ? getMainHandler()
               : new Handler(callbackLooper);
      
        // 构建了一个 WorkerRunnable,WorkerRunnable 实际上是对 Callable 的简单包装(见附2),
        // 区别仅仅在于可以传入参数。
        mWorker = new WorkerRunnable<Params, Result>() {
            // 当 mWorker 被执行时
            public Result call() throws Exception {
                // 将当前 Task 设置为被执行
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    // 进行线程优先级的设置
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    // 调用 doInBackground 方法并获取了返回值
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 看来这个 WorkerRunnable 是在异步线程中执行的。
                    // 不论成功与否,它最后都会调用 postResult 进行结果的交付。
                    postResult(result);
                }
                // 返回结果
                return(result);
            }
        };
    
        // 基于前面的 mWorker 构建了一个 FutureTask
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    // 当执行完成或被取消时,会调用 postResultIfNotInvoked 方法传入 mWorker 的执行结果。
                    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 方法传入 mWorker 的执行结果。
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
    
    附1
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return(sHandler);
        }
    }
    
    附2
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
    

    总的来说,其构造过程主要是对 Handler、WorkerRunnable 以及 FutureTask 进行了构建。


    执行

    让我们看看当调用execute之后它做了什么:

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

    可以看到,它转调到了executeOnExecutor方法,并且传入了一个Executor对象sDefaultExecutor,我们先不去关注这个Executor的设计,让我们先看看executeOnExecutor方法:

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                        Params...params) {
        // 首先,它对当前 Task 的状态进行了检查,AsyncTask 共有三种状态(见附1)
        // 只有 PENDING 的 Task 才能被 execute。
        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)");
            }
        }
    
        // 改变当前 Task 的状态
        mStatus = Status.RUNNING;
    
        // 调用 onPreExecute 方法进行了用户实现的预处理
        onPreExecute();
    
        // 将用户的参数交给 mWorker
        mWorker.mParams = params;
        // 将 mFuture 交给了传入的线程池进行处理
        exec.execute(mFuture);
    
        return(this);
    }
    
    附1
    public enum Status {
        // 待执行
        PENDING,
        // 正在执行
        RUNNING,
        // 已执行
        FINISHED,
    }
    

    FutureTask执行后,会使得mWorker被执行,它执行后会将mTaskInvoked.set(true),并调用postResult进行结果的交付:

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

    这里实际上就是把结果包装成了AsyncTaskResult类并放入了消息队列,这样就实现了线程的切换,将消息通过Handler由子线程发送给了主线程(指AsyncTask被指定的线程),当Handler收到消息后,就会对消息进行处理。

    回到AsyncTask创建的时候,我们可以看到在构造时没有指定Looper的情况下,默认的InternalHandler是如何进行处理的:

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return(sHandler);
        }
    }
    
    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) {
                    // 在 MESSAGE_POST_RESULT 消息下,它在收到 AsyncTaskResult 后会调用 finish 方法进行整个任务
                    // 的完成处理(见附1)
                    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;
            }
        }
    }
    
    附1
    private void finish(Result result) {
        // 若任务已取消,它会调用 onCancelled 方法进行回调
        if (isCancelled()) {
            onCancelled(result);
        } 
        // 若任务正常完成,它会调用 onPostExecute 方法
        else {
            onPostExecute(result);
        }
      
        // 设置状态为 FINISHED
        mStatus = Status.FINISHED;
    }
    

    当 FutureTask 执行完成后,会调用postResultIfNotInvoked方法,并传入mWorker的执行结果:

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
    

    可以看到,这里实际上是在Task没有被Invoke的情况下才会调用到,由于mWorker在被执行时首先就会将wasTaskInvoked置为 true,因此实际上这里很少能被调用到。

    我们再看看onProgressUpdated何时会被调用到,我们可以找到MESSAGE_POST_PROGRESS的消息是何时被发出的:

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

    它是在publishProgress方法中被调用的,这个方法是提供给用户的,因此进度的更新需要用户自行在doInBackground中调用publishProgress从而实现。


    取消

    我们看看任务的取消,看到cancel方法:

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        // cancel 方法最后会调用到 mFuture 的 cancel,并且它需要传递一个参数 mayInterruptIfRunning,
        // 见附1
        return(mFuture.cancel(mayInterruptIfRunning));
    }
    
    附1
    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
               U.compareAndSwapInt(this, STATE, NEW,
                        mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
                return(false);
        try {
                // 当 mayInterruptIfRunning 为 true 时,即使任务在运行时也会被打断。
                // 而如果为 false,则 doInBackground 仍然会一直执行到结束。
                if (mayInterruptIfRunning) {
                        try {
                                Thread t = runner;
                                if (t != null)
                                        t.interrupt();
                        } finally {
                                U.putOrderedInt(this, STATE, INTERRUPTED);
                        }
                }
        } finally {
            finishCompletion();
        }
        return(true);
    }
    

    这个设计非常奇怪,按道理来说取消的场景只有任务正在执行时需要取消,完全可以提供一个这个参数默认为 true 的方法供外部调用。并且这个方法只能尽量让任务尽快结束,如果执行的过程中有一些不可打断的操作,则这个方法调用后仍然不会使得任务停止。


    线程池

    对于AsyncTask,我们还有最后一个问题没有研究,那就是它执行任务的sDefaultExecutor

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    

    可以看到它的默认值为SERIAL_EXECUTOR

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    

    SERIAL_EXECUTORSerialExecutor的实例。

    private static class SerialExecutor implements Executor {
        // Executor 内部维护了一个 ArrayDeque 队列,这个队列是一个任务缓存队列,
        // 里面存放了对真正要执行的 Runnable 进行包装的 Runnable。
        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();
                          }
            }
        } );
    
            // 当 mActive 为 null,它会调用 scheduleNext 方法。
            if (mActive == null) {
                scheduleNext();
            }
        }
    
        // 在 scheduleNext 方法中会从 mTasks 队首取出任务赋值给 mActive,
        // 之后通过 THREAD_POOL_EXECUTOR 这个线程池对该任务进行执行。
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    

    当任务执行完成后,会继续调用scheduleNext对队列中的下一个任务进行执行。通过这样的设计,Runnable的执行变成了一种按序执行,只有前一个执行结束,才会进行下一个任务的执行,因此AsyncTask的执行顺序实际上是一种串行执行。

    那么THREAD_POOL_EXECUTOR又是一个怎样的线程池呢?让我们看看它的声明:

    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int KEEP_ALIVE_SECONDS = 3;
    
    public static final Executor THREAD_POOL_EXECUTOR;
    
    static {
      ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
              new SynchronousQueue<Runnable>(), sThreadFactory);
      threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
      THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    

    可以看到,THREAD_POOL_EXECUTOR的配置如下:

    • CORE_POOL_SIZE:内部核心线程个数
    • MAXIMUM_POOL_SIZE:最大线程数量
    • KEEP_ALIVE_SECONDS:每一个线程空闲后等待的时间,时间到了就会被停止

    只保留一个池线程。如果需要,让池增长到相当多的线程,但让他们很快超时。 在不太可能发生线程用尽的情况下,退回到一个简单的无界队列执行程序。

    这种组合可确保:

    • 保持少量的线程
    • 仅在启动明显更大,但仍有限的线程集之后才进行排队
    • 将线程总数限制为有界,但仍然允许无限制的任务集排队。

    不足之处

    AsyncTask看似十分美好,但实际上存在着非常多的不足,这些不足使得它在 API 30被废弃:

    • 生命周期:AsyncTask没有与ActivityFragment的生命周期绑定,即使Activity被销毁,它的doInBackground任务仍然会继续执行。
    • 取消任务:AsyncTaskcancel方法的参数mayInterruptIfRunning存在的意义不大,并且它无法保证任务一定能取消,只能尽快让任务取消(比如,如果正在进行一些无法打断的操作时,任务就仍然会执行)
    • 内存泄漏:由于它没有与Activity等生命周期进行绑定,因此它的生命周期仍然可能比Activity长,如果将它作为Activity的非static内部类,则它会持有Activity的引用,导致Activity的内存无法释放。

    总结

    本文基本上对AsyncTask的实现原理有一个大致的了解了,它的原理实际上还是挺简单的:通过ExecutorFutureTask配合,从而实现任务的异步执行,最后在任务结束后通过Handler进行线程的切换从而实现了整个线程调度的功能。在Android 3.0之后,默认情况下AsyncTask的执行顺序是串行进行的。


    © 2020 Tyhoo Wu, All rights reserved.

    相关文章

      网友评论

        本文标题:【Android】深入解析 AsyncTask 源码

        本文链接:https://www.haomeiwen.com/subject/trhsvktx.html