Android O:AsyncTask分析

Posted ZhangJianIsAStark

tags:

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

这一篇博客主要分析一下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的基本概念、使用方式及源码。

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

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

Android AsyncTask分析

Async分析

Android中AsyncTask基本用法与源码分析(API 23)

Android AsyncTask内部线程池异步执行任务机制简要分析

Android多线程分析之五:使用AsyncTask异步下载图像

Android异步任务AsyncTask的使用与原理分析