Android AsyncTask的使用及源码解析

AsyncTask, 翻译成中文就是“异步任务”,顾名思义它就是用来处理不同线程相互协作的问题的,是个轻量级的异步类。
常用的场景比如从网络下载图片并显示的过程,“下载”这个过程得放在后台线程里,而更新进度以及显示图片需要在主线程完成,这时候,用AsyncTask就很方便了。

一、AsyncTask的使用

先介绍一下AsyncTask类的一般使用方法和注意事项,然后再进行源码对照分析:
AsyncTask类是个抽象类,使用前必须继承并实现它的重要方法:

class AsyncDownload extends AsyncTask<String, Integer, Boolean> {//泛型参数Params, Progress, Result
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        XXX //操作
    }

    @Override
    protected Boolean doInBackground(String... params) {
        Boolean result;
        Integer progress;
        XXX //操作
        publishProgress(progress);//可选。如果调用的话onProgressUpdate(progress)才会执行
        XXX //操作
        return result;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        XXX //操作
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);
        XXX //操作
    }
}

那么怎么使用呢?例如下面这样创建对象并调用execute()即可:

AsyncDownload asyncDownload = new AsyncDownload ();
asyncDownload.execute(str); //String类型参数

上面传入的str为一个String字符串。具体使用时可以按需传各种类型的参数,但是要和泛型参数的第一个参数类型保持一致。

二、AsyncTask类的方法,参数和返回值简介

上面只是个示例,这里需要介绍下这AsyncTask的几个方法和三个泛型参数。
需要注意的是,这几个方法不能手动调用,都是自动执行的,我们只需要给出具体实现即可。
而且,一个AsyncTask实例只能被执行一次,多次调用时将会出现异常 。

  1. onPreExecute():异步任务最先被调用的方法,可以在这里做些准备操作,比如把进入条显示出来。如果不需要的话就不用实现。无参数,无返回值。

  2. doInBackground(String… params):这个在AsyncTask类里是个抽象方法,所以必须要有实现。这个方法里用来处理后台逻辑,比如做下载操作,因为它是在新线程里运行的,在onPreExecute()之后被调用。

    此方法的参数是哪里传入的呢?就是上面asyncDownload.execute(str) 传入的String字符串,这个参数类型和AsyncTask的第一个泛型参数的类型需要一致。另外此方法的返回值,需要和AsyncTask的第三个泛型参数的类型保持一致。如上面的例子,都是Boolean类型。

  3. onProgressUpdate(progress):如果我们在 doInBackground(String… params)方法中调用了publishProgress(progress),则表示发出“进度有更新”的通知,于是onProgressUpdate(progress)会被调用。onProgressUpdate(progress)用来处理进度更新的操作,运行在UI线程,它的参数类型,和AsyncTask的第二个泛型参数的类型需要一致,上面的例子里,都是Integer类型。

  4. onPostExecute(Boolean result): 此方法是任务结束后,被调用的方法(不管任务是完成了或者失败,都会被调用,任务取消不会被调用)。它的参数就是doInBackground(String… params)的返回值。此方法也运行在UI线程。

总结一下,除了doInBackground(String… params)运行在新线程,其他的包括调用AsyncTask的execute()方法,都运行在UI线程。

三、AsyncTask类源码解析

好了,上面抛出了AsyncTask的一些使用结论,下面,我们用源码对照分析,来证明上面的结论。

第一段:从execute() 到 onPreExecute() —————
当我们实现了一个AsyncTask,然后调用了它的execute()方法后,它是怎么工作的呢?
我们先来看android.os.AsyncTask类的execute()方法:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);//处理完成后返回自己
}

此方法调用了一个executeOnExecutor()方法来处理,传入了一个sDefaultExecutor,这个sDefaultExecutor是哪来的?

可以找到:

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

所以,sDefaultExecutor就是一个静态变量。

它的实现暂时先不用管。继续看executeOnExecutor()对传入的SerialExecutor做了什么:

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

这里就是重点了,首先对此task的状态做了判断。这里先解释下:
AsyncTask会有三种状态:PENDING就绪状态,RUNNING运行状态,FINISHED结束状态。这mStatus就是表示这三种状态的标志,会随着运行而变化。这个比较简单,不细讲。

从上面可以看到,如果一个task执行了execute()时是RUNNING或者FINISHED状态,就会抛出异常。这是我们前面提到的注意点之一。

如果运行到这里,mStatus是PENDING状态,则符合运行条件,mStatus会更新为RUNNING。然后onPreExecute()就执行了。所以它是最先执行的方法。

第二段:任务的初始化 —————

接下来两行:

    mWorker.mParams = params;
    exec.execute(mFuture);

mWorker和mFuture是什么?追代码,这里是定义:

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

然后它们都是在AsyncTask的构造方法里被初始化的, 也就是我们new AsyncTask() 创建对象的时候,mWorker和mFuture就被初始化了:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {//WorkerRunnable实现了Callable接口
        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) {//FutureTask实现了Runnable和Future接口
        @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);
            }
        }
    };
}

看到了熟悉的doInBackground()方法,它就是在上面的call()中被调用的。那么call()又是哪里被调用呢?先不管。
明白了mWorker和mFuture是在AsyncTask构造方法中被创建的,再回头看上面蓝色字体那两行,其中exec.execute(mFuture)的exec是传进来的一个对象,也就是之前那个sDefaultExecutor,是个静态的SerialExecutor类型的对象。那么我们现在去看看这个SerialExecutor类的具体定义,以及它的execute()方法。

第三段:doInBackground() —————
SerialExecutor类是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.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

“Serial”,翻译就是“串行化的”。那么“SerialExecutor”我们就可以猜到它是一个用于串行处理task的处理器。它是怎么实现串行的呢?
上面可以看到,类里有个mTasks,是个双端队列,当SerialExecutor的execute()执行的时候,传入的Runnable r 就会被包装成一个新Runnable对象,放入队列里。

为什么要把传入的Runnable r又包装成一个新的Runnable对象呢?这就是为了实现串行,新的Runnable对象的run()方法不仅调用了传入的r 的run() 方法,而且还调用了scheduleNext(),使得Runnable从队列中依次取出,依次执行。

类里有个mActive变量,这个变量就是正在处理的task,它是对 r , 也就是传入的mFuture(FeatureTask对象)的包装。如果它为null(第一次执行必然为null),则从队列中取出下一个Runnable对象,赋给mActive,并且用线程池来执行(THREAD_POOL_EXECUTOR.execute(mActive))。

THREAD_POOL_EXECUTOR的解释点这里

mFuture是个FeatureTask对象,Executor执行FeatureTask时,会调用FeatureTask的run()方法,然后run()方法中又会调用构造FeatureTask对象时传入的Callable,也就是mWorker的call()方法(这部分代码就不贴了)。call()方法的实现见上面AsyncTask的构造方法中,我们再贴出来看下:

mWorker = new WorkerRunnable<Params, Result>() {//WorkerRunnable实现了Callable接口
    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()是运行在新线程了。

第四段:onPostExecute() —————

doInBackground()的返回值又会传入postResult()方法中,看下这个方法:

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

也就是把doInBackground()的返回结果,用handler进行处理了,会怎么处理呢?这里getHandler()会得到一个静态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;
        }
    }
}

上面doInBackground()返回之后,handler发送的消息是MESSAGE_POST_RESULT,也就是会走到上面result.mTask.finish(result.mData[0])这里。

这个result是什么呢?上面两块代码看看就明白,它是handler发送的消息携带的obj,也就是发送消息时new AsyncTaskResult(this, result)传入的this,也就是当前的AsyncTask对象。

它的finish()做了什么?

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

这里看到了熟悉的onPostExecute()了。很简单,如果AsyncTask对象正常执行的话,onPostExecute()就会被调用,传入的result就是postResult()传入的参数,也就是doInBackground()的返回值。同时mStatus 状态改为FINISHED。也就是这个异步任务到此结束。

第五段:onProgressUpdate() —————
在上面handler对消息的处理中,还有个case MESSAGE_POST_PROGRESS,这里也看到了熟悉的onProgressUpdate()方法。那么什么时候handler会发送这个消息呢?搜一下,找到了这一处:

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

publishProgress()无调用,需要我们自行按需调用。看到这里就很明确了,重温一下文章开始部分的那个结论:如果我们在doInBackground()里调用了publishProgress()的话,onProgressUpdate()就会得到执行。从这里就知道为什么了。publishProgress()执行时handler发送消息,然后接收消息时调用的onProgressUpdate()。

第六段:AsyncTask任务的取消 —————
还有个问题,AsyncTask的构造方法中,初始化FutureTask对象mFuture时重写了done()方法,它什么情况下会被调用呢?

    mFuture = new FutureTask<Result>(mWorker) {//FutureTask实现了Runnable和Future接口
        @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);
            }
        }
    };

在源码中查找就知道,如果我们调用了AsyncTask的cancel()方法,

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

则FutureTask的cancel()方法被调用,在那里会调用finishCompletion(),然后done()最后就会执行,这里就不贴代码了。

done()执行会怎么样?看done()里调用了postResultIfNotInvoked(get()),这个get()返回的是callable()的返回值,也就是前面mWorker的call()方法的返回值(postResult()的返回值),

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

关于postResult()可返回去看前面的分析,postResult()里handler会发送消息,如果是cancel的,那么处理消息时最后会走到onCancelled()里,onCancelled()是个protected的空方法,我们可自行实现,如果任务取消可以在这里处理。

AsyncTask的主要工作原理就分析到这里,从上面我们知道它际也是用Handler实现的异步操作,用线程池来处理任务。

第七段:AsyncTask的线程池 —————

这里对AsyncTask的线程池做个分析,从上面的源码中,可以看到,任务执行过程中用到了两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR,其中SerialExecutor是用来把任务放到队列使其串行处理 ,而THREAD_POOL_EXECUTOR是真正执行任务的那个。

看下THREAD_POOL_EXECUTOR的定义:

public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

其中几个参数如下:

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;//最大数量受CPU限制
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);

如果我们想让任务并行处理,怎么办呢?

AsyncTask里面有两种线程池供我们指定使用:
1.THREAD_POOL_EXECUTOR, 异步线程池
2.SERIAL_EXECUTOR,同步线程池
如果直接调用execute的话,是刚刚说的那样,先SERIAL_EXECUTOR把任务串行化,再使用THREAD_POOL_EXECUTOR执行任务。

如果想任务并行处理,可以调用executeOnExecutor(Executor exec, Params… params)方法,传入THREAD_POOL_EXECUTOR。这样就没有串行化这一步了,直接由THREAD_POOL_EXECUTOR处理。具体原理可从上面源码分析第一段和第三段找。也可以自定义线程池传入。

要注意: Android 3.0以前,默认情况下AsyncTask是串行执行,3.0以后才有executeOnExecutor方法。

四、总结

AsyncTask各个方法分工明确,使用简单方便,适用于在执行耗时任务的同时更新UI线程的情况,但是当任务特别耗时的时候,就不适合用AsyncTask了,因为AsyncTask要么串行执行,要么并行执行上对线程数量作了约束。特别耗时的任务还是采用线程池来处理比较好。

猜你喜欢

转载自blog.csdn.net/fenggering/article/details/80949154