我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android AsyncTask 完美解析 看不懂源码你就输了

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android AsyncTask 完美解析 看不懂源码你就输了

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{
    @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 构造函数
   
    public AsyncTask() {
        this((Looper) null);
    }
    
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        mWorker = new WorkerRunnable() {
            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(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 implements Callable {
        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(this, result));
        message.sendToTarget();
        return result;
    }

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

    mFuture = new FutureTask(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 execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    @MainThread
    public final AsyncTask 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 是啥呢?

    
    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 mTasks = new ArrayDeque();
    // 当前执行的任务
    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)进来时

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

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

3.3 执行流程

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

android.os.AsyncTask#execute(Params…)

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

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

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

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 知识点总结 使用FutureTask 和 Callable 实现获取任务的返回值 使用 SerialExecutor 维护任务队列,保证串行的执行顺序 使用 ThreadPoolExecutor 来获取一个新的工作线程来执行后台任务 使用 Handler 来做消息的分发,最后切换回UI 线程
作者:A类函数


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android AsyncTask 完美解析 看不懂源码你就输了

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android AsyncTask 完美解析 看不懂源码你就输了

1.简介 android.os.AsyncTask,一个执行异步操作的类,我们可以使用它来处理后台任务,并且在UI线程中处理结果,而无需关心线程的问题。 AsyncTask 内部是使用 {@link Thread}和{@link Handl
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录