Android 源码解析AsyncTask的工作原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pihailailou/article/details/78816878

一,写在前面

       1.1,AsyncTask的使用场景:开线程执行耗时的任务,并需要在主线程操作UI。值得一提的是,AsyncTask并不适用于做特别耗时的操作,特别耗时的操作可以用线程池+Handler来处理。
      
      1.2,本篇文章着重讲述AsyncTask的原理,不再介绍具体如何使用AsyncTask。在使用AsyncTask时会介绍它的几个方法,这里先直接给出一些结论,后面会具体分析。方法如下:
       void  onPreExecute():主线程中被调用,执行异步任务前被调用,用于做一些准备工作。
       
       Result doInBackground(Params... params):子线程中被调用,用于做一些耗时的操作。参数params,调用execute方法时传入;返回值Result,作为参数传入onPostExecute方法。
      
       void onPostExecute(Result result):主线程中被调用,用于操作UI。执行完异步任务后被调用,也就是doInBackground后被调用,参数result是方法doInBackground的返回值。

        onProgressUpdate(Progress... values):主线程中被调用,用于操作UI,显示异步任务的执行进度。在异步任务执行过程中,也就是在doInBackground方法中调用publishProgress方法,该方法会被调用。
   
      void onCancelled():异步任务被取消是调用,也就是cancel方法取消异步任务时被调用。

       1.3,使用AsyncTask注意要点(后面具体分析):
       1,AsyncTask类需要在主线程中完成加载,否则执行完异步任务后无法操作UI;
       2,AsyncTask对象在主线程中创建,且execute方法也在主线程中执行。
       3,上面提到那些方法均是被回调,不要在程序中直接调用。
       4,Android1.6以前,AsyncTask是串行执行任务;在Android1.6~3.0版本里,AsyncTask采用了线程池并发执行任务;Android3.0开始,AsyncTask在原有线程池基础上,又添加了一个SerialExecutor线程池,用于串行执行任务,避免之前版本并发执行产生的同步问题。本篇文章是在Android3.0以后的版本进行分析,也就是AsyncTask串行执行任务。
       5,AsyncTask对象只能调用一次execute方法,否则会抛出异常,也就是说一个AsyncTask对象只能执行一个任务。

       另外,在阅读本篇文章前,建议先了解Handler机制,以及线程池的知识,本篇本章将不再阐述相关要点。可以参考文章:

二,AsyncTask源码分析

      在使用AsyncTask执行一个异步任务的时候,若是需要串行执行任务,可以调用AsyncTask的execute方法。在查看该方法前,先来看看AsyncTask构造方法做了一些什么。       查看源码如下:
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
       第2行,创建了一个WorkerRunnable的匿名子类对象mWorker。WorkerRunnable是AsyncTask的内部类,结构:private static abstract class WorkerRunnable<Params, Result> implements Callable<Result>,它是一个抽象类,实现了Callable接口。
       查看Callable源码:
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
       Callable接口只有一个call方法,由于不是本篇文章重点,这里就不深究Callable了,有兴趣的哥们可以在网上搜索Callable+Future的使用。

        回到构造方法的第12行,创建FutureTask的匿名子类对象mFuture,且是有参的构造方法,参数mWorker就是WorkerRunnable的匿名子类对象。
       查看其结构:public class FutureTask<V> implements RunnableFuture<V>  , public interface RunnableFuture<V> extends Runnable, Future<V>。也就是说,FutureTask实现了Runnable接口和Future接口,由于实现了Runnable接口,可以把FutureTask看做是一个任务。

         接下来查看AsyncTask$execute方法源码:
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

    //继续查看...

    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;
    }
       第6行,调用了executeOnExecutor方法,参数sDefaultExecutor是一个SerialExecutor对象,见第1,2行。值得一提是,SerialExecutor是AsyncTask的内部类。
       第13行,在执行任务前,会对变量mStatus进行检查。mStatus有三种值:PENDING表示任务还没开始执行,RUNNING表示任务正在执行中,FINISHED表示任务已经执行完成。因此execute只能被调用一次,否则会抛出异常。
       第25行,设置变量mStatus的值为RUNNING。
       第27行,调用onPreExecute(),在任务执行前做一些准备工作,此时逻辑仍在主线程。
       第30行,调用SerialExecutor$execute方法,且参数是FutureTask的匿名子类对象mFuture。

       查看AsyncTask$SerialExecutor$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);
            }
        }
    }
       第2行,ArrayDeque是一个泛型的队列类,这里泛型声明的是Runnable,ArrayDeque可以对Runnable对象进行入队和出队的操作。例如,入队顺序是R1,R2,R3,那么出队顺序是R1,R2,R3。ArrayDeque并不是一个多么神秘的类,查看其结构的源码发现它本质是一个Collection集合,同样具有存储,删除,查询数据的操作。ArrayDeque提供了offer方法将数据对象放到队列的队尾,提供了poll方法将队首的数据对象移除出队列,并返回该数据对象。

       第3行,定义一个Runnable对象,用于存放队列中取出的Runnable对象。
      
       第6行,调用ArrayDeque$offer方法,将一个Runnable任务添加到队列里,这里称这个Runnable任务为 M。需要注意的是, 任务M并不是mFuture,它是只是一个普通的Runnable对象,里面封装了变量mFuture。看代码时,先不要管任务M里的这个run方法,只需要知道添加了一个任务M到队列里。
       
       第15行,第一次调用AsyncTask$execute方法时,mActive为null,会调用scheduleNext()。
       
       第21行,取出队列中处于队首的任务M,并将返回值也就是任务M,赋值给mActive。这样当在主线程中 再次执行AsyncTask$execute方法时,新的任务M会加入队列,但由于mActive不为空,就不会调用scheduleNext()。那么新的任务M何时才能出队列呢,接着往下看。
       
       第22行,使用THREAD_POOL_EXECUTOR线程池执行任务M,逻辑会执行到第7行的run方法。在该run方法里,执行了r.run(),也就是执行了mFuture的run方法。mFuture的run方法才是真正执行了异步任务(后面会具体分析),它是一个耗时操作,在执行完异步任务后才会执行finally里的代码,并不断重复该操作。
       
       THREAD_POOL_EXECUTOR是一个什么样的线程池呢?查看其源码:
    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);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
       ThreadPoolExecutor是Java提供的API,它是接口Executor唯一的实现类,关于它的介绍见文章  关于Android中常用的四种线程池的介绍 ,这里不再重复阐述。线程池THREAD_POOL_EXECUTOR有这样一些信息:核心线程数是cpu数加1,最大线程数是2倍的cpu数加1,非核心线程闲置超过1s会被系统回收,任务队列容量为128个。
       
       值得一提的是,使用线程池执行一个新的任务时:
       1,若线程池中仍有核心线程未使用,则启动核心线程来执行任务;
       2,若核心线程均在执行任务,将任务放入工作队列中(工作队列未满);
       3,若工作队列已满,启动一个非核心线程来执行任务(线程池中的线程未超过最大线程数);
       4,若线程池中的线程超过最大线程数,任务被拒绝执行,默认是抛出异常给调用者。

     
      小结:在Android3.0以后,调用AsyncTask$execute方法执行异步任务,是以串行的方式执行任务。
     AsyncTask使用了SerialExecutor线程池,先将任务放在队列中,第一次执行任务会调用scheduleNext方法取出队列里任务,并采用THREAD_POOL_EXECUTOR线程池执行任务。在任务执行完毕后,才调用scheduleNext方法取出下一个任务并执行之,否则不会取出队列里的任务。

三,采用线程池THREAD_POOL_EXECUTOR,执行异步任务的过程

       前面讲到真正执行异步任务是由线程池THREAD_POOL_EXECUTOR完成,最终会调用变量mFuture的run方法。
       mFuture是一个FutureTask类型的变量,查看FutureTask$run方法源码:
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }


    public void run() {

        //...

        Callable<V> c = callable;
        if (c != null && state == NEW) {
	    
	    //...

	    result = c.call();
	       
            //...
	    
	}

        //...

    }
       第13行,变量callable在构造函数中初始化,即前面提到的WorkerRunnable类型的对象mWorker。
       第18行,调用Callable$call方法,即WorkerRunnable的call方法。

       查看变量mWorker初始化的源码:
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
	mTaskInvoked.set(true);

	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
	//noinspection unchecked
	return postResult(doInBackground(mParams));
    }
};
       第7行,先执行doInBackground(mParams),并将其的返回值作为postResult方法的参数。需要注意的是,此时任务的执行仍在线程池THREAD_POOL_EXECUTOR中,因此doInBackground方法是在子线程中被调用,可以重写该方法并执行耗时操作。

       查看AsyncTask$postResult方法源码:
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    //继续查看...

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

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
       第3行,创建一个Message,消息的唯一标示what是MESSAGE_POST_RESULT,obj是AsyncTaskResult对象。AsyncTaskResult中封装了AsyncTask的引用,Result对象,至于为何要如此封装后面会给出解释。
       第5行,发送消息,将任务的执行切换InternalHandler所在的线程。

       查看InternalHandler源码:
    private static final InternalHandler sHandler = new InternalHandler();

    private static class InternalHandler extends Handler {
        @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;
            }
        }
    }
       第1行,sHandler是一个静态类型的变量,执行完耗时操作后要操作UI,sHandler只能在主线程中创建,因此AsyncTask类在主线程中被加载。
       第3行,InternalHandler是Handler的子类,它是AsyncTask中的静态内部类,在类加载的时候就完成了。
       第7行,取出Message中的数据obj,是一个AsyncTaskResult类型的对象,AsyncTaskResult也是AsyncTask的一个静态内部类。
       第11行,调用AsyncTaskResult的字段mTask获取AsyncTask的实例,并调用finish方法,此时在主线程中执行。这里必须要通过AsyncTaskResult这个静态内部类来获取,若直接使用this,由于类加载时this没有值,会编译出错。因此,需要采用AsyncTaskResult这个静态内部类,来封装AsyncTask的引用。

       查看AsyncTask$finish方法源码:
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
       第3行,如果任务被取消,也就是调用cancel方法,则onCancelled被调用。我们可以重写onCancelled方法,把任务取消后想做操作放在里面。
       第5行,调用onPostExecute方法,它是在doInBackground方法之后被执行。我们可以重写onPostExecute方法,将操作UI的代码放在该方法里。
       第7行,将变量mStatus的值设置为FINISHED。

四,最后

       关于调用publishProgress方法后,onProgressUpdate方法被调用的原因就不再分析了,比较容易分析,有兴趣哥们可以自行验证。
      
        AsyncTask中封装了两个线程池和Handler,自定义的线程池SerialExecutor保证多个任务的串行执行,真正执行异步任务的是线程池THREAD_POOL_EXECUTOR。                 
       Android3.0之后,若需要并行的方式执行任务,可以直接调用AsyncTask$executeOnExecutor(THREAD_POOL_EXECUTOR,params)。将任务的执行交给线程池THREAD_POOL_EXECUTOR,而不再需要线程池SerialExecutor。关于THREAD_POOL_EXECUTOR,前面已经做了详细分析了。

       到这里,对AsyncTask工作原理的分析就结束了~      ^_^



       









    



       

       

猜你喜欢

转载自blog.csdn.net/pihailailou/article/details/78816878