Android源码解析——AsyncTask

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 简介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方法。
目录
相关文章
|
1月前
|
Java 开发工具 Android开发
Android与iOS开发环境搭建全解析####
本文深入探讨了Android与iOS两大移动操作系统的开发环境搭建流程,旨在为初学者及有一定基础的开发者提供详尽指南。我们将从开发工具的选择、环境配置到第一个简单应用的创建,一步步引导读者步入移动应用开发的殿堂。无论你是Android Studio的新手还是Xcode的探索者,本文都将为你扫清开发道路上的障碍,助你快速上手并享受跨平台移动开发的乐趣。 ####
|
2天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
25天前
|
存储 Linux API
深入探索Android系统架构:从内核到应用层的全面解析
本文旨在为读者提供一份详尽的Android系统架构分析,从底层的Linux内核到顶层的应用程序框架。我们将探讨Android系统的模块化设计、各层之间的交互机制以及它们如何共同协作以支持丰富多样的应用生态。通过本篇文章,开发者和爱好者可以更深入理解Android平台的工作原理,从而优化开发流程和提升应用性能。
|
26天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
54 12
|
22天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
25天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
3天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
1月前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。

热门文章

最新文章

推荐镜像

更多