【源码】Android 异步任务 AnsycTask 深入源码剖析

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

    在 Android 异步处理中,有一个 AnsycTask 非常的实用,相信大部分的 Android 开发者都有使用过。我们 Java 中的同步和异步这个概念,也许有人还不是非常清楚,我们在学习线程那一部分内容的时候,首先引出的两个概念就是同步和异步,后面才学习线程。

同步和异步介绍:

    那么,先看看什么叫做同步,我个人的理解就是:比如,一个问题解决的步骤需要1、2、3这三个步骤才能完成,同步就是指我们必须先完成1才能继续完成2,最后完成3。这种情况就好比我们数学中的大题,大题中有三个小题,我们必须完成第1题,然后用第1题的答案参与第2题计算,最后要综合1、2完成第3题,才能得出正确答案,是一环扣一环的,必须等待前一个步骤的完成才能继续进行。

    然后,异步就是指:这个问题虽然有1,2,3才能完成,但是它们之间的联系不太密切,也就是说可以任意的顺序完成这三个步骤,不需要等待谁。举个例子,比如有一组去参加数学竞赛,这个小组有三名学生,他们需要完成90道选择题,计时开始,他们就分配每人30题同时开始做,等到做完时,就把答案合并到一起,才能完成比赛。从这个例子可以看出,异步可以同时操作一个或多个任务。

    复习一下同步和异步的概念,是为了让我们更好的去理解线程。在 Android 中只有存在两种线程,一个是主线程,也称 UI线程,而其他的都是子线程。而子线程可以任意的创建,每个子线程都可以同时进行一些操作,完成自己的任务,比如上面的三个同学就像三个子线程一样,而子线程做到都是异步任务。

AnsycTask 为什么是异步的呢,下面我们从源码一探究竟吧。

    首先,来看看我画的一张思维导图:

    那么,为什么说 AnsycTask 是异步任务呢,我们在 Android 中创建它的时候,哪里有看到与线程相关的代码了?例如,我们一般创建一个 Task 去继承 AnsycTask,代码如下:

package com.example.xww.myapplication;

import android.os.AsyncTask;

/**
 * @author xww
 * @blog https://blog.csdn.net/smile_running
 * @name 威威猫
 */
public class MyTask extends AsyncTask<Void, Integer, Void> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //异步任务开始
    }

    @Override
    protected Void doInBackground(Void... voids) {
        //执行在子线程中
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        //任务进度
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        //任务完成
    }
}

1、通常要求我们必须重写 doInBackground() 方法,这是为什么呢?

   从源码可以看出,因为 AnsycTask 是一个抽象类,而 doInBackground() 是一个抽象方法,我们继承抽象类时,必须重写抽象方法。

抽象类:

抽象方法:

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

    从源码上面的注释,我们大致可以翻译出来,它说重写这个方法去执行一些计算是在后台线程中进行的,也就是在子线程中去执行。还说了通过 publishProgress() 方法可以更新 UI 操作,我们来一探究竟。

2、为什么 doInBackground() 方法在子线程中执行?

    首先,当然是它的调用方式,我们开启异步任务时,要调用 execute() 方法,我们跟着方法进行找源代码。

        MyTask myTask = new MyTask();
        myTask.execute();

     开始,会去调用构造函数,初始化一些参数的操作,代码如下:

AsyncTask 构造函数:提供三个构造函数

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        this((Looper) null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return 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);
                }
            }
        };
    }

     这里有一些差距,第一个是无参的,用的是 AsyncTask 类中所有默认的。而第二个构造函数,要我们传入一个 Handler 对象,使用的是我的 Handler 对象的 Looper,而第三个需要传入 Looper 对象,但是不管调用哪个,最终都会去执行第三个构造函数,这里我不太明白,直到自己去尝试了一下,写了一个例子就恍然大悟,原来可以这样写。以前的话,一般都是在内部自己 new 自己,这下学到了:

package com.example.xww.myapplication;

/**
 * @author xww
 */
public abstract class Test {

    public Test() {
        this(null);
    }

    public Test(int num) {
        this(String.valueOf(num));
    }

    public Test(String str) {

    }
}

     那么,我们看第三个构造函数的方法具体初始化了哪些东西。首先是一个 worker 的初始化操作:

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

     WorkerRunnable 是 AsyncTask 一个内部的抽象类,内部做了一个参数的赋值。它实现了 Callable 接口,重写了 call() 方法,这个方法也是工作在子线程中的,不过它与 Runnable 接口的区别就是,run() 方法返回的是 void 类型,而 call() 是一个泛型,也就是我们可以在子线程中返回任务的执行结果。

    这里的 Result 就是我们传入的泛型,它的结果是在 doInBackground() 执行后返回的。这里也可以很好的说明,我们的 doInBackground() 方法在子线程中执行的原因。

    还有一个初始化就是对我们的 FutureTask 的初始化,FutureTask 是一个针对 Callable 接口进行状态监听的实现类,它可以判断任务是否取消、是否完成,并且可以获得任务执行完的返回值等一些操作的封装类。这里的话代码这样的:

        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 类是对上面的一个 WorkerRunnable 类的对象进行监听的,而 WorkerRunnable 实现了 Callable 接口,而它的返回值是交于 doInBackground() 方法去执行,所以它要等待的就是 doInBackground() 方法的返回结果,在 done() 方法中去监听任务的完成情况,这里是监听 postResult() 方法没有被执行时。

    实例化完对象后,需要去调 execute() 方法后才能执行工作。这里来看看 execute() 方法是如何执行的:

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

myTask.execute() 之后,它会去调用 executeOnEcecutor() 这个方法:

     /**    
     * <p>This method must be invoked on the UI thread.
     *
     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
     *              convenient process-wide thread pool for tasks that are loosely coupled.
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     *
     * @see #execute(Object[])
     */
    @MainThread
    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;
    }

    这里有一个状态枚举类,用于标记这个 Task 的生存时间,分别是 Task 开始,Task 进行中,Task 完成。看源码,默认的 Status 是 PENDING 状态。

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

    我们为什么不能重复调用 myTask.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)");
            }
        }

    首先,我们调用 myTask.execute() 方法的时候,把 Task 状态改为 RUNNING ,并最先得到执行的就是 onPreEcecute() 方法。也就是说,我们这里可以让子类重写 onPreEcecute() 做一些初始化操作。

    然后调 exec.execute(mFuture),这里的 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);
            }
        }
    }

    源码中定义了一个静态内部类 SerialExecutor 类去实现 Executor 接口,这里的 execute 就是接口中的抽象方法,我们看它用了 synchronized 修饰了,所以意味着 AsyncTask 它是线程安全的。

    这里用了一个队列 ArrayDeque<Runnable> 来存储 Runnable 对象,THREAD_POOL_EXECUTOR 是一个线程池,所以,可以看出,AsnycTask 内部还有一个线程池来维护,线程池初始化代码:

    /**
     * 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(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

    当从 ArrayDeque<Runnable> 队列中取出的 Runnable 对象不为空的时候,线程池就会调用 execute() 方法,把 Runnable 对象传进去,否则会 new 一个 Runnable 对象,不管如何,最终它们都是跑到 Runnable 接口中的 run 方法里面执行,而你肯定会奇怪,为什么 THREAD_POOL_EXECUTOR 也可以调用 execute() 方法?

    原来,这里的线程池 TheadPoolExecutor 类也间接的实现了 Executor 接口,它首先实继承 AbstractExecutorService 类,而 AbstractExecutorService 类又实现了 ExecutorService 接口,而 ExecutorService 接口又继承 Executor 接口,绕了一大圈才明白,这里的 TheadPoolExecutor 线程池类与 SerialExecutor  类一样,都是实现了 Executor 接口,所以这里的 THREAD_POOL_EXECUTOR 调用 execute() 方法才会跑到线程池中执行的一个重要原因。并且这里的线程池是一个单利模式,使用了静态代码块。

    经过这样的一番追根究底,我们终于弄明白 doInBackground() 方法在子线程中执行的原因。不过还没有结束,从源码可以看出这个SerialExecutor 类的作用是把 Runnable 加入到线程池当中去执行,并设置了默认的线程池。

    /**
     * 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 volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    AsyncTask 内部维护的一个默认的线程池参数如下:

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

     线程池的核心线程大小:2 ~ 4 个之间,不会小于2也不会大4。最大线程数是 CPU 的两倍 +1 个,线程结束任务后,可以继续存活 30s,还有使用了 AtomicaInteger 这个类实现了 new Thead 数量自增的创建,AtomicaInteger 类是线程安全的,这里不看它的源码了。最后是一个阻塞队列,也就是线程池中可以添加的最大 Runnable 的数量,也就是最大的任务数量128个。

    当任务执行结束了之后,我们之前说过的一个 FutureTask 就会监听到任务完成了,在 done() 方法中就会调用 postResult() 返回结果。

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

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

    postResult() 为什么是在 UI 线程中执行的,从上面的代码中,可以很明显的看出来。它调用了 Handler 的 obtainMessage() 方法发送消息,并把结果传递过去。它的结果是一个泛型集合,代码如下:

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

    然后在 Handler 中,通过消息分类进行接收结果:

    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
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    我们看到,在消息循环里面,处理消息分为两个状态,一种是任务完成后,返回执行的结果,另一种是在任务执行当中的状态,用于进度更新操作。我们看看 finish() 方法代码:

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

    首先得判断任务是否被取消,否则就调用 onPostExecute() 方法把结果返回,然后任务状态就会标记为完成。我们在子类中,一般都是重写 onPostExecute() 方法获取执行结果的。在 onProgressUpdate() 方法中,可以获取任务执行的进度,比方说开启进度条来显示进度,就重写这个方法,并且这两个方法都是在 UI 线程中执行的。

    还有最后一个,为什么 publishProgress() 方法也是在 UI 线程的,通过上面的介绍,我们大概就能猜到了,看看它的代码吧,也是差不多:

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

    到此为止,我们把 AsyncTask 的工作原理以及源码全部分析了一遍,整个 AsyncTask 工作的流程就是这么一步一步下来的,通常在刚刚开始学习 AsyncTask 的时候,我们只会知道它怎么去用,只知道 onInBackground() 方法就是在子线程中执行的,而它其他都是在主线程中执行的,这是远远不够的,那些都是止步于 API 的调用层面,还是要深入源码的理解。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值