Android 源码解析之AsyncTask

Posted 谢维开

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 源码解析之AsyncTask相关的知识,希望对你有一定的参考价值。

AsyncTask相信大家都不陌生,它是为了简化异步请求、更新UI操作而诞生的。使用它不仅可以完成我们的网络耗时操作,而且还可以在完成耗时操作后直接的更新我们所需要的UI组件。这使得它在android开发中成为炙手可热的网络请求工具类。

而今天我们就以源码分析的形式来彻底的学习下它的实现过程。

首先,我们先看看AsyncTask的定义形式:

public abstract class AsyncTask<Params, Progress, Result> {
}

首先AsyncTask它是一个抽象类,包括三种泛型类型,具体含义如下:

  • Params:它表示请求参数的类型
  • Progress:执行任务的进度类型
  • Result:返回结果的类型

但是以上三个参数并不是一定必须,在不需要时可以设置为Void,没有返回类型。

然后我们看看它的执行过程,包括以下几个方法:

  • execute(Params... params),我们在执行异步操作时会调用该方法,表示开始执行任务。

  • protected void onPreExecute() {},在调用execute方法后,该方法就会得到执行,它执行在UI线程中,用来初始化一些UI空间等

  • protected abstract Result doInBackground(Params... params);在onPreExecute执行完后将会执行该方法,它执行在后台,并接受一个Params类型的数组参数,用于请求网络,并且它返回一个Result 类型的结果。该方法中可以在执行网络请求的同时更新请求进度,调用publishProgress(Progress... values) 。

  • protected void onProgressUpdate(Progress... values) ,假如在doInBackground方法中调用了publishProgress方法,那么该方法就会得到执行,它是执行在UI线程的,根据values的值不停的更改进度,以达到想要的效果。

  • protected void onPostExecute(Result result),该方法是在doInBackground方法执行完毕后得到执行,可根据doInBackground返回的结果进行后续的UI操作,由此可见它是工作在UI线程中的。

经过上面一系列的方法运转,一个完整的AysncTask请求就正式的完成了任务。不仅完成了耗时操作还更新的UI组件,这就是它的魅力所在。但是这时候你该有疑问了,上面的方法都是你说执行哪个就执行哪个,哪到底是怎么执行的呢?

那么接下来就正式的揭开它的庐山正面目。

在正式介绍它的源码之前,你必须知道new 一个类它所执行的过程:

  • 在new的过程中,它会首先一层一层的加载它所继承的父类的成员变量及构造方法

  • 然后在加载自己的成员变量和构造方法。

顺序是不可变得。

那么看看在我们执行 new AsyncTask()中,它到底加载了哪些成员呢?


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

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

    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;
    private static InternalHandler sHandler;

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    private volatile Status mStatus = Status.PENDING;
    
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    private static class SerialExecutor implements Executor{...}
    
    public enum Status {
        PENDING,
        RUNNING,
        FINISHED,
    }

看到这么一大堆是不是很麻头皮,其实仔细拆分下来,你主要看几个变量即可。

  • THREAD_POOL_EXECUTOR :这个成员变量从它THREAD_POOL_EXECUTOR = new ThreadPoolExecutor中可以看出,它是一个线程池,而ThreadPoolExecutor线程池中需要几个参数,如corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、workQueue(任务队列)、threadFactory(线程工程)等等,所以像CORE_POOL_SIZE,sPoolWorkQueue ,sThreadFactory 等成员变量,只是为了配置这个线程池而已。

  • sDefaultExecutor 这个成员变量是默认的线程调度任务,从上面可看出SERIAL_EXECUTOR则是一个序列化的任务调度,从sDefaultExecutor = SERIAL_EXECUTOR;中可以明确的知道sDefaultExecutor任务调度中是按先后顺序执行的。

  • sHandler顾名思义是一个handler,mWorker是一个工作线程,mFuture则是一个FutureTask,FutureTask是专门用于管理Runnable线程的,mStatus 则是一个枚举,里面有三种状态,分别是未执行,执行中,以及执行完成状态,默认状态是未执行状态。

所以我们只要理解好上面几个变量可以不用害怕它一堆的初始化成员。

然后我们在看看AysncTask的构造方法中具体做了那些事:

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

简单来说,AsyncTask的成员变量中就只是初始化了两个变量,mWorker 和 mFuture 。这两个变量是非常重要的,后续的所有执行过程都是由这两个变量构成或引导的。

首先mWorker 是一个抽象内部类实例,是一个任务线程,它实现Callable

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

然后mFuture 则是一个针对任务线程的管理类。专门用于管理任务线程的,可以使我们的任务得到更好的控制,来看看它的构造方法吧:

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

就是接受了我们的mWorker 对象以及把自身的状态设置为NEW。

以上就是在new一个AsyncTask所进行的所有操作,无非就是初始化了一些数据和变量。

下面来看看AysncTask的正式执行。

我们所知道开启一个AsyncTask任务所调用的方法是execute方法,该方法必须在主线程中调用。

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

在调用execute方法后,该方法什么也没做,只是把已初始化好的默认序列任务线程sDefaultExecutor和传递进来的数据params传递给了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的执行状态,如果是正在执行或是已经结束执行了,它就会报出一个IllegalStateException的异常,告诉你线程或是在执行中或是已经执行完毕了。

  • 只有在未执行的状态下,才可以进行AsyncTask请求任务,接下来它直接把AsyncTask的执行状态更改为Status.RUNNING,告诉其他任务该AsyncTask正在执行中,保持执行结果的一致性。然后就执行了onPreExecute();由于execute方法是必须在主线程中执行的,所以到目前为止还是在主线程中运行,也就证明了onPreExecute()方法是在主线程中运行的。

  protected void onPreExecute() {
  }

onPreExecute源码中并没有做什么事情,这对于我们来说,只需要重写该方法就可以在主线程中进行一些UI组件的初始化等操作。

  • 接下来则是将我们所传递的数据赋值给mWorker的mParams变量,然后调用exec.execute(mFuture)方法,我们通过execute方法中知道exec其实就是一个sDefaultExecutor,sDefaultExecutor实则是一个SerialExecutor 序列线程,而mFuture我们在构造方法中也很清楚的知道,它是一个封装了mWorker线程的一个可管理的任务线程,那么在调用sDefaultExecutor的execute方法并传递进了mFuture任务线程,那到底做了什么事情呢,我们来看下它的源码:
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);
            }
        }

源码我们很清晰的知道在execute方法中最终的目的就是把mFuture任务线程赋值给一个Runnable 线程并放到了THREAD_POOL_EXECUTOR线程池中,由THREAD_POOL_EXECUTOR线程池来执行mFuture线程任务。

那么接着我们看看在THREAD_POOL_EXECUTOR线程池中execute的方法中主要做了什么事情:

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

THREAD_POOL_EXECUTOR线程池中主要是判断了传递的线程是否为空,是否小于当前线程池中保存的核心线程数,如果小于则直接执行addWorker(command, true)方法,下面看看addWorker方法中的实现内容:

 private boolean addWorker(Runnable firstTask, boolean core) {
        ...(前面代码省略)
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

我们只看主要的逻辑,首先是将我们的mFuture任务线程存放到了一个Worker的对象中,然后又从Worker对象中获取到mFuture线程并赋值给了Thread ,接着把Worker对象放到workers的HashSet数据集合对象中,经过获取HashSet的大小并进行一些判断,把workerAdded 设置为true,最后开启t.start();线程,由此进入了子线程中

那么接下来在开启的子线程中又做了什么事情呢?

我们从上面的分析指导t.start()开启就是一个mFuture的异步任务线程,那么它在哪执行呢?

细心的朋友可以发现,原来是在SerialExecutor 中的execute方法中我们的mFuture的run()早已在等待了线程的启动,那么,我现在去看看mFuture的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();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

这段代码很简单,一眼就可以看得出来就是利用我们在为mFuture初始化时传递的mWorker 对象实例并调用它的call()方法,我们先不管call怎么实现的,先来看看这个方法中的后续是什么。

接着它得到一个执行结果,并把一个boolean类型的ran设置为true,最后根据ran调用set(result);方法,并把结果传递进去,下面看看set的源码:

protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

它主要调用了finishCompletion();在来看看finishCompletion的源码:

 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
    }

在执行完call中,把一些对象进行还原,还调用了 done(),该方法就是在AsyncTask构造方法中我们有看到它的实现:

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主要是用来管理异步线程任务的,那么在done方法中就有很好的体现,在该方法中,它会判断执行的结果是否成功,成功后有没有被发送,如果有发送它就不再发送消息,如果结果执行成功,但没有被发送它就会发送最终的执行结果:

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

postResult方法的内容我们推后一点讲,那么现在我们来看看mWorker 中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);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

在这里我们终于见到了我们所熟悉的一个方法doInBackground(),由此也可以知道其确实是在子线程运行的,而doInBackground()方法在AsyncTask类中是一个抽象方法:

 protected abstract Result doInBackground(Params... params);

那么我们在重写doInBackground()时就可以直接的在其中进行一些耗时的网络和IO操作了。

这里插上一句,假如在doInBackground()调用了publishProgress方法来更新进度的话,那来看看它是怎么做的:

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

publishProgress方法中主要是通过Hangler发送一条更新进度的标志用来更新进度。这里的Hangler接受消息在下面和执行结果一起讲。

最后doInBackground()执行获取的Result 结果也将会传递到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;
    }
    
  private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

  private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

postResult中首先封装了doInBackground异步执行结果的AsyncTaskResult对象,然后获取到一个Handler ,通过消息处理机制发送一条信息来切换到主线程中进行UI界面的更换,消息处理机制不属于本次博文的内容所以不再细说,那来看看这个Handler是怎么处理这个消息内容的。

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

Handler中主要是根据消息标志进行区分是更新进度还是执行结果:

如果是更新进度则调用AsyncTask的onProgressUpdate方法来更新内容,由于通过Handler已转变为主线程中,所以我们在重写该方法时可以直接更新UI组件。

如果是执行结果则AsyncTask的finish(result.mData[0]);并把结果数据传递过去,来看看finish()中是怎么实现的:

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

finish()方法中也非常的简单,首先判断是否为取消线程,否则的话则执行onPostExecute(result)方法,由此我们在重写了onPostExecute方法后可以直接的更新我们的UI组件。

最后把AsyncTask的状态改为完成状态,至此整个AsyncTask生命周期就执行完毕了。

好了,至此AsyncTask整个执行过程就完全讲完了,相信大家也学到了不少东西,建议大家有空自己对着源码在梳理一遍,毕竟自己总结出来的印象就更深刻。

今天就到这里吧,祝大家生活愉快。

更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
技术分享


以上是关于Android 源码解析之AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

Android源代码解析之--&gt;HandlerThread

Android 源码解析之AsyncTask

Android 源码解析之AsyncTask

Android -- AsyncTask源码解析

Android AsyncTask 源码解析

Android 从源码的角度带你完全解析AsyncTask