AsyncTask是一个执行在UI线程的一个类,这个类可以简单实现在后台线程中执行任务,然后将结果发布到前台。当然,你也可以用Thread和Handler来实现,所以AsyncTask的设计初衷只是一个Thread+Handler的一个帮助类,而不是一个线程框架。

如何使用

AsyncTask是一个抽象类,必须由子类实现后,才能使用。子类必须重写doInBackground(Params...),一般情况下,也会重写onPostExecute(Result)
一个简单实现AsyncTack的子类:

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 calledif (isCancelled()) break;}return totalSize;}protected void onProgressUpdate(Integer... progress) {setProgressPercent(progress[0]);}protected void onPostExecute(Long result) {showDialog("Downloaded " + result + " bytes");}}

调用子类:

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

AsyncTask声明的3种泛型定义

1.Params,需要发送给doInBackground(Params... params)后台线程的参数.
2.Progress,完成进度的单位,一般写int就可以,由onProgressUpdate(T t)来接收处理
3.Result,返回给UI线程的结果类型,由onPostExecute(Result result)处理
如果在使用中,并没有这些参数的传递,都可以指定为Void,比如:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

AsyncTask执行的4步

当一个task执行时,下面的4步都可能被执行:
1.onPreExecute(),UI线程中执行,在执行后台线程之前被调用,一般情况下,可以show一个progressbar给用户。
2.doInBackground(Params...),在第一步执行完成后执行,在后台线程中调用。这一步执行相对耗时的操作,比如请求网络,优化图片等。这一步中,可以调用publishProgress(Progress...)方法将执行进度发送到第三步。
3.onProgressUpdate(Progress...),UI线程执行,在调用publishProgress(Progress...)后执行,什么时候执行,不确定。在这个方法中,可以显示给用户执行进度。
4.onPostExecute(Result),UI线程执行,在doInBackground()执行完成后调用。doInBackground()的返回结果,会作为一个参数传给onPostExecute(Result)

线程规则

必须遵守的几条线程规则:
- AsyncTask类必须在UI线程中装载
- AsyncTask子类必须在UI线程中实例化
- 不要手动的调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)方法
- 一个实例,只能被执行一次,也就是说只能调用一次execute(),调第二次的时候会抛出异常

执行顺序

在Android 3.0之后,task就是串行执行,也就是依次打开task1task2task1会执行,而task2会等task1执行完成后,再执行.所以官方又建议,执行特别耗时的操作时,请使用java.util.concurrent包下的类来执行,比如:Executor, ThreadPoolExecutor 和FutureTask.

源码解析

搞懂了AsyncTask基本使用原理之后,就应该看看源码,是怎么实现的了。
先从构造函数看起:

public AsyncTask() {/** mWorker是一个实现了Callable接口的对象 */mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection unchecked/** 执行doInBackground(),并调用postResult发送结果到UI线程 */return postResult(doInBackground(mParams));}};/** mFuture是一个runnable对象1,当执行这个runnable对象时,会调用mWorker的call(),2,当call()执行完毕后,会调用mFuture的done(),3,mFuture的get()方法能得到mworker.call()的返回值,并且这是个阻塞方法*/mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected 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);}}};}

看一下postResultIfNotInvoked()方法

private void postResultIfNotInvoked(Result result) {final boolean wasTaskInvoked = mTaskInvoked.get();if (!wasTaskInvoked) {postResult(result);}}

call()最后一行代码,和postResultIfNotInvoked()中,都执行了postResult()将结果发送出去:

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

看到这里,应该很熟悉了,果然AsyncTask是Hanlder的封装,内部通知UI线程也是用的Handler方式,getHandler()最终是获取一个内部类InternalHandler:

private static class InternalHandler extends Handler {public InternalHandler() {super(Looper.getMainLooper());}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:/** AsyncTask的第三步 */result.mTask.onProgressUpdate(result.mData);break;}}}

这个InternalHandler构造方法中绑定了UI线程的Looper,负责处理从后台线程中传递过来的Message,result.mTask为当前对象,msg.what == MESSAGE_POST_PROGRESS,就调用了task的第三步,更新UI。

    private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {/** AsyncTask的第四步 */onPostExecute(result);}mStatus = Status.FINISHED;}

finish()方法最终调用了task的第四步方法,到这里,已经知道了第三步和第四步都是在UI线程中调用的,那倒着来看第一步和第二步何时调呢?
下面从task.execute()入手:

    public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}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;/** AsyncTask的第一步 */onPreExecute();mWorker.mParams = params;exec.execute(mFuture);return this;}

先是判断了任务的状态,是不是pending,而后再去设置状态为running,这就是为什么一个task只能执行一次了。
然后接着直接调用了AsyncTask的第一步,这也就是为什么我们必须在UI线程中去执行execute()的原因。
接着exec.execute()会开启后台线程执行mFutrue;如果还记得前面的构造函数的mWorker,mFuture的话,就知道了,这里是去执行mWorker的call()(这里会执行第三步),然后调用postResult()使用handler发送消息给InternalHandler去处理。

到这里,差不多源码解析完了,知道了task的4步都在哪些线程中执行。下面再看看这个执行AsyncTask的线程池是怎么来的。
当直接调用execute()时,默认使用成员变量sDefaultExecutor作为线程池来执行mFuture。下面看一下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);}}}/*** 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);

sDefaultExecutor最终是一个SerialExecutor对象,SerialExecutor类在javaapi文档中写过,他是一个严格的串行执行任务的类,所以前面提到的,AsyncTask是串行执行的。
最终执行mFuture的是一个ThreadPoolExecutor线程池。

ps1:以前听人说AsyncTask最多开启的任务数是有限制的,但今天看了源码,发现并没有限制数量,所以自己做了个实现,去通知开启1000个任务,发现并没有挂,而且也没有什么问题,很完美的运行下去了。所以,以后不能光听别人说,如果有疑问,还是实践是检验真理的唯一标准。
ps2:写上面实验的例子时,为了方便,我写了个匿名的内部类来实例化一个task,然后开启他,结果报了个异常:

Object[] cannot be cast to String[] in AsyncTask

很是奇怪,明明我传的就是一个String类型,为什么突然就变成了Object了。
最后在http://stackoverflow.com/questions/20455644/object-cannot-be-cast-to-void-in-asynctask上找到了答案,就是说必须用实例化后的对象去调用.execute()。

Android_AsyncTask学习相关推荐

  1. java入门 慕路径,Java入门基础知识总结学习教程大全【必看经典】

    类型的表达式,是循环条件,表达式3是党执行了一遍循环之后,修改控制循环的变量值. ??? for语句的执行过程是这样的:首先计算表达式1,完成必要的初始化工作:然后判断表达式2的值,如果表达式的值为t ...

  2. Java EE学习心得

    –Java EE学习心得   1.    称为编程专家的秘诀是: 思考-----编程--------思考------编程--.. 编程不能一步到位,不能一上来就编,必须先思考如何写,怎样写?然后再编程 ...

  3. FastAI 2019课程学习笔记 lesson 2:自行获取数据并创建分类器

    文章目录 数据获取 google_images_download 的安装和使用 挂载google 个人硬盘到Google colab中 删除不能打开文件 创建ImageDataBunch 训练模型 解 ...

  4. FastAI 课程学习笔记 lesson 1:宠物图片分类

    文章目录 代码解析 神奇的"%" 导入fastAI 库 下载解压数据集 untar_data 获取帮助文档 help() ? ?? doc 设置路径 get_image_files ...

  5. 深度学习学习指南-工具篇

    colab Colab是由Google提供的云计算服务,通过它可以让开发者很方便的使用google的免费资源(CPU.GPU.TPU)来训练自己的模型. 学习经验总结 如何使用命令行? 通过!+cmd ...

  6. Redis学习之路(一)--下载安装redis

    redis学习之路--下载安装redis windows安装redis 1.下载redis 2.安装 3.查看是否安装成功 windows安装redis 1.下载redis 网址:https://gi ...

  7. python内置库之学习configparser库(一)

    python内置库之学习configparser库(一) 1.引言 ini文件简介 [节] 键=值 注:节不能重复出现 2.自己封装了一个增删改查的类,可以参考一下 import configpars ...

  8. 前端Vue学习之路(二)-Vue-router路由

    Vue学习之路 (二) Vue-router(基础版) 一.增加静态路由 二.动态路由+路由嵌套+404页面 三. 编程式导航 四.命名路由 五.命名视图 六.重定向和起别名 1.重定向 2.起别名 ...

  9. 前端Vue学习之路(一)-初识Vue

    Vue学习之路 (一) 1.引言 2.更换npm国内镜像源 3.用npm下载Vue 4.Vue全家桶 5.使用命令创建项目 5.推荐插件 6.推荐网站 7.学习扩展 1.引言 先安装node.js环境 ...

最新文章

  1. 存储过程while_超详细的Oracle存储过程基础入门介绍
  2. 人工智能会取代科学家吗
  3. model.train_on_batch介绍【TensorFlow2入门手册】
  4. 致远M1移动协同软件
  5. Boost智能指针——weak_ptr
  6. Linux命令行上传文件到百度网盘
  7. 怎样在PHP中通过ADO调用Asscess数据库和COM程序
  8. Spring与Struts框架整合
  9. 中国风喜庆传统新年元旦海报PSD分层模板
  10. Windows 7系统mac地址修改攻略
  11. 二分类问题的评分值与损失函数
  12. java鼠标变粗怎么办_java – 使用Apache POI使整行变粗
  13. 创建ejb项目以及weblogic部署ejb
  14. Java结合docx4j生成docx文件
  15. AI笔记: 数学基础之贝叶斯公式(概率公式)
  16. 移动端网页字体过多时,字体被自动放大问题
  17. mysql .idb_mysql.idb
  18. 爬虫基本概念(新手必看)
  19. Three.js 实现虎年春节3D创意页面
  20. 天主教七宗罪(你范了那些条)

热门文章

  1. 遗传算法(二)之组卷算法
  2. 中控身份证打卡器ID100接口调用中遇到的一些坑
  3. 三菱FX系列PLC串口通讯协议整理
  4. 银行数字化业务转型书籍推荐:郭立仑2022.7月《银行数字化转型路线图——一套系统的数字化解决方案》
  5. wangEditor出现初始化编辑器时候未传入任何参数,请查阅文档
  6. 如何挖掘用户终身价值?开发会员制?
  7. C#/.NET/.NET Core优秀项目框架推荐
  8. 踩坑---django定时任务django-apscheduler
  9. 国产数据库备份恢复(TiDB\达梦\OceanBase\openGauss\GaussDB\GBASE)——筑梦之路
  10. 树梅派应用2:全新配置 HiFiBox DAC + Volumio 系统