文章目录

  • 1.简介
  • 2.基本使用
    • 2.1 关键API
    • 2.2 伪代码
  • 3.源码分析
    • 3.1 构造函数
    • 3.2 android.os.AsyncTask#execute(Params...)
    • 3.3 执行流程
    • 3.4 知识点总结
  • 4. 异常分析
    • 4.1 the task is already running
    • 4.2 the task has already been executed

1.简介

android.os.AsyncTask,一个执行异步操作的类,我们可以使用它来处理后台任务,并且在UI线程中处理结果,而无需关心线程的问题。

AsyncTask 内部是使用 {@link Thread}和{@link Handler}来实现的。理想情况下,应将AsyncTasks用于短操作(最多几秒钟)。如果需要长时间保持线程运行,建议使用 java.util.concurrent 包提供的各种API。 例如{@link Executor},{@ link ThreadPoolExecutor}和{@link FutureTask}。


2.基本使用

2.1 关键API

android.os.AsyncTask#execute(Params…)
使用指定的参数执行任务。 任务返回自身(this),以便调用者可以保留对其的引用。这个方法必须在UI 线程上调用。

android.os.AsyncTask#onPreExecute
在后台任务执行之前执行,同样是运行在UI 线程。

android.os.AsyncTask#doInBackground
后台任务,用于处理一些异步操作。该方法由AsyncTask 内置的调度者执行,在使用中需要复写该方法来完成异步处理。在此方法中可以调用android.os.AsyncTask#publishProgress 函数来更新进度,通知UI 线程来显示。

android.os.AsyncTask#onProgressUpdate
更新进度。该方法是经 android.os.AsyncTask#publishProgress 函数执行后由android.os.AsyncTask 内部的Handler 进行消息分发,然后在UI 线程执行。

android.os.AsyncTask#onPostExecute
后台任务执行完毕后,通过内部的handler 将返回结果发送至UI 线程,入参是{@link #doInBackground}函数的返回值,可在该方法处理执行结果。

2.2 伪代码

2.2.1 继承 AsyncTask,复写方法

class DownloadAsyncTask extends AsyncTask<Params, Progress, Result>{    @Override    protected void onPreExecute() {        super.onPreExecute();        // todo 在 doInBackground() 函数之前执行,运行在UI线程    }    @Override    protected Result doInBackground(Params... params) {        // todo 处理异步任务,运行在后台(work)线程        // todo 可以调用此方法来更新进度        onProgressUpdate();        return null;    }    @Override    protected void onPostExecute(Result result) {        super.onPostExecute(result);        // todo 在 doInBackground() 函数之后执行,可以在这里处理运行结果。运行在UI线程    }}

2.2.2 调用执行

DownloadAsyncTask asyncTask = new DownloadAsyncTask();asyncTask.execute();

2.2.3 小心内存泄漏
因为AsyncTask 是执行的异步操作,所以在使用的时候一定要注意内存泄露的问题,切记切记!!!


3.源码分析

源码版本:SDK 28

3.1 构造函数

   /**     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.     */    public AsyncTask() {        this((Looper) null);    }    /**     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.     *     * @hide     */    public AsyncTask(@Nullable Looper callbackLooper) {        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()            ? getMainHandler()            : new Handler(callbackLooper);        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Result result = null;                try {                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                    //noinspection unchecked                    result = doInBackground(mParams);                    Binder.flushPendingCommands();                } catch (Throwable tr) {                    mCancelled.set(true);                    throw tr;                } finally {                    postResult(result);                }                return 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);                }            }        };    }

在构造函数里做了三件事,初始化Handler 的实例mHandler,WorkerRunnable 的实例mWorker 以及 FutureTask 的实例 mFuture。
mHandler:用于连接后台线程和UI 线程,做消息分发的。
mWorker:本质上是一个Callable,查看 WorkerRunnable 发现是实现的 java.util.concurrent.Callable 接口,而 Callable 是一个可以携带返回结果的任务。

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

在mWorker 的 call() 方法中执行 doInBackground 函数来获取返回结果

而后在finally 中执行了 android.os.AsyncTask#postResult 方法,此方法是发送一个message,其中携带的数据就是doInBackground 方法返回的数据。

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

mFuture:用来处理异步请求,配合 java.util.concurrent.Callable 接口可以实现在工作线程获取返回结果。在这里 mWorker 表示的也就是 mFuture 要执行的任务。另外,还复写 java.util.concurrent.FutureTask#done 的方法,该方法在任务执行完毕时被调用,这里做了个安全校验,当没有执行task 时仍会调用 postResult 方法,执行到UI线程上。

    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);            }        }    };
    private void postResultIfNotInvoked(Result result) {        final boolean wasTaskInvoked = mTaskInvoked.get();        if (!wasTaskInvoked) {            postResult(result);        }    }

mTaskInvoked 是个原子操作(AtomicBoolean),在mWorker 的run 方法中设置为true。

    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

3.2 android.os.AsyncTask#execute(Params…)

android.os.AsyncTask#execute(Params…) 内部会执行 android.os.AsyncTask#executeOnExecutor 方法,传入sDefaultExecutor

    @MainThread    public final AsyncTask<Params, Progress, Result> execute(Params... params) {        return executeOnExecutor(sDefaultExecutor, params);    }        @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();// 1.        mWorker.mParams = params;        exec.execute(mFuture);// exec 为 sDefaultExecutor        return this;    }

在方法内部调用了android.os.AsyncTask#onPreExecute 函数,到这里目前还是运行在UI 线程。其次执行 exec.execute(mFuture),而入参 exec 为 sDefaultExecutor。那sDefaultExecutor 是啥呢?

    /**     * 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();    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

这里 sDefaultExecutor 指向了 SERIAL_EXECUTOR,这是一个串行的任务调度器,将进入的任务按顺序取出执行。

private static class SerialExecutor implements Executor {    // 这是一个双端队列,符合先进先出的原则。    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();    // 当前执行的任务    Runnable mActive;    public synchronized void execute(final Runnable r) {        // 1.把传递进去来的runnable 打包成一个新的Runnable对象,然后入队。        mTasks.offer(new Runnable() {            public void run() {                try {                    // 4.执行传入进来的runnable 任务,即 FutureTask -> WorkerRunnable -> doInBackground                    r.run();                } finally {                    // 5.执行完毕后,继续执行下一个任务。                    scheduleNext();                }            }        });        // 2.当 mActive 为 null 时,从队列中取出任务并执行        if (mActive == null) {            scheduleNext();        }    }    protected synchronized void scheduleNext() {        // 3.从队头取出一个任务,如果不为null,则通过线程池获取一个子线程来执行该任务        if ((mActive = mTasks.poll()) != null) {            THREAD_POOL_EXECUTOR.execute(mActive);        }    }}

当一个新的任务(FutureTask)进来时

  1. 把传递进来的任务 打包成一个新的Runnable 对象,然后按先进先出的顺序执行入队(offer)操作。
  2. 检查当前是否有正在执行的任务,如果没有,则获取任务执行。
  3. 从队头取出一个任务,如果不为null,则通过线程池获取一个子线程来执行该任务 (AsyncTask 内包装了一个特有的线程池)。
  4. 执行传入进来的runnable 任务,即 可获取返回值的task ,该任务中执行 doInBackground 方法,还有执行结果的消息分发。
  5. 继续执行下一个任务,重复 3-4-5,直到任务队列中没有需要执行的任务。

THREAD_POOL_EXECUTOR 指向了一个线程池的对象,当有Runnbale 任务进来时,通过 threadPoolExecutor 来开启一个新线程执行任务。注意,这里执行的任务是经过 SerialExecutor 包装过的任务,在包装的任务内首先执行的才是FutureTask 中的 WorkerRunnable。

3.3 执行流程

话不多说,直接上图
emmmmmm… 算了算了 画图太浪费时间了,还是简单的做个描述吧。

  1. android.os.AsyncTask#execute(Params…)

  2. android.os.AsyncTask#executeOnExecutor
    -> android.os.AsyncTask#onPreExecute
    -> exec.execute(mFuture) 调用调度器执行任务

  3. android.os.AsyncTask.SerialExecutor
    -> 打包传递进来的Runnable,然后执行入队(offer)操作。
    -> 检查当前是否有正在执行的任务,如果没有,则获取任务执行。
    从队头取出一个任务,如果不为null,则通过线程池获取一个子线程来执行该任务 (AsyncTask 内包装了一个特有的线程池)。
    -> 执行传入进来的runnable 任务,即 可获取返回值的task ,该任务中执行 doInBackground 方法,还有执行结果的消息分发。
    -> 继续执行下一个任务,重复 3-4-5,直到任务队列中没有需要执行的任务。

  4. FutureTask -> android.os.AsyncTask.WorkerRunnable
    -> android.os.AsyncTask#doInBackground
    -> android.os.AsyncTask#postResult 发送消息,通知任务完成。

  5. InternalHandler
    -> Message -> android.os.AsyncTask#MESSAGE_POST_RESULT -> android.os.AsyncTask#finish -> android.os.AsyncTask#onPostExecute
    -> Message -> android.os.AsyncTask#MESSAGE_POST_PROGRESS -> android.os.AsyncTask#onProgressUpdate

3.4 知识点总结

  1. 使用FutureTask 和 Callable 实现获取任务的返回值
  2. 使用 SerialExecutor 维护任务队列,保证串行的执行顺序
  3. 使用 ThreadPoolExecutor 来获取一个新的工作线程来执行后台任务
  4. 使用 Handler 来做消息的分发,最后切换回UI 线程

4. 异常分析

4.1 the task is already running

现象:asyncTask的后台任务还在执行中,再次调用execute 方法报错。

源码分析:

@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.");            // 忽略其他代码        }    }    mStatus = Status.RUNNING;// 置状态为运行状态        // 忽略其他代码}

可以看到AsyncTask 内部用了一个枚举值来表示当前的状态,当调用execute-> executeOnExecutor 方法时,首先会检查状态,如果当前已经是 RUNNING 状态,则抛出异常。如果是第一次调用则会初始化状态为 Status.RUNNING,这样做也是防止多次调用 execute 方法。

结论:一个AsyncTask 实例对象不能重复调用execute 方法,该方法只能执行一次。

4.2 the task has already been executed

原因:当一个 AsyncTask对象 的execute 方法执行完毕(含后台任务),再次调用execute 方法报错。

源码分析:

@MainThreadpublic final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,        Params... params) {    if (mStatus != Status.PENDING) {        switch (mStatus) {            // 省略其他代码            case FINISHED:                throw new IllegalStateException("Cannot execute task:"                        + " the task has already been executed "                        + "(a task can be executed only once)");        }    }    // 省略其他代码    return this;}

可以看到同样是在 executeOnExecutor 方法中抛出的异常,那么,mStatus 是什么时候变成 FINISHED 的呢?追踪代码发现是在android.os.AsyncTask#finish 方法中做了赋值,而finish 方法又是在 InternalHandler 收到的 MESSAGE_POST_RESULT 消息时做的处理,到这里也就明白了。

简单来说就是在后台任务执行完毕之后把状态改为了 Status.FINISHED

private void finish(Result result) {    if (isCancelled()) {        onCancelled(result);    } else {        onPostExecute(result);// 后台任务执行完毕,获得返回的结果,运行在UI 线程    }    mStatus = Status.FINISHED;}

结论: 一个AsyncTask 实例对象不要重复调用execute 方法,该方法只能执行一次。

更多相关文章

  1. seekBard的使用方法
  2. Flutter 透明状态栏及字体颜色的设置方法
  3. Android(安卓)远程回调
  4. android 恢复出厂设置流程
  5. Android(安卓)Studio 中build.gradle文件的详细解析
  6. Mac Android(安卓)Studio快捷键
  7. Android(安卓)AIDL详解
  8. Fragment的交互及管理
  9. android实时高斯模糊

随机推荐

  1. mac里边配置android开发环境,intellij开发
  2. Android中ScrollView无法正常achartengin
  3. Android(安卓)技巧记录
  4. Android(安卓)最近的一些新的功能
  5. Android(安卓)第五天 (下午)
  6. Android(安卓)Wifi几点
  7. 在ubuntu上编译android的mupdf
  8. Android(安卓)O 8.0 新功能预览
  9. Android(安卓)Spinner
  10. 【Android进阶】android:configChanges属