AsyncTask源码解析

Posted 孙晓凯

tags:

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

虽然关于AsyncTask的文章有很多,并且文章对AsyncTask褒贬不一,本文关注的是AsyncTask的实现原理,以及它的优缺点,至于它的好坏,请读者自行辨别。

什么是AsyncTask

AsyncTask是android框架为开发者提供的一个辅助类(只是一个类,不是库)。

AsyncTask有什么用

AsynTask的出现主要是为了解决主线程和异步线程之间的交互问题的,说到这大家是不是想到了Handler,子线程,线程池等一些列的东西?如果没有,那说明你还是太嫩啊!
其实AsyncTask就是Handler,子线程,线程池等技术的封装,为的是让android开发者处理异步更方便,现在的开源库也是这个道理。

AsyncTask怎么使用

AsyncTask的使用非常简单,读者可以自行查找相关资料,如:官方文档

AsyncTask源码分析

在执行异步任务的时候,我们首先会调用:

new mAsyncTask().execute();

因此,我们就从这里下手!

首先让我们来看看execute()方法中的逻辑:

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

是不是一看就惊了个呆,竟然只有一行代码,有一行代码不代表它简单,接下来我们看看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;
    

我们看到代码中有一个if语句:

 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代表当前的运行状态,如果一个我们的asynctask正在运行,这时我们再一次执行了excute方法,就会引发throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
,而如果我们的asynctask已经运行结束了,这是我们再一次运行了excute方法,就会触发throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
异常,通过这段代码,我们应该可以明白,为什么excute方法只能被执行一次。

接下来代码把状态设置为 mStatus = Status.RUNNING;:这是因为如果没有异常,当然是正在执行啦。

接着我们就看到了我们非常熟悉的方法:onPreExecute(); (如果不熟悉,说明你还没有掌握AsyncTask的应用,就先不要看源码了,先学应用,一步一步来),这也说明了这个方法是首先被调用的,并且是在主线程中执行的。

接着往下看,出现了一行mWorker.mParams = params;这样的代码,params我们可以知道,就是我们excute(Params…param)方法中传过来的参数,可是mWorker又是什么呢?我们点进去看看:

private final WorkerRunnable<Params, Result> mWorker;

发现这是一个WorkerRunnable对象,那WorkerRunnable对象又是啥玩意,我们再点进去:

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

额,原来实现了Callable接口,其中Callable接口中有一个call()方法。

我们再来看看mWorker是怎么进行实例化的:

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

可以发现call()方法返回了Result,那Result从哪里来呢,唉 唉 唉 !!!,好像又发现了一个熟悉的方法:Result result = doInBackground(mParams);,不过据说这个方法是在子线程中执行的,在这也没见开子线程啊,这是怎么回事?这个等会再说。

我们接着向下分析,执行到了exec.execute(mFuture);这行代码,此时出现了个mFuture,这是什么东西呢?

private final FutureTask<Result> mFuture;

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

它就相当于一个线程任务(Runnable);

exec是什么东西呢?
executeOnExecutor(sDefaultExecutor, params);中传过来的参数,sDefaultExecutor是啥呢?对,点进去看看:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

额,原来是一个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);
            
        
    

其实这就是一个线程池,线程池中的任务就是mFuture:当传入一个任务后,execute方法会首先把该任务添加到mTasks中,

if (mActive == null) 
                scheduleNext();
            

这段代码表示,如果当前没有正在活动的任务,就调用scheduleNext()执行下一个AsyncTask任务,并且当一个任务执行完后,会继续调用下一个任务进行执行,直到执行完所有的任务为止,从这一点也可以看出AsyncTask是串行的。

其实在AsyncTask中有两个线程池,一个是SerialExecutor,另一个是THREAD_POOL_EXECUTOR,前者用于任务的排队,后者用于任务的执行。

还记得在上面我们遗留了一个问题,就是从哪里可以看出来doInBackground(mParams)是在子线程中执行?我们再来分析一下这段代码:

 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(mParams)在mWorker中,mWorker传给了mFuture,mFuture又加入到了线程池中,接着返回了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;
    

这段代码是不是面熟,对,就是Handler发送消息,它发送了一个MESSAGE_POST_RESULT消息,接下来我们再看看它发送到的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:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            
        
    

我们可以发现,Handler是静态的,并且Handler必须在主线程中进行创建,静态成员是在类进行加载的时候进行初始化的,这也间接说明了AsyncTask必须在主线程中进行加载,handler收到MESSAGE_POST_RESULT消息后,会执行result.mTask.finish(result.mData[0])方法:

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

此逻辑表示,如果我们取消了任务,就不再执行onPostExecute(result)方法,否则,就把结果传给onPostExecute(result).

OK,到此为止,我们平时用的三个方法在源码中都见到了,但是需要注意的是对于AsyncTask来说,google在不同的版本中改动是比较频繁的,本文用的是API23,对于其他的版本请读者按照同样的分析方式自行分析即可。

当然,AsyncTask有很多的缺点,但是根据使用场景的不同,我们可以想办法避免这些缺点,对于AsyncTask的一些缺点,请参考Android中糟糕的AsyncTask

如文章有错误,请留言告知

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

AsyncTask使用与源码解析

Android -- AsyncTask源码解析

android源码解析之-->异步任务AsyncTask

AsyncTask源码解析

在 Asynctask 中将视图作为参数发送

AsyncTask源码解析