Android源码解析——AsyncTask

简介: 简介AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用。它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler。

简介

AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用。它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler。它被设计为一个围绕着Thread和Handler的辅助类,而不是一种通用的线程框架。在API文档中,建议你把它用于一些耗时比较短(大部分情况下只有几秒)的操作,如果你需要让线程长期运行,则强烈推荐使用java.util.concurrent里的API来处理。
如上所述,一个异步任务是在后台中进行计算,并把结果带到UI线程中。在这里,一个异步任务定义了三个泛型类型(ParamsProgressResult),以及四个步骤(onPreExecutedoInBackgroundonProgressUpdateonPostExecute)。

用法

在官方文档中,以下载文件为例,提供了示例代码,先定义一个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
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

然后通过调用它的实例的execute(Params…)方法来执行。

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

源码解析

下面来看一下这个类的代码,本文以android-23版本的代码为例。

成员变量

首先是这个类的成员变量。

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

    private volatile Status mStatus = Status.PENDING;//表示线程的执行状态

    private final AtomicBoolean mCancelled = new AtomicBoolean();//表示是否被取消
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();//表示是否被调用

其中WorkerRunnable是对Callable的封装,包装了一个异步任务的参数。Status是定义的表示异步任务状态的枚举类型,共有三个状态,分别是PENDING(待绪),RUNNING(正在运行),FINISHED(已经完成)。

成员方法

再来看API中提供的所有成员方法:
这里写图片描述

我们从上面的示例代码中一个异步任务的执行开始,逐步跟踪它的执行过程。

    @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();

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

        return this;
    }

从上面的代码可以看出,一个异步任务不能被重复执行,否则将会抛异常。当执行它的时候,状态会被设为Status.RUNNING,同时会调用onPreExecute()方法,然后把传进来的参数赋值到所封装的worker中,再调用一个Executor的 execute方法。
onPreExecute是前面所述的一个异步任务的四个步骤之一,它是用在UI线程中的。所以我们要在UI线程中调用execute方法,才能使其在UI线程中被调用。
上面所用的Executor为sDefaultExecutor,我们来看一下它的定义:

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile 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) {
            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);
            }
        }
    }

可以看到,默认的Executor是串行处理后台任务的。然而,需要注意的是,AsyncTask在Android API 11之前曾经被设计为并行的,所以如果你在API 11之后需要并行地执行任务,可以直接调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)。对此,谷歌在support库提供了封装这个操作的兼容类AsyncTaskCompat,因此你也可以直接调用AsyncTaskCompat.executeParallel(task, params)来使它能够被并行执行。

那么,一个异步任务是如何完成各个步骤的依次调用的?我们再回到它的构造方法来:

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            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) {
            @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);
                }
            }
        };
    }

mFuture被Executor执行时,会执行其mWorker。它在调用的时候,先把mTaskInvoked设置为true表示已调用,设置线程优先级,然后才执行doInBackground()方法,并执行postResult发送其返回的结果。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过程中发生异常的话,而上面的代码可能就不会被执行,所以通过重写FutureTaskdone方法,在里面调用postResultIfNotInvoked,以确保任务执行完成的步骤被调用。
在前面的代码中,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;
            }
        }
    }

也就是消息被绑到主线程的消息队列了。同样,更新进度也是通过这种方法来把消息发送到主线程的消息队列中去执行:

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

而在任务完成的方法中,先判断任务是否被取消,被取消的话则回调被取消的方法,否则回调onPostExecute,最终把任务的状态设为已结束。源码如下:

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

以上就是AsyncTask对后台任务及UI线程之间的交互所做的封装。
AsyncTask还提供了取消任务以及等待任务执行的结果的几个API,它们原是FutureTask所提供的特性,所以也只是通过调用mFuture的对应方法,这里不再赘述。

总结

通过上面的代码可以总结出几条:

  • AsyncTask在不同的版本可能是串行执行的,也可能是并行执行的。
  • 如果要确保任务被并行执行,可以通过support库的AsyncTaskCompat来执行。
  • 一个AsyncTask只能被执行一次,不能被多次执行。
  • 取消一个AsyncTask,可以设置是否允许中断它。
  • 如果取消了一个AsyncTask,则不会再回调onProgressUpdate
  • 当一个任务完成时,如果它已经被取消,则回调onCancelled方法,否则回调onPostExecute方法。
目录
相关文章
|
3月前
|
数据采集 监控 API
告别手动埋点!Android 无侵入式数据采集方案深度解析
传统的Android应用监控方案需要开发者在代码中手动添加埋点,不仅侵入性强、工作量大,还难以维护。本文深入探讨了基于字节码插桩技术的无侵入式数据采集方案,通过Gradle插件 + AGP API + ASM的技术组合,实现对应用性能、用户行为、网络请求等全方位监控,真正做到零侵入、易集成、高稳定。
630 52
|
7月前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
368 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
8月前
|
XML 搜索推荐 Android开发
Android改变进度条控件progressbar的样式(根据源码修改)
本文介绍了如何基于Android源码自定义ProgressBar样式。首先分析了系统源码中ProgressBar样式的定义,发现其依赖一张旋转图片实现动画效果。接着分两步指导开发者实现自定义:1) 模仿源码创建一个旋转动画XML文件(放置在drawable文件夹),修改图片为自定义样式;2) 在UI控件中通过`indeterminateDrawable`属性应用该动画。最终实现简单且个性化的ProgressBar效果,附带效果图展示。
574 2
|
9月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
10月前
|
XML JavaScript Android开发
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
412 15
|
10月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
10月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
450 4
|
10月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
10月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
API 开发工具 Android开发
Android源码下载
Android源码下载
2348 0

热门文章

最新文章

推荐镜像

更多