Android知识Android开发Android技术知识

AsyncTask源码分析

2016-12-28  本文已影响0人  yk_looper

使用AsyncTask的一般步骤是:

  • 定义一个类继承自AsyncTask,实现抽象方法

那么就一步一步来分析AsyncTask的实现原理,首先看构造函数

/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */
public AsyncTask() {
    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);
        }
    };
    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);
            }
        } 
   }}

构造函数很简单,就是初始化了2个全局的变量mWorkermFuture,并在创建mFuture的时候把mWorker作为参数传递进去。其中mWorker实现了Callable,mFuture是一个FutureTask。关于Callable和FutureTask请参考

然后执行execute()方法

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

调用了executeOnExecutor(sDefaultExecutor, params);params就是execute()传入的参数,这个sDefaultExecutor是在哪里定义的呢?

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

原来sDefaultExecutor是AsyncTask内部的一个常量指向的是SerialExecutor。继续跟进executeOnExecutor().

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

从这个函数可以看出,AsyncTask一共有三种状态,并且非PENDING状态下调用execute()都会抛出异常

  • PENDING 初始状态

第一次调用execute()会把状态置为RUNNING,任务完成时会把状态置为FINISHED。这就要求一个AsyncTask只能执行一次execute()只能完成一个后台任务,如果需要处理多个任务,只有重新创建一个AsyncTask

回到上面的函数,可以看到 onPreExecute()被最先调用,所以我们可以在这个回调中做一些初始化操作比如开始加载动画(这就是execute()一般都会在UI线程调用的原因)。然后把execute(Parem... param)传递过来的param赋值给在构造中初始化好的mWorkermParams变量。

再执行exec.execute(mFuture),其中exec是上文可以知道指向的是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就是mFuture
                    r.run();
                }
               finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

可以看到这里调用了mFuture的run()方法,在来看看这个方法

public void run() {
    Callable<V> c = callable;
    if (c != null && state == NEW) { 
       V result;
        boolean ran;
      //这个的c就是mFuture创建时候,传递的参数mWorker 
        result = c.call();
        ran = true;
    }
}

注意c.call(),这个c什么呢?其实就是在AsyncTask构造中创建mFuture的参数mWorker,转了一大圈其实就是调用了mWorker.call()方法

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //调用doInBackground()做后台任务,mParams就是调用execute()时传递的参数
        Result result = doInBackground(mParams); 
        Binder.flushPendingCommands();
        return postResult(result);
    }
};

可以看到在这个方法中回调了doInBackground(mParams),而参数就是我们调用execute()时传递的参数。因为WorkerRunnable是实现Runnable接口的所以doInBackground的确是在子线程中执行的。

doInBackground(mParams)完成我们自己的后台逻辑之后,把结果作为参数传递给了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;
}
private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }        return sHandler;
    }
}
private static class InternalHandler extends Handler {
    public InternalHandler() {
        //获取主线程Looper来构造Handler
        super(Looper.getMainLooper());
    }
    @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;
        }
    }
}

这里代码比较简单,就是后台任务返回结果后,把结果封装进message发送,Handler接受到消息后,根据消息类型执行不同的逻辑。我们看到有2种消息类型,MESSAGE_POST_RESULT就是后台任务完成了,而MESSAGE_POST_PROGRESS看命名也大概知道了,没错,就是我们调用publishProgress()更新进度的时候就会发送这个类型的消息

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

注意 : 3.0 以后,默认会使用主线程的Looper来构造handler,所以不管AsyncTask在那个线程创建或者execute()在那个线程调用,onPostExecute(result)publishProgress()都是在主线程执行

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

finish()中判断后台任务时候取消过,是,onPostExecute(result)就不再调用,而是回调onCancelled(result)。并且设置状态,保证AsyncTask只能执行一次。

3.0之后,AsyncTask默认是串行执行任务的,来看看是怎么实现串行的

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就是mFuture
                    r.run();
                }
               finally {
//上一个任务执行完毕后,才会从队列中取出下一个任务
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
//取出队列头部的任务
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

调用AsyncTask.execute()后会调用SerialExecutor.execute(),这是AsyncTask中维护的一个静态串行控制器,不管创建多少个AsyncTask都会使用同一个SerialExecutor来完成串行控制

ArrayDeque是一个线性双向队列,每一次调用SerialExecutor.execute()都会把传进来的任务(FutureTask)加入到这个队列中。首先判断mActive是否为空第一次肯定是null,调用schecduleNext()会从队列中取出头部任务,交给AsyncTask维护的线程池去执行,第二次mActive!=nullschecduleNext()就执行不到,那如果第二个任务怎么执行呢?注意try..finally中的逻辑,在前一个.run执行完之后,才会又一次schecduleNext()被执行。这就实现了任务的串行控制

SerialExecutor是一个串行控制器,他会把加入的任务按顺序加入到任务队列中,给任务排序,然后一个一个去处交给AsyncTask中维护的线程池执行。

AsyncTask中维护的线程池是一个ThreadPoolExecutor,这是一个并发线程池,3.0以前默认同一时刻运行的线程数是5个,最大线程数是128个,3.0以后根据CPU配置而定。

所以,在3.0以后我们也是可以让AsyncTask并行执行的,就是不让SerialExecutor来控制任务的串行,即调用AsyncTask.executeOnExcutor(THREAD_POOL_EXECUTOR),参数参入线程池的引用,这样任务就跳过了排序,被直接交给了线程池去执行

AsyncTask的缺点

AsyncTask我们肯定都用过,优点就不说了,使用简单,简化代码,过程可控。我们来说一说它的缺点

的确以上都是在使用AsnycTask过程中可能遇到的问题,但是我认为这些都不是AsyncTask自身的缺陷,而是程序猿代码设计的问题,因为使用Thread+handler或者相似的类库都可能出现同样的问题。比如内存泄漏,我们完全可以用静态内部类+弱引用、或者是用MVP解耦来解决。对于cancle()可能不正常取消,那也是Thread的问题。

** AsyncTask真正的缺点是处理多个任务和控制串行、并行**

task1.execute()
task2.execute()
task3.execute()

我们还得关心每个task的回调,每一个任务的cancle处理,并且如果每个任务需要的泛型参数不同的话,就的写多个AsyncTask。

总结

所以,3.0以前创建AsynacTask必须在主线程,而3.0以后只要不在onPreExecute(),publishProgress(),onPostExecute()回调中更新UI,AsyncTask完全可以不在主线程创建。但是这明显跟我们日常的使用相悖


最后附一篇任玉刚大神分析的源码,代码注释非常详细

上一篇 下一篇

猜你喜欢

热点阅读