AsyncTask源码梳理及总结

结合Android 7.0源码,全面解析AsyncTask的源码,梳理AsyncTask使用过程中的一些注意事项。
分析源码之前,我们先来梳理一下使用,AsyncTask使用示例:

  public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new MyAsyncTask().execute();
    }

    private class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... params) {
            int downPercent=1;
            publishProgress(downPercent);
            return null;
        }

       @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Void Void) {
            super.onPostExecute(Void);
        }
    }
}

AsyncTask是一个abstract类,我们要使用AsyncTask类,需要实现它的抽象方法。AsyncTask的泛型参数有3个:

  • Params 执行AsyncTask的时候传入的参数。
  • Progress 后台任务的执行的进度参数。
  • Result 后台任务执行的返回值。

    我们使用AsyncTask的时候会先构建一个AsyncTask的实例,然后调用它的execute方法,
    然后会回调AsyncTask的4个方法,onPreExecute是执行在主线程中的,我们通常会做一些初始化的操作。 doInBackground方法中我们会做一些真正的耗时操作,如果是需要通知进度的话publishProgress通过进行更新,然后在onProgressUpdate方法中更新ui组件。 onPostExecute方法在doInBackground方法执行结束后回调。

使用起来很简单,但是我们要深究的话,就有很多疑问?比如:
1.为什么AsyncTask的几个方法可以在子线程和主线程之间灵活转换呢?
2.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);
                    //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);
                }
            }
        };
    }

可以看到在构造方法中并没有执行什么代码,只是构建了两个对象mWorker ,mFuture 两个对象,然后把
mWorker 对象作为参数传递到了mFuture 对象中。这段代码看不出来什么。然后跟进execute方法中看一下:

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

what… >–< 就一行代码,只能看executeOnExecutor方法了:

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

这段代码才点有意思了呀,可以看到这里先执行了 onPreExecute()方法,
所以onPreExecute方法执行在哪个线程呢,一定就是主线程?no , no,no, 太绝对了。 应该是AsyncTask.execute方法执行在哪个线程它就执行在哪个线程。然后我们看到代码执行了exec.execute(mFuture)方法,这个execute是何许人也呢? 回头看一下我们的调用栈,发现exec其实是我们传递过来的sDefaultExecutor对象。从源码我们知道sDefaultExecutor是AsyncTask中的一个静态变量,我们来看一下赋值过程:

 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    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);
            }
        }
    }

赋值一通后,发现sDefaultExecutor其实就是一个SerialExecutor对象,那我们之前调用的sDefaultExecutor.execute方法,其实就是调用SerialExecutor的execute方法。来看一下execute方法的实现,可以看到这里是使用了一个ArrayDeque集合,将一个Runable对象压入到集合的尾部,但是这里有个比较精妙的地方,就是将Runable压入集合的时候,压入的是一个重新构建的Runable对象,然后在新的Runable的run方法中调用了传递进来Runable的run方法,而且finally方法块中执行了scheduleNext方法。

然后判断mActive是否为null,如果为null,就执行scheduleNext方法。 然后我们看一下scheduleNext方法,在这个方法中从ArrayDeque集合队首取一个Runable对象,然后丢进THREAD_POOL_EXECUTOR的
execute方法中。这里的这个THREAD_POOL_EXECUTOR又是什么呢?查看源码,我们可以知道,THREAD_POOL_EXECUTOR是一个线程池。

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

分析到这里,我们需要梳理一下思路,才能进一步向下分析。从源码看到SERIAL_EXECUTOR对象是一个静态变量,意味着SerialExecutor对象在AsyncTask类中只有一份。那就是说ArrayDeque集合也只有一份。所以不论我们构建多少个AsyncTask的实例执行execute方法,最后都会放入同一个ArrayDeque队列中。结合我们之前Runable对象压入ArrayDeque和取出的方法。可以知道,不论我们当前应用构建多少个AsyncTask的实例并执行execute方法,都是顺序执行的。 这种方法其实是一种很有意思的单线程实现机制!

继续分析:我们现在知道FutureTask的run方法是执行在子线程的,按照逻辑doInBackground也应该运行在这个方法中。我们来找一下,我们注意到FutureTask的run方法中执行了callable.call方法:

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

这里的这个callback对象就是我们构建AsyncTask的时候创建的mWorker对象,所以我们再回到看一下mWorker对象的call方法:

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

果然,哈哈。doInBackground就在这里。所以doInBackground方法是执行在子线程中的。finally代码块中执行了postResult方法,跟进去看一下:

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

好眼熟呀,原来还是用了AsyncTask内部还是用了handler机制进行线程间的消息通信的,跟着源码我们很快就发现这个handler就是InternalHandler 对象。

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

原来postResult发送消息,然后在handleMessage中执行finish方法,然后我们看一下finish方法:

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

先判断任务是否取消,如果任务已经取消就执行onCancelled,如果没有取消就回调onPostExecute,表示任务执行完成了。 既然onPostExecute是执行在handleMessage方法中,而这handler的looper对象又是Looper.getMainLooper()那么毫无疑问的onPostExecute就是执行在主线程中的。

然后我再看下publishProgress方法,

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

原来也是通过handler发送消息,然后在handleMessage中回调onProgressUpdate方法。

ok,分析到这里基本上我们就把AsyncTask的实现原理分析清楚了。 还记得前面我们说过现在的这个AsyncTask不论我们构建多少个AsyncTask实例执行execute方法,每个AsyncTask的后台任务(doInBackground)都是顺序依次执行的。 那有没有办法,让AsyncTask的后台任务并发执行呢?
当然,之前的分析我们知道因为AsyncTask是通过sDefaultExecutor来执行后台任务的,而sDefaultExecutor是SerialExecutor对象,SerialExecutor中的队列是顺序执行的,所以后台任务才会顺序执行。如果我把sDefaultExecutor替换为自己定制的线程池,不就可以实现并行执行任务了。
代码如下:

  new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 

通过AsyncTask中的这个线程池,我们可以并行执行任务。 这个线程池创建的规则是什么??

分析完成AsyncTask的源码后,我们总结一下重要的知识点,以便之后使用的时候灵活应用:
1.AsyncTask.execute方法执行在哪个线程onPreExecute就执行在哪个线程。
2.不论我们当前应用构建多少个AsyncTask的实例并执行execute方法,所有都是顺序执行的。
3.如果需要并行执行AsyncTask任务,我们需要手动配置线程池。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值