Android AsyncTask

作者: 撑起头顶的天 | 来源:发表于2018-02-02 16:24 被阅读1次

    AsyncTask简介

    AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后会把执行的进度和最终结果传递给主线程并更新UI。AsyncTask本身是一个抽象类它提供了Params、Progress、Result 三个泛型参数,其类声明如下:

    public abstract class AsyncTask<Params, Progress, Result> 
    

    由类声明可以看出AsyncTask抽象类确实定义了三种泛型类型 Params,Progress和Result,它们分别含义如下:

    Params :启动任务执行的输入参数,如HTTP请求的URL
    Progress : 后台任务执行的百分比
    Result :后台执行任务最终返回的结果类型

    关键方法

    (1)onPreExecute(), 该方法在主线程中执行,将在execute(Params… params)被调用后执行,一般用来做一些UI的准备工作,如在界面上显示一个进度条。
    (2)doInBackground(Params…params), 抽象方法,必须实现,该方法在线程池中执行,用于执行异步任务,将在onPreExecute方法执行后执行。其参数是一个可变类型,表示异步任务的输入参数,在该方法中还可通过publishProgress(Progress… values)来更新实时的任务进度,而publishProgress方法则会调用onProgressUpdate方法。此外doInBackground方法会将计算的返回结果传递给onPostExecute方法。
    (3)onProgressUpdate(Progress…),在主线程中执行,该方法在publishProgress(Progress… values)方法被调用后执行,一般用于更新UI进度,如更新进度条的当前进度。
    (4)onPostExecute(Result), 在主线程中执行,在doInBackground 执行完成后,onPostExecute 方法将被UI线程调用,doInBackground 方法的返回值将作为此方法的参数传递到UI线程中,并执行一些UI相关的操作,如更新UI视图。
    (5)onCancelled(),在主线程中执行,当异步任务被取消时,该方法将被调用,要注意的是这个时onPostExecute将不会被执行。

    AsyncTask的工作原理

    1.入口execute方法

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

    execute方法只是一个壳子,直接调用了executeOnExecutor(sDefaultExecutor, params),其中sDefaultExecutor是一个串行的线程池,接着看看sDefaultExecutor内部实现:

    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.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
    //串行线程池类,实现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() { //插入一个Runnble任务
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //判断是否有Runnable在执行,没有就调用scheduleNext方法
            if (mActive == null) {
                scheduleNext();
            }
        }
    
        protected synchronized void scheduleNext() {
          //从任务队列mTasks中取出任务并放到THREAD_POOL_EXECUTOR线程池中执行.
          //由此也可见任务是串行进行的。
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    

    从源码可以看出,ArrayDeque是一个存放任务队列的容器(mTasks),任务Runnable传递进来后交给SerialExecutor的execute方法处理,SerialExecutor会把任务Runnable插入到任务队列mTasks尾部,接着会判断是否有Runnable在执行,没有就调用scheduleNext方法去执行下一个任务,接着交给THREAD_POOL_EXECUTOR线程池中执行,由此可见SerialExecutor并不是真正的线程执行者,它只是是保证传递进来的任务Runnable(实例是一个FutureTask)串行执行,而真正执行任务的是THREAD_POOL_EXECUTOR线程池,当然该逻辑也体现AsyncTask内部的任务是默认串行进行的。顺便看一下THREAD_POOL_EXECUTOR线程池的声明:

    //CUP核数
    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;
    //非核心线程的存活时间1s
    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);
    

    关于sDefaultExecutor,我们先了解到这,回到之前execute方法内部调用的executeOnExecutor方法的步骤,先来看看executeOnExecutor都做了些什么事?其源码如下:

    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()在此执行了!!!
       onPreExecute();
       //参数传递给了mWorker.mParams
       mWorker.mParams = params;
       //执行mFuture任务,其中exec就是传递进来的sDefaultExecutor
       //把mFuture交给线程池去执行任务
       exec.execute(mFuture);
    
       return this;
        }
    

    从executeOnExecutor方法的源码分析得知,执行任务前先会去判断当前AsyncTask的状态,如果处于RUNNING和FINISHED状态就不可再执行,直接抛出异常,只有处于Status.PENDING时,AsyncTask才会去执行。然后onPreExecute()被执行的,该方法可以用于线程开始前做一些准备工作。接着会把我们传递进来的参数赋值给 mWorker.mParams ,并执行开始执行mFuture任务,那么mWorker和mFuture到底是什么?先看看mWorker即WorkerRunnable的声明源码:

    //抽象类
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
    

    WorkerRunnable抽象类实现了Callable接口,因此WorkerRunnable本质上也算一个Callable对象,其内部还封装了一个mParams的数组参数,因此我们在外部执行execute方法时传递的可变参数最终会赋值给WorkerRunnable的内部数组mParams,这些参数最后会传递给doInBackground方法处理,这时我们发现doInBackground方法也是在WorkerRunnable的call方法中被调用的,看看其源码如下:

    public AsyncTask() {
       //创建WorkerRunnable mWorker,本质上就是一个实现了Callable接口对象
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //设置标志
                mTaskInvoked.set(true);
    
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //执行doInBackground,并传递mParams参数
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                //执行完成调用postResult方法更新结果
                return postResult(result);
            }
        };
    //把mWorker(即Callable实现类)封装成FutureTask实例
    //最终执行结果也就封装在FutureTask中
        mFuture = new FutureTask<Result>(mWorker) {
            //任务执行完成后被调用
            @Override
            protected void done() {
                try {
                 //如果还没更新结果通知就执行postResultIfNotInvoked
                    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时,不仅创建了mWorker(本质实现了Callable接口的实例类)而且也创建了FutureTask对象,并把mWorker对象封装在FutureTask对象中,最后FutureTask对象将在executeOnExecutor方法中通过线程池去执行。给出下图协助理解:


    协助理解

    AsynTask在初始化时会创建mWorker实例对象和FutureTask实例对象,mWorker是一个实现了Callable线程接口并封装了传递参数的实例对象,然后mWorker实例会被封装成FutureTask实例中。在AsynTask创建后,我们调用execute方法去执行异步线程,其内部又直接调用了executeOnExecutor方法,并传递了线程池exec对象和执行参数,该方法内部通过线程池exec对象去执行mFuture实例,这时mWorker内部的call方法将被执行并调用doInBackground方法,最终通过postResult去通知更新结果。关于postResult方法,其源码如下:

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

    显然是通过Handler去执行结果更新的,在执行结果成返回后,会把result封装到一个AsyncTaskResult对象中,最后把MESSAGE_POST_RESULT标示和AsyncTaskResult存放到Message中并发送给Handler去处理,这里我们先看看AsyncTaskResult的源码:

    private static class AsyncTaskResult<Data> {
            final AsyncTask mTask;
            final Data[] mData;
    
            AsyncTaskResult(AsyncTask task, Data... data) {
                mTask = task;
                mData = data;
            }
        }
    

    显然AsyncTaskResult封装了执行结果的数组以及AsyncTask本身,这个没什么好说的,接着看看AsyncTaskResult被发送到handler后如何处理的。

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            //获取主线程的Looper传递给当前Handler,这也是为什么AsyncTask只能在主线程创建并执行的原因
            super(Looper.getMainLooper());
        }
    
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
        //获取AsyncTaskResult
            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:
                //执行onProgressUpdate方法,自己实现。
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
    

    从Handler的源码分析可知,该handler绑定的线程为主线线程,这也就是为什么AsyncTask必须在主线程创建并执行的原因了。接着通过handler发送过来的不同标志去决定执行那种结果,如果标示为MESSAGE_POST_RESULT则执行AsyncTask的finish方法并传递执行结果给该方法,finish方法源码如下:

    private void finish(Result result) {
            if (isCancelled()) {//判断任务是否被取消
                onCancelled(result);
            } else {//执行onPostExecute(result)并传递result结果
                onPostExecute(result);
            }
            //更改AsyncTask的状态为已完成
            mStatus = Status.FINISHED;
        }
    

    该方法先判断任务是否被取消,如果没有被取消则去执行onPostExecute(result)方法,外部通过onPostExecute方法去更新相关信息,如UI,消息通知等。最后更改AsyncTask的状态为已完成。到此AsyncTask的全部流程执行完。
      这里还有另一种标志MESSAGE_POST_PROGRESS,该标志是我们在doInBackground方法中调用publishProgress方法时发出的,该方法原型如下:

    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
        //发送MESSAGE_POST_PROGRESS,通知更新进度条
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
    

    总结

    AsyncTask的整体流程基本分析完,最后来个总结吧:当我们调用execute(Params… params)方法后,其内部直接调用executeOnExecutor方法,接着onPreExecute()被调用方法,执行异步任务的WorkerRunnable对象(实质为Callable对象)最终被封装成FutureTask实例,FutureTask实例将由线程池sExecutor执行去执行,这个过程中doInBackground(Params… params)将被调用(在WorkerRunnable对象的call方法中被调用),如果我们覆写的doInBackground(Params… params)方法中调用了publishProgress(Progress… values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress… values)方法将被调用;最后如果FutureTask任务执行成功并返回结果,则通过postResult方法发送一条MESSAGE_POST_RESULT的消息去执行AsyncTask的finish方法,在finish方法内部onPostExecute(Result result)方法被调用,在onPostExecute方法中我们可以更新UI或者释放资源等。这既是AsyncTask内部的工作流程,可以说是Callable+FutureTask+Executor+Handler内部封装。

    总图

    总图.png

    赞赏

    微信 支付宝

    参考

    http://blog.csdn.net/javazejian/article/details/52462830
    http://blog.csdn.net/javazejian/article/details/52464139

    相关文章

      网友评论

        本文标题:Android AsyncTask

        本文链接:https://www.haomeiwen.com/subject/wrcezxtx.html