深入源码理解 AsyncTask 异步操作


我们知道,Android中的异步操作有几种,一般常见的是 new Thread + Handler,这种最常见;Activity 提供了 runOnUiThread(Runnable action) 方法;Android源生比较早的也提供了一个系统封装好的异步方法, AsyncTask ,传入参数,然后再指定的方法中做相应的操作。 AsyncTask 历经几次修改,后来基本都废弃不用了,但我还是想对它做一次源码分析,以24版本为例,分析一番。本文分析代码,它的用法网上一大堆,非本文的重点。

代码是个抽象类,意思是使用时,至少要重写 doInBackground() 方法;进入类里面,看到有个静态模版快,这个里面的代码会执行一遍,优先于类的对象的加载,看看里面的内容,是一个线程池,我们可以看看线程池的配置参数,private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 这个意思是根据手机cpu获取最佳线程个数,private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); 这个是线程池的核心线程,意思是cpu支持个数减去1,然后和4做比较,获取其中较小的数,然后再和2做比较,取出其中比较大的数,作为线程池的核心线程; 非核心线程数为 cpu 最佳个数的2倍加1,;非核心线程回收的等待时间为30,单位为秒;线程队列为线性阻塞队列,最大值为128个;线程工厂方法里面产生线程直接new,名字为 AsyncTask #1开始,最后一个数字依次累加,如 AsyncTask #2、AsyncTask #3 。

    public enum Status {
        PENDING,
        RUNNING,
        FINISHED,
    }
这是一个枚举,里面代表三种状态:准备,进行中,结束。这是一个标识记录。getHandler() 方法,是获取一个Handler,我们发现,InternalHandler 重写了 Handler,构造器里面,传入的Looper是 Looper.getMainLooper(),意味着这个Handler是个UI线程的Handler,handleMessage(Message msg) 回调的线程时UI线程,这样就保证了从子线程切换到主线的逻辑,从这里可以看出,AsyncTask 也是借助 Handler 来实现线程切换的。继续往下看, AsyncTask 的构造方法,构造中初始化了两个对象,正是上一条介绍的 FutureTask 和 Callable,看过上一章的小伙伴们,看到这里,心里基本就有谱了, AsyncTask 原来是这么一回事,线程池+FutureTask+Callable+Handler。

我们一般是创建了 AsyncTask 对象后,执行耗时操作,是调用 execute() 方法,该方法形参是一个可变参数,看看调用的方法, 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;
    }  

方法里的线程池,就是静态模版块创建的线程池,所有AsyncTask共用一个,这个线程池可以自定义,然后通过反射调用 setDefaultExecutor(Executor exec) 方法,来进行替换。继续往下看,首先是Status状态的校验,Status mStatus = Status.PENDING; 表明初始状态是 PENDING,此时如果是RUNNING或者FINISHED,抛出异常,意思就是一个AsyncTask对象执行execute()方法只能执行一次。 执行该方法,mStatus = Status.RUNNING; 状态值开始改变,然后是 onPreExecute(); 方法,这个方法是暴露给外面的,可以重写,一般是弹出个Dialog框或者什么也不做;我们传进来的参数,此时赋值给 mWorker 对象的属性,mWorker是一个Callable;下一步,重点来了,用Executor执行了 mFuture 对象。 sDefaultExecutor 这个成员变量的来源是 SERIAL_EXECUTOR,而 SERIAL_EXECUTOR 是一个 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);
            }
        }
    }

会执行execute()方法,把 mFuture 传进来 ,此时,
    new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            }

里面的 r 就是 mFuture,此时又创建一个 Runnable 把它包裹一下,然后添加到mTasks中,mTasks是一个ArrayDeque队列集合,此时 mActive 是个空对象,因此执行scheduleNext(); 方法,此时把 mTasks 队列中第一个对象赋值给 mActive,然后用默认的线程池来执行它。此时,就上面的那个Runnable,我们发现,它会执行包裹的mFuture,mFuture执行完后,会继续执行scheduleNext();方法,此时会继续从mTasks中头部取元素,赋值给mActive,然后继续用线程池执行它,直到mActive为null时停止。我们接下来分析mFuture 的 run()方法,重新回到 AsyncTask 构造方法中,我们发现,mFuture = new FutureTask<Result>(mWorker) ,结合上一条目,可以看出,会执行 WorkerRunnable 的 call() 方法,此时耗时操作 Result result = doInBackground(mParams); mParams 是我们传入的参数, result是耗时操作后得到的数据,doInBackground()这个方法就是需要我们重写的方法,此时它是处在子线程中,mTaskInvoked 设置为 true,表示做了doInBackground()操作。获取到 result 值后,调用 postResult(result) 方法,此时,创建一个 AsyncTaskResult 对象,把当前 AsyncTask 和 result 传入其中,然后把该对象传给Handler,通过what值为 MESSAGE_POST_RESULT 来做下一步操作,result.mTask.finish(result.mData[0]);  AsyncTaskResult<?> result就是AsyncTaskResult对象,result.mTask 就是 AsyncTask, result.mData[0] 就是耗时操作的  Result result值,看看 finish()方法,此时,如果之前调用过 cancel()方法, 此时会执行 onCancelled(result); 方法,如果没有取消,则执行onPostExecute(result);方法,这两个方法都可以重写。然后就是 mStatus = Status.FINISHED; 改变 status 的状态值。上一条目中,介绍 FutureTask 时,讲过最后会执行 finishCompletion() 方法,该方法会执行 done(); 方法,也就是 mFuture 中的  done() 方法,即postResultIfNotInvoked(get()),get() 返回的耗时操作后获取的值 result,然后执行postResultIfNotInvoked()方法,
    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
由于mTaskInvoked之前设置过true,此时是不会执行的,这个地方是为了以防万一的。

我们知道,在使用AsyncTask时,比如下载文件,可以显示进度,这是通过 publishProgress(Progress... values)方法设置数字,然后通过Handler,what码为MESSAGE_POST_PROGRESS,执行result.mTask.onProgressUpdate(result.mData); 代码,执行的是 AsyncTask 的 onProgressUpdate(Progress... values) 方法,我们可以重写此方法,获取values可变参数中对应的值即可。
 

扫描二维码关注公众号,回复: 5934714 查看本文章

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/87386061