AsyncTask源码解析,你需要摸清的细节

利用Downloader下载文件(C#) C#下,利用Downloader下载文件 阅读详情

AsyncTask简介

1. AsyncTask提供了一种恰当的简单的跟UI Thread交互的方式。
2. 它不需要通过操控Threads或Handler就可以将后台操作以及其结果展示在UI Thread上。
3. AsyncTask是围绕Thread和Handler被设计出的一个Helper类,它不构成一般的线程框架。
4. AsyncTasks使用在短时间操作(最多几秒钟)上是非常理想的。
5. 如果你需要保持一个长时间操作,强烈建议还是使用java.util.concurrent包中的类,比如Executor、ThreadPollExecutor和FutureTask。

如何定义一个Asynchronous Task

1. 需要在后台进行计算
2. 结果要被public到UI Thread上。

定义Asynchronous Task的三个元素

1. Params 参数
2. Progress 进度
3. Result 结果

定义Asynchronous Task的4个步骤

1. onPreExecute
2. doInBackground
3. onProgressUpdate
4. onPostExecute

范例

1. 类的实现

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");
     }
 }

2. 执行

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

3. 取消Task

Async Task可以通过cancel(boolean)在任何时间cancel.在cancel之后,isCancelled()返回的函数会变成true。
在cancel函数被调用后,onCancelled(Object)会替代onPostExecute(Object)在doInBackground(Object[])之后被调用,
注.如果要尽快确定task被cancelled,要周期性的不断在doInBackground(Object[])检查isCancelled(), 
可以在里头内置一个 循环,不断的查询。

4. 使用规则

1. AsyncTask类必须在UI线程中被实例化。
2. AsyncTask必须在UI线程中被创建。
3. execute(Params...) 必须在UI线程中被调用
4. 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
5. task只能被执行一次,否则多次调用时将会出现异常;

Memory可观察性

AsynTask保证所有回调调用不用加synchronized也是线程安全的。
1. Set member fields in the constructor or onPreExecute(), and refer to them in doInBackground(Params...).
2. Set member fields in doInBackground(Params...), and refer to them in onProgressUpdate(Progress...) and onPostExecute(Result).

顺序执行,演变史

1. 在刚被Google引入使用时,AsyncTasks会被线性的在单一线程里头执行。
2. 从Android 2.0DONUT开始, AsyncTasks改进到在线程池中,并允许多线程并发执行 
3. 从Andriod 3.0HONEYCOMB开始, tasks在单一线程中执行以避免因并发Exception导致一般的应用错误
4. 如果你一定要并发执行,可以通过executeOnExecutor(java.util.concurrent.Executor, Object[])函数,在THREAD_POOL_EXECUTOR中执行(线程池).

源码解析

1. 先说下有几个需要实现的
    protected abstract Result doInBackground(Params... params);

    protected void onPreExecute() {
    }
	
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onPostExecute(Result result) {
    }
	
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onProgressUpdate(Progress... values) {
    }
2. 先看结构体
public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
在这里实例化了WorkerRunnable并将赋值给mWorker;将mWorker作为参数以实例化FutureTask,赋值给mFuture.
3. 再看如何execute
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
	
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }
AsyncTask有两个execute。可以看到莫名情况下execute会直接以mDefaultExecutor为参数调用executeOnExecutor()。
直接运行runnable,mDefaultExecutor也会直接执行一个runnable。
    看下sDefaultExecutor
    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.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    再跟
    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);
            }
        }
    }

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
	
    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
THREAD_POOL_EXECUTOR看来google在3.0之后,对ThreadPoolExecutor进行的优化,会考虑到当前硬件配置而行。
这里的配置方案倒是挺符合惯例的。要注意之类KEEP_ALIVE = 1.单线程运行。
sDefaultExecutor是个线性Executor。先将Task加载进ArrayDeque,再一个个poll出来,用THREAD_POOL_EXECUTOR执行。
4. 回到之前的execute流程。看executeOnExecutor
 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;
    }
分析一下,
mStatus有三个状态
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }
通过mStatus的状态来判断是否要抛出Exception,可以看出,正在运行的Task不能被再次运行,已经运行过的task不能再次被运行。
设置好mStatus的状态之后,会调用abstract onPreExecute().并调整mWorker的mParams;然后才在executor中执行mFuture,开始真正的运算。
5. 回头再看下mWorker的实例化
    在executor调用execute(mFuture)之后,就会调用。doInBackground
    protected abstract Result doInBackground(Params... params);
6. 那publishProgress什么时候调用呢?
    在doInBackground的函数实现的时候,可以调用publishProgress及时更新状态。progress的进度需要手动实现。
    
protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
	
    private static class InternalHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                ....
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
    通过Handler调用onProgressUpdate.
7. 再看mWorker的实例化。在doInBackground完成之后,会postResult
 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
 
    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
	
	private static class InternalHandler extends Handler {
        ...
        @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;
                ....
            }
        }
    }
    再跟
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
    可以看到。如果没有被cancel掉,那么会就调用onPostExecute。不然就会调用onCancelled。mStatus的状态也会被更新为Status.FINISHED

    小结,至此AsyncTask的execute流程就完结。
8. 中止Task
    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }
    mCancelled的标识被设置成true。再将mFuture给取消掉。

    继续跟,看下mCancelled是如何使用的?
    public final boolean isCancelled() {
        return mCancelled.get();
    }
    看isCancelled()是如何使用?
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
    假如mCancelled被设置成true,那么将不会继续更新状态。
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
    当mFuture被cancel掉之后,运算就结束了。postResult就会被调用。通过MESSAGE_POST_RESULT会调用finish。
    按照finish的逻辑,onCancelled就会被顺利调用
    至此,cancel的流程就完成。

结束

AsyncTask,其实就是个利用ThreadPoolExecutor。Future。Handler实现的可以更新。
在5个使用规则的前提下,AsyncTask就可以顺利更新UI。
换言之,AsyncTask如果你不用View的更新.理论上是可以在分线程上用.可以从中了解AsyncTask的设计理念.
这可能是最完善的AsyncTask源码分析了,看起来很舒服。 前言江湖盛传一句话,编程最好的老师就是看源码。然后当我们十分乖巧的准备好了一切,小心翼翼的打开源码的时候,大部分人都是看不了几行就已经想着今晚要不要吃鸡了?那么,为什么会出现这种情况呢?就拿AsyncTask举例:1.源码中设计到很多你不懂的知识点:ThreadPoolExecutor,SerialExecutor,FutureTask,Callable等等。注意:先不要去想这4个单词是干什么用的... 阅读详情

相关推荐

Retrofit实战指南:Android网络请求从入门到生产级落地

Retrofit是Android开发中主流的类型安全HTTP客户端,其本质是构建在OkHttp之上的声明式网络抽象层,通过注解将Java接口映射为HTTP请求,并借助Gson等Converter完成JSON与对象的自动序列化。它解决了传统AsyncTask+HttpURLConnection模式下手动拼URL、解析JSON、线程切换易错等工程痛点,显著提升网络层可维护性与稳定性。在电商、金融、IoT等真实业务场景中,Retrofit需配合OkHttp连接池、超时控制、拦截器链及生命周期管理才能发挥最大价值。

快马扬鞭须努力! 755

httpclient和webclient比较

综上所述,如果需要更高性能和更灵活的操作,并发下载方面更推荐使用HttpClient。对于简单的HTTP请求和基本的下载操作,WebClient可能更加适合。具体选择取决于项目需求和个人偏好。在并发下载时,HttpClient和WebClient都可以用于发送多个HTTP请求。

u010019329的博客 1441

网易Android提前批笔试复盘:从考点地图到备战策略

技术笔试是校招提前批的第一道筛选关卡,尤其对于Android开发岗位,它考察的不仅是算法刷题量,更是对计算机基础、Android系统机制与工程实践的综合检验。从AMS组件调度到Handler消息循环,从View绘制流程到R8混淆压缩,笔试题目往往围绕系统底层原理展开,要求开发者具备源码级理解能力。理解这些核心机制,有助于在有限的答题时间内快速定位考点、规避选择题陷阱,并在ACM模式的编程题中高效处理输入输出与边界条件。针对网易等大厂的提前批笔试,系统梳理组件生命周期、消息机制、性能优化等高频考点,结合真题复

weixin_33841722的博客 386

C#中的Async的异常处理

C#中基于async task编程的异常处理方式

穿越星空 994

C#实现多线程文件下载的实战源码解析

在多线程下载程序中,可能需要处理许多特定的错误情况,这时可以通过定义自定义异常类来增强错误处理的灵活性和可读性。全局异常处理通常在应用程序启动时设置,并在整个程序中保持一致性。在应用程序的主入口点设置全局异常处理:// 全局未处理异常的处理逻辑。

weixin_42515340的博客 728

Unity URP热扭曲效果实战:如何让半透明物体也‘动’起来(附完整Shader代码)

本文深入解析Unity URP管线中热扭曲效果无法影响半透明物体的核心原因,并提供一套完整的实战解决方案。通过编写自定义Shader,并巧妙利用URP的Render Objects渲染器功能,成功让指定半透明物体(如粒子特效)也能被扭曲效果影响,从而制作出火焰、热浪等更具视觉冲击力的特效。文末附有完整的Shader代码与进阶优化技巧。

pz890123的博客 501

使用 C# 下载文件的十八般武艺

文件下载是一个软件开发中的常见需求。本文从最简单的下载方式开始步步递进,讲述了文件下载过程中的常见问题并给出了解决方案。并展示了如何使用多线程提升 HTTP 的下载速度以及调用 aria2 实现非 HTTP 协议的文件下载。 简单下载 在 .NET 程序中下载文件最简单的方式就是使用 WebClient 的 DownloadFile 方法: var url = "https://www.coderbusy.com"; var save = @"D:\1.html"; using (var web

寒冰屋的专栏 1340

在 CentOS Stream 10 上搭建 Kubernetes 集群并支持开发调试与日志查看

搭建 Kubernetes 集群的每个步骤详细的文档。

灵犀物润 1641

Android面试吃透这一篇就够了,没有拿不到的offer!1096页Android学习笔记(1)

找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。多练。(视频优势是互动感强,容易集中注意力)你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。自行下载直达领取链接:【点击我即可获得!

m0_60607895的博客 732

保姆级教程:用DaVinci Configurator & Developer从零搭建AUTOSAR OS(TC2xx三核实战)

本文提供了一份详细的AUTOSAR OS配置指南,使用DaVinci Configurator和Developer工具从零开始搭建Infineon TC2xx三核系统。涵盖多核配置协同逻辑、版本兼容性陷阱及验证错误的解决方案,帮助开发者在3小时内构建可运行的多核OS框架。

weixin_33940469的博客 313

STM32驱动四位共阳极数码管实现高精度秒表计时器

本文详细介绍了如何使用STM32驱动四位共阳极数码管实现高精度秒表计时器。项目基于STM32F103C8T6最小系统板,通过定时器中断和GPIO控制,实现0.1秒精度的计时功能,并解析了共阳极数码管的动态扫描原理及按键消抖等关键技术。

n8m7b6v5c4的博客 951

android存储!作为一名Android面试者你应该知道的,建议收藏

一、自我介绍 应该算是起点比较高吧!985大学毕业后面一直在国外读研。之前准备面试微软但是可能经验不够,没有通过。经过朋友介绍我准备回国,积累一些开发经验。于是我面试了国内大厂BATJ,还有一些其他比较知名的公司,一共报了11家。我朋友说这叫广撒网多捞鱼! 其他就不说了BATJ都过了,但是由于地区还有待遇问题,我选择了讯飞一家不大不小的公司。一开始做的挺好的,没想到… 一、关于Handler面试那些问题 1、Handler Looper Message 关系是什么? 2、Messagequeue 的数据结构

m0_53537576的博客 122

CNN经典网络模型(二):AlexNet简介及代码实现(PyTorch超详细注释版)

CNN经典网络模型之一:AlexNet,本文包含其简介及代码,在PyTorch中实现,进行超详细注释,适合新生小白阅读学习~

qq_43307074的博客 2万+

UE5集成AI实战:大语言模型驱动NPC动态对话与智能决策

游戏人工智能(Game AI)是提升游戏沉浸感与交互深度的核心技术,其原理从早期的有限状态机、行为树发展到现代的智能体(AI Agent)与机器学习。通过赋予非玩家角色(NPC)自主感知、决策与学习能力,AI技术能创造更真实、动态的游戏世界,极大提升玩家的体验价值。在游戏开发中,将大语言模型(LLM)与强化学习等AI能力集成到引擎,已成为实现下一代智能NPC的关键路径。本文聚焦于Unreal Engine 5这一主流开发平台,详细解析如何将大语言模型(如Kimi、GPT)与行为树系统深度结合,构建能进行动态

weixin_34007886的博客 337

基于 STM32 的手持式安检金属探测器设计与实现

【代码】基于 STM32 的手持式安检金属探测器设计与实现。

weixin_42300449的博客 899

Agisoft Metashape 基于RTK技术的无人机航测免像控技术流程

利用无人机进行航空摄影测量,其使用方便、快速获得成果,已经是测绘行业外业生产的主要方式。RTK定位系统的出现,其厘米级的定位系统,使得航测无人机在作业时,能够准确获取拍照时的GPS位置,能够大量减少像控点,即使不使用像控点也能满足规范对精度的要求。可以有效解决特殊地形(如山川、峡谷、河流、沼泽、水库等)外业人员很难布设像控点的情况。

penglu366366的博客 2188

AsyncTask - 源码篇

概述 AsyncTask是一个轻量级选手,适合处理轻量级的后台任务。处理过程中还可以把处理的进度反馈到主线程中,方便我们更新UI,不需要我们去操作 handler,在早期 Android 版本中是十分方便的工具。今天我们来一起来阅读一下源码吧 ~ 1. 类注释 * <p>AsyncTask enables proper and easy use of the UI thread. T...

kid1421lzj的博客 335

域适应分类:MCD论文笔记——Maximum Classifier Discrepancy for Unsupervised Domain Adaptation

域适应分类:MCD论文笔记——Maximum Classifier Discrepancy for Unsupervised Domain Adaptation

qq_50001789的博客 1005

Unity中使用C#的WebClient的DownloadFileAsync异步回调不执行

在开发时遇到了一个很奇怪的问题,使用WebClient来异步下载远程资源,并且使用了WebClient的两个异步回调方法DownloadProgressChanged和DownloadFileCompleted。在Unity编辑器中,两个异步回调方法可以正常执行,当打包后发现,两个异步回调方法根本不会执行,比较神奇的是远程文件居然下载了下来。 通过无数次踩坑,发现是.net版本影响的。见下图: 代码如下: void Start() { do

Mickey丶笑风沙 2474

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

WebClient.DownloadFile(线程机制,异步下载文件)

线程机制(避免卡屏),异步下载文件。  我做网站的监控,WebClient.DownloadFile这个方法是我经常用到的,必要的时候肯定是要从网上下载些什么(WebRequest 也可以下载网络文件,不妨搜下,不过WebClient.DownloadFile使用更简单)。  今天简单的演示下WebClient.DownloadFileAsync的使用,刚刚写好的实例,有问...

weixin_34132768的博客 845

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

WebClient.DownloadFileAsync实现下载网络数据-简单的下载软件

本软件利用WebClient.DownloadFileAsync实现下载网络数据。很简单的一个程序。   源程序: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq;

PHP_前端_ios软件制件_概念西瓜专栏 6685

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: 浅谈StrictMode
下一篇: 如何选择Android 第三方工具(服务)
手艺人王学明
手艺人王学明 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄17年 441粉丝 185原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值