Android 7.0 AsyncTask分析

这一篇博客主要分析一下Android 7.0中AsyncTask的源码。


1、基本定义

在分析AsyncTask的源码前,先来结合原生文档,回顾一下AsyncTask的定义。

AsyncTask enables proper and easy use of the UI thread.
This class allows to perform background operations and publish results on the UI thread without
having to manipulate threads and/or handlers.

AsyncTask主要用于帮助UI线程执行后台操作,并将执行结果递交给UI线程。
使用AsyncTask执行后台工作,可以简化UI线程的设计,避免在UI线程中操作额外的线程和Handler。

AsyncTasks should ideally be used for short operations (a few seconds at the most.)
If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs
provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

AsyncTask执行的后台操作不应该耗时过长,若需要利用线程执行耗时任务,最好使用并发库中的工具类。

An asynchronous task is defined by 3 generic types, called Params, Progress and Result.

Params, the type of the parameters sent to the task upon execution.
Progress, the type of the progress units published during the background computation.
Result, the type of the result of the background computation.

Not all types are always used by an asynchronous task.
To mark a type as unused, simply use the type Void.

AsyncTask是一个模板类,定义了3个模板参数,分别是Params、Progress和Result。其中:
Params: AsyncTask执行时的输入参数的类型;
Progress: 后台执行任务时,计算出的完成进度的数值类型;
Result: AsyncTask执行完毕后的返回类型。

这些模板参数并不一定需要全部定义,对于不用的参数,指定其为Void即可,例如:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

When an asynchronous task is executed, the task goes through 4 steps:

AsyncTask定义了4个回调接口,分别为onPreExecute、doInBackground、onProgressUpdate和onPostExecute。

onPreExecute, invoked on the UI thread before the task is executed.
This step is normally used to setup the task, for instance by showing a progress bar in the user interface.

UI线程启动AsyncTask前,会先调用AsyncTask的onPreExecute。
这个接口中的操作主要用于做一些准备工作,例如创建任务对应的进度条。

doInBackground, invoked on the background thread immediately after onPreExecute() finishes executing.
This step is used to perform background computation that can take a long time.
The parameters of the asynchronous task are passed to this step.
The result of the computation must be returned by this step and will be passed back to the last step.
This step can also use {publishProgress to publish one or more units of progress.
These values are published on the UI thread, in the ProgressUpdate step.

当UI线程执行完onPreExecute函数后,后台线程将立即调用doInBackground接口。
doInBackground接口负责完成主要的耗时工作,AsyncTask执行时的输入参数将传递到doInBackground接口。
doInBackground中完成耗时工作后,必须返回执行结果(与模板参数Result一致)。

在doInBackground中可以调用publishProgress接口更新进度条,
进度条的数值将通过onProgressUpdate接口更新到UI界面。

onProgressUpdate, invoked on the UI thread after a call to publishProgress. The timing of the execution is undefined.
This method is used to display any form of progress in the user interface while the background computation is still executing.
For instance, it can be used to animate a progress bar or show logs in a text field.

当后台线程调用了publishProgress接口后,UI线程将在合适的时间内调用onProgressUpdate函数(由系统决定调用时间)。
在onProgressUpdate函数中,UI线程可以更新进度条信息。

onPostExecute, invoked on the UI thread after the background computation finishes.
The result of the background computation is passed to this step as a parameter.

后台线程完成工作后,UI线程将调用onPostExecute接口,doInBackground的返回结果将作为该函数的输入参数。

AsyncTask must be subclassed to be used. The subclass will override at least one method doInBackground,
and most often will override a second one onPostExecute.

在使用AsyncTask时,通常定义继承该类的子类,并覆盖其中的doInBackground、onPostExecute等函数。

A task can be cancelled at any time by invoking cancel(boolean).
Invokingthis method will cause subsequent calls to isCancelled() to return true.

After invoking this method, onCancelled(Object), instead of
onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.

To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled()
periodically from doInBackground(Object[]), if possible (inside a loop for instance.)

调用AsyncTask的cancel接口可以结束任务。
当调用了cancel接口后,doInBackground结束后,将回调onCancelled接口而不是onPostExecute接口。
为了尽可能快的相应结束命令,应该在doInBackground函数中周期性的检测isCancelled接口的返回值。

There are a few threading rules that must be followed for this class to work properly:
1. The AsyncTask class must be loaded on the UI thread.
2. The task instance must be created on the UI thread.
3. execute must be invoked on the UI thread.
4. Do not call onPreExecute(), onPostExecute, doInBackground,
onProgressUpdate manually.
5. The task can be executed only once (an exception will be thrown if a second execution is attempted.)

AsyncTask的加载、创建和启动必须在UI线程完成。
不要手动调用AsyncTask的onPreExecute、onPostExecute、doInBackground和onProgressUpdate接口。
每个AsyncTask只能执行一次,重复执行将会抛出异常。

AsyncTask guarantees that all callback calls are synchronized in such a way that the following
operations are safe without explicit synchronizations.
1. Set member fields in the constructor or onPreExecute, and refer to them
in doInBackground.
2. Set member fields in doInBackground, and refer to them in
onProgressUpdate and onPostExecute.

为了保证线程安全,doInBackground中引用的AsyncTask成员变量,必须事先初始化于构造函数或onPreExecute。
doInBackground中初始化的成员变量,由onProgressUpdate和onPostExecute使用。

When first introduced, AsyncTasks were executed serially on a single background thread.
Starting with android.os.Build.VERSION_CODES.DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel.
Starting with android.os.Build.VERSION_CODES.HONEYCOMB, tasks are executed on a single thread
to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

从文档来看,AsyncTask默认将在单一的后台线程中执行,
如果需要并发执行,则可以使用AsyncTask的executeOnExecutor接口。

了解AsyncTask的基本定义后,使用AsyncTask就比较简单了。


2、基本用法

Android给出的使用示例如下:

//使用时继承AsyncTask,并指定模板参数
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

    //后台执行的任务
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
            for (int i = 0; i < count; i++) {
                //下载操作
                totalSize += Downloader.downloadFile(urls[i]);

                //更新下载进度
                publishProgress((int) ((i / (float) count) * 100));

                // Escape early if cancel() is called
                //若主动结束AsyncTask,则结束任务
                if (isCancelled()) break;
        }
        return totalSize;
    }

    protected void onProgressUpdate(Integer... progress) {
        //UI线程更新进度
        setProgressPercent(progress[0]);
    }

    protected void onPostExecute(Long result) {
        //执行完毕后,UI线程调用
        showDialog("Downloaded " + result + " bytes");
    }
}

AsyncTask的使用较为简单,对于上面的例子,可以按照以下方式使用:

new DownloadFilesTask().execute(url1, url2, url3);

对于APK开发者来说,掌握到这个地方就能满足开发需求了。
不过为了满足我们的好奇心,我们还是进一步看看源码是如何实现的。


3、源码分析

我们主要按照AsyncTask基本用法的流程,来分析AsyncTask的实现。


3.1 构造函数

在使用AsyncTask前,会创建出AsyncTask的实例。实际的子类会调用AsyncTask的构造函数。

AsyncTask的构造函数如下所示:

public AsyncTask() {
    //WorkerRunnable实现Callable接口
    //参数类型为Params,接口函数call返回值的类型为Result
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //mWorker在call接口中的工作先不分析,后文会提到
            ................
        }
    };

    //创建一个FutureTask对象,构造函数的参数为一个callable对象
    //当FutureTask执行时,会调用callable对象的call函数,在执行完毕后再回调done接口
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            //此处done接口中的内容,先不分析后文会提到
            ................
        }
    };
}

从上面的代码可以看出,AsyncTask的构造函数中仅创建出了两个对象。
注意到这两个对象均创建在主线程中。

如果用命令模式来理解的话,那么AsyncTask中仅创建出了一个需要执行的命令。
这个命令还没有被添加到执行队列中。


3.2 execute接口

AsyncTask有好几个execute接口,我们分析最常用的接口。

//注解说明了此接口在主线程中调用
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //注意到除了传入参数外,还传入了sDefaultExecutor
    return executeOnExecutor(sDefaultExecutor, params);
}

在分析executeOnExecutor流程前,我们看看sDefaultExecutor。

..............
//静态成员,进程共享,默认指向SERIAL_EXECUTOR
//可以调用AsyncTask的setDefaultExecutor进行设置
//因此,设置了volatile属性,保证并发的可见性
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.
*/
//SERIAL_EXECUTOR指向的对象为SerialExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
............
private static class SerialExecutor implements Executor {
    //队列中保存所有的任务
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();

    //mActive中保存当前正在执行的任务
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        //execute函数被调用时,将runable对象加入到mTasks中
        mTasks.offer(new Runnable() {
            //当该runnable被执行后,调用scheduleNext
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });

        //当有新加入的任务时,若当前没有正在处理的任务,则直接调用scheduleNext接口
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        //从队列中取出第一个任务
        if ((mActive = mTasks.poll()) != null) {
            //利用THREAD_POOL_EXECUTOR执行
            //根据execute中的定义,我们知道一个runable的run方法结束后
            //才会重新调用scheduleNext
            //因此,虽然THREAD_POOL_EXECUTOR可以同时创建多个线程
            //但AsyncTask还是一个接一个的处理任务
            //如果将本函数的if改成while,就是并发处理了
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

THREAD_POOL_EXECUTOR为AsyncTask加载时创建出的ThreadPoolExecutor,
由于SerialExecutor限制任务必须单一依次执行,因此我们不再关注THREAD_POOL_EXECUTOR的参数。


3.3 executeOnExecutor接口

现在我们将视线拉回到executeOnExecutor接口。

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    //AsyncTask中定义了:private volatile Status mStatus = Status.PENDING;
    //因此每个AsyncTask对象创建后,其状态都是PENDING
    //这里就是文档中叙述的,每个AsyncTask只能运行一次的原因
    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)");
        }
    }

    //AsyncTask开始运行后,状态变为RUNNING
    mStatus = Status.RUNNING;

    //回调子类的onPreExecute接口
    //仍然是@MainThread
    onPreExecute();

    //将参数赋给构造函数中创建的WorkerRunnable
    mWorker.mParams = params;

    //将构造函数中创建的FutureTask加入到SerialExecutor的队列中
    exec.execute(mFuture);

    return this;
}

3.4 FutureTask的run函数

AsyncTask中创建出的FutureTask实现了runnable接口。
根据前文介绍SerialExecutor的工作流程,我们知道SerialExecutor最终会利用scheduleNext函数,
将FutureTask递交给THREAD_POOL_EXECUTOR处理。

ThreadPoolExecutor作为底层支持库中的类,其具体的运行逻辑,我们在此不做深究。
我们只需要知道,最终FutureTask作为一个runnable对象,其run方法将被调用。

我们看看FutureTask的run方法:

public void run() {
    //条件检查
    if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;

    try {
        //这个callable,就是AsyncTask构造函数中,构造FutureTask时传入的WorkerRunnable对象
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //调用WorkerRunnable的call方法
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }

            if (ran)
                //WorkerRunnable执行完毕后,才调用FutureTask的set接口
                set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
}

从上面的代码可以看出,FutureTask实际执行时,调用的是WorkerRunnable的call接口。


3.5 FutureTask的run函数

我们回过头来看看AsyncTask构造函数中,WorkerRunnable的完整定义:

..............
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        //调用子类的doInBackground函数
        //此时还是在后台线程
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();

        //调用postResult函数
        return postResult(result);
    }
};

跟进postResult函数:

private Result postResult(Result result) {
    //getHandler返回的是AsyncTask中的静态类InternalHandler,运行在主线程中
    //创建Message,消息中包含了后台线程的执行结果
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            //封装了当前的AsyncTask对象和执行结果
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

3.6 InternalHandler的处理

最后,我们看看InternalHandler的实现:

private static class InternalHandler extends Handler {
    //运行在主线程中的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
                //调用AsyncTask的finish函数
                result.mTask.finish(result.mData[0]);
                break;
            //调用publishProgress时,发送的是这个消息
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

我们看看AsyncTask的finish函数:

private void finish(Result result) {
    //如果调用过cancel接口,则回调onCancelled
    if (isCancelled()) {
         onCancelled(result);
    } else {
        //正常情况下,回调onPostExecute接口
        onPostExecute(result);
    }

    //更改当前AsyncTask的状态
    mStatus = Status.FINISHED;
}

至此,AsyncTask的基本流程分析完毕。
从执行流程可以看出,AsyncTask中前、后台的通信实际上还是依靠了Handler。


3.7 后续的处理

前文提到过,在FutureTask的run函数中,调用WorkerRunnable的run方法后,
将会调用FutureTask的set函数。

Future的set函数,将调用FutureTask的finishCompletion函数。
在该函数中将会回调FutureTask的done接口。

AsyncTask中创建的FutureTask覆盖了父类的done接口,我们来看看相关的工作:

...............
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);
        }
    }
};
.............
private void postResultIfNotInvoked(Result result) {
    //前面的代码提到过,WorkRunnable的call被执行后,mTaskInvoked已经置为了true
    final boolean wasTaskInvoked = mTaskInvoked.get();

    //因此,正常情况下不会再进行postResult的工作
    if (!wasTaskInvoked) {
        postResult(result);
    }
}
............

个人觉得这里处理可能是为了配合AsyncTask的cancel接口。
我们看看AsyncTask的cancel接口:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    //调用FutureTask的cancel接口
    return mFuture.cancel(mayInterruptIfRunning);
}

跟进FutureTask的cancel接口:

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
            U.compareAndSwapInt(this, STATE, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    //传入参数为true时,直接中断线程
                    t.interrupt();
            } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
            }
         }
    } finally {
        //回调FutureTask的done接口
        finishCompletion();
    }
    return true;
}

从上面的代码可以看出,由于AsyncTask是单一依次处理的。

因此,当某个AsyncTask加入到队列中时,可能并没有被实际处理,即其WorkRunnable的call接口没有被调用。
此时,调用AsyncTask的cancel接口,就会触发FutureTask的cancel接口,如上代码所示,进一步回调FutureTask的done接口。
于是,最终触发AsyncTask的onCancelled接口。


4、总结

本篇博客中,我们分析了AsyncTask的基本概念、使用方式及源码。

虽然在实际使用时,我们并不需要对其实现方式有深刻的理解,
但如果可能的话,掌握它的实现方式,可能会得到一些额外的启发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值