带你轻松看源码---AsyncTask(异步任务)

564 阅读12分钟
原文链接: blog.csdn.net

本文出自博客Vander丶CSDN博客,如需转载请标明出处,尊重原创谢谢

博客地址:blog.csdn.net/l540675759/…

写作背景

  • 愚人节特别篇

  • 这篇博客准备了好久,一直放在草稿箱里面,随着之前的深入了解Android线程池 . 完结之后,在写这篇文章就顺手了很多.

  • 写这篇博客的时候,只想了解下AsyncTask,没想到看着源码之后就沉迷了,一点一点看了队列,同步,异步,也尝试着翻译英文文档,总之收货满满.

  • 本文用图,全部为本人独立创作,如果转载请注明出处,请尊重原创谢谢.

  • 本文大部分观点,属于博主个人观点,如有错误,希望大家多多指正,然后博主及时更改.

  • 最后希望看到博主文章的小伙伴,能够给博主顶一顶博客,欢迎留言,欢迎讨论.

前言:

  • 本篇文章主要介绍AsyncTask的内部工作原理

  • 在阅读文本之前,如果你不太了解AsyncTask,可以先参考我的博客AsyncTask(异步任务)分析之基本使用

  • 文中涉及到一些术语如:串行、并行、队列这些关键词会在导读中为大家介绍


导读:

AsyncTask的组成很简单,但是还有有一些关键的知识点,导读中将会把一些比较重要的概念整理出来,让大家提前了解下,会使接下来的阅读更顺畅。

串行和并行的概念

在Android3.0之前,AsyncTask一直是并行执行的,而在Android3.0之后更改为串行执行,如果对串行和并行概念不太了解,可以参阅下我之前的线程中同步、异步、串行、并行这篇文章,通过图能让你迅速了解串行和并行的基本概念。


Android的消息机制,Handler的原理

在AsyncTask的工作原理中,InternalHandler是其运转的主要一环,AsyncTask通过它,来保持和UI线程的通信,从而实现进度的更新,以及AsyncTask的回调、中断的通知。
如果对Handler机制不太熟悉,可以参考下android的消息机制——Handler机制这篇文章。


队列的概念,容器ArrayDeque的使用

在AsyncTask中,有一个双端队列的线程池,通过其来保持AsyncTask的调度,而其内部的容器就是ArrayDeque,对于ArrayDeque网上的介绍比较少,这里可以参考我整理的深入了解双端队列Deque能够大致了解ArrayDeque的结构。


Android中的线程池

AsyncTask中采用线程池用于任务的调度和执行,如果对线程池不太了解,可以参考我的博文深入了解Android线程池 .


AsyncTask的源码解析

AsyncTask的主要成员

从AsyncTask的内部模块图可以看出,AsyncTask主要分为以下几个模块 :

(1)用于任务的调度的线程池SerialExecutor,图中的任务队列池.
(2)用于任务的执行的线程池ThreadPoolExecutor,图中的执行任务的线程池.
(3)用于和UI线程交互的InternalHandler,通知任务的完成的进度,以及任务的一些状态.

AsyncTask的状态

    public enum Status {
        /**
         * AsyncTask的初始状态,表明AsyncTask处于未执行任务的状态
         */
        PENDING,
        /**
         * AsyncTask的运行状态,表明AsyncTask正在执行任务,正在运行
         */
        RUNNING,
        /**
         * AsycnTask的完成状态,表明AsyncTask处于完成状态,并且会调用onPostExecute
         */
        FINISHED,
    }

任务调度线程池SerialExecutor

我们能够看到SerialExecutor只是实现了Executor这个接口,其内部维持的队列是ArrayDeque(一种双向队列).它会在一个任务执行结束,和SerialExecutor初始化之后,会自动去队列中获取任务并通过THREAD_POOL_EXECUTOR(执行线程池)执行.

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

任务的执行的线程池THREAD_POOL_EXECUTOR

执行线程池的初始化过程:

    //获得当前CPU的核心数
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //设置线程池的核心线程数2-4之间,但是取决于CPU核数
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //设置线程池的最大线程数为 CPU核数*2+1
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    //设置线程池空闲线程存活时间30s
    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());
        }
    };

    //初始化存储任务的队列为LinkedBlockingQueue 最大容量为128
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 一个可以执行并行任务的线程池哦
     */
    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);
        //设置核心线程池的 超时时间也为30s
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

用于和UI交互的InternalHandler

    private static class InternalHandler extends Handler {

        //注意:这里是获取主线程的Looper(),也就是为什么AsyncTask能和主线程交互的原因
        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:
                    //这里将结果通过Handler传递到主线程
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //这是是通知主线程更新进度的操作.
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

AsyncTask的内部模块图

AsyncTask的内部模块图

上图是AsyncTask的内部模块图,通过此图大家能够了解AsyncTask简单的工作原理。


AsyncTask的构造方法

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //添加线程的调用标识
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //设置线程的优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //执行异步操作
                    result = doInBackground(mParams);
                    //将进程中未执行的命令,一并送往CPU处理
                    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 {
                    //在执行完任务做一道检查,将没被调用的Result也一并发出.
                    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) {
                    //如果发生异常,则将结果滞null发出.
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

在AsyncTask的构造方法中,将任务包装好,然后进行初始化操作:

在构造方法中,WorkerRunnable就一个能储存参数的Callable.

//这里的Callable也是任务,但是与Runnable不同的是,Callable<T>存在返回值,返回值就为其泛型
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

而FutureTask也是一个包装类,我们来看下FutureTask的构造方法:

//其实FutureTask内部包含Callable<T>,并且增加了一些状态标识和暴漏出操作Callable<T>的一些接口.
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;      
    }

而其done()方法,就是FutureTask内的Callable执行完成之后的调用方法.在done()方法中,对任务的调用进行复查,将未被调用的任务的结果通过InternalHandler传递到UI线程.

//方法很简单,就是取得标志,然后判断该标志而已,复查没有被调用的任务,将其Result对象发送出去.
    private void postResultIfNotInvoked()(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        //如果没有被执行,也需要把结果发送出去.
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

AsyncTask的工作原理

通过上面的介绍,我们了解了AsyncTask的核心组成,以及AsyncTask在初始化所做的操作.

execute()

那么接下来,我们来一起看下AsyncTask的工作原理,这里我们从AsyncTask的执行方法execute()开始:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //从这里我们发现最终调用的方法是executeOnExecutor()方法
    //此时是通过队列线程池储存任务,然后执行线程池取出任务执行.
        return executeOnExecutor(sDefaultExecutor, params);
    }

executeOnExecutor()

   @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
            //判断AsyncTask当前的执行状态,PENDING为初始化状态
        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)");
            }
        }
        //executeOnExecutor()被调用时,AsyncTask的状态就变成了RUNNING状态
        mStatus = Status.RUNNING;
        //此时进行准备工作(主线程)
        onPreExecute();
        //将参数添加到任务中
        mWorker.mParams = params;
        //执行任务
        exec.execute(mFuture);
        return this;
    }

AsyncTask的基本执行流程大致的情况,上面已经介绍,需要注意的是,在Android3.0之前AsyncTask是并行执行的.

而在Android3.0之后默认AsyncTask是串行执行的.关于线程的串行和并行这里如果不明白,可以参考导读.具体大家可以在Android模拟器分别在3.0以下和3.0以上的版本进行 测试.

而如果在Android3.0以上的版本并行执行AsyncTask,我们可以这样:

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.d(TAG, "异步任务完成阶段阶段");
        SimpleDateFormat df =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Log.d(TAG, "当前异步任务 : "+"\t"+ this.hashCode()+"" );
        Log.d(TAG, "当前结束时间 : "+"\t"+df.format(new Date()) );
    }
//这里直接调用executeOnExecutor()方法,并且制定处理的线程池为 //AsyncTask.THREAD_POOL_EXECUTOR,也就是执行线程池
new 
TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

通过在完成方法打印当前的AsyncTask的hashCode来区分是不是同一个AsyncTask,以及时间上的差异来确定是否是并行,得到的结果为:

03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  632327564
03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完成阶段阶段
03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  496311509
03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完成阶段阶段
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  688151786
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完成阶段阶段
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  304548827
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48

由上述结果可以发现,上述的并行的执行过程直接绕过了队列线程池,直接制定执行线程池去执行任务.那么并行的原因在哪呢 ?

public AsyncTask() {
                    ......
                try {
                    //设置线程的优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //执行异步操作
                    result = doInBackground(mParams);
                    //将进程中未执行的命令,一并送往CPU处理
                    Binder.flushPendingCommands();
                    .........

注意Binder.flushPendingCommands()这个JNI命令.

    /**
     * Flush any Binder commands pending in the current thread to the kernel
     * driver.  This can be
     * useful to call before performing an operation that may block for a long
     * time, to ensure that any pending object references have been released
     * in order to prevent the process from holding on to objects longer than
     * it needs to.
     */
    public static final native void flushPendingCommands();

这个方法我的理解是将进程中等待的Binder命令,一并提交给CPU处理,当然这会造成一些阻塞,我们知道线程的执行也是依靠CPU来运转,所以我认为这个方法才是AsyncTask能够同步执行的关键.

在每个任务执行完时,都会把当前进程的等待任务提交,然后阻塞,等都完成,该进程内没有Thread提交时,一起返回,从而形成同步.

而在这里之所以不去使用队列线程池的原因也在这,因为队列线程池实现了锁的机制,并且通过它的代码我们可以得知它是处理完一个任务,才会去下个任务,这一块也是AsyncTask默认能够实现串行的原因.

这里写图片描述


postResult()方法

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        //正常的把结果通过InternalHandler发送给UI线程.
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

这个方法,没有太多可介绍的.熟悉Handler机制的,自然就能看懂.当postResult()方法执行后,InternalHandler 会调用AsyncTask的finish()方法.


finish()方法

    private void finish(Result result) {
        //判断任务是否被取消,如果被取消则回调onCalled()
        if (isCancelled()) {
            onCancelled(result);
        } else {
            //请求成功
            onPostExecute(result);
        }
        //变更AsyncTask的状态
        mStatus = Status.FINISHED;
    }

finish()是返回结果的方法,不管AsyncTask是否被取消都会将该AsyncTask的状态变更成FINISHED.


总结

最后基本上整个AsyncTask的介绍也就清晰了,本文的介绍的思路是:

1.先介绍AsyncTask的核心构成.
2.在介绍AsyncTask的工作流程:
 execute() -->executeOnExecutor() -->postResult() --> finish() 
3.还了解了AsyncTask中的串行和并行的特点,以及AsyncTask实现并行和串行的原理.

AsyncTask内部工作流程图解

AsyncTask的内部工作逻辑

这图可能画的有点复杂,其实仔细对照上文的逻辑和源码看,你一定会弄明白的.而且通读完全文之后,你会发现AsyncTask并不难,原来是这么简单.


参考文章

1.安卓艺术探索