Connor学Android - AsyncTask基本使用与源码解析

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/BK6K4,话不多说我们马上开始!

1.更新UI的三种方式

在工作线程中更新UI(通过某些特定方法)

(1)使用 View 的 postInvalidate() 方法刷新页面

(2)使用 View 的 post() 或 postDelayed() 方法更新UI

(3)使用 Activity 的 runOnUIThread() 方法更新UI

TextView tv = (TextView) findViewById(R.id.hello_text);

// 1
tv.postInvalidate();

// 2
tv.post(new Runnable() {
	@Override
	public void run() {
		tv.setText("Hi,Fresh By Post");
	}
});

tv.postDelayed(new Runnable() {
	@Override
	public void run() {
		tv.setText("Hi,Fresh By postDelayed :");
    }
},1000);

// 3
MainActivity.this.runOnUiThread(new Runnable() {
	@Override
	public void run() {
		tv.setText("OH,I am Fine!!!");
	}
});

使用 Handler

(1)Handler是专门用来在线程之间传递消息的工具类,主要用于UI界面的更新、消息的传递与处理

(2)Handler发送消息的两种方法:

  • send方法:用于发送一个包含数据的Message对象,并在handleMessage(Message message)方法中处理

  • post系列的方法:用于发送一个Runnable对象,并在MessageQueue收到消息时执行

使用 AsyncTask

(1)AsyncTask是Android给我们提供的一种轻量级的异步任务类

(2)使用步骤:

  • 自定义一个继承 AsyncTask 类的子类
  • 重写 onPreExecute()、onPostExecute()、onProgressUpdate()、onCancelled() 方法
  • 在使用时 new 一个该类的对象,调用 execute() 方法完成更新

2.AsyncTask 重要方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6U8AMF8-1662515145951)(F:\yhy925\dailylife\Work\2.Android\2.《Android进阶之光》\《Android进阶之光》知识点总结\3.AsncTask\1.方法.png)]

onPreExecute():在主线程执行。一般在任务执行前做准备工作,比如对 UI 做一些标记

doInBackground(Params… params):在线程池中执行。在 onPreExecute() 执行后执行,用于执行较为耗时的操作。在执行过程中可以调用 publishProgress(Progress… values) 来更新进度信息

onProgressUpdate(Process… values):在主线程中执行。当调用 publishProgress() 时,此方法会将进度更新到UI组件上

onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,这个方法会被执行。doInBackground 方法得到的结果就是返回的 result 的值。此方法一般做任务执行后的收尾工作,比如更新 UI 和数据

3.AsyncTask 源码分析

public abstract class AsyncTask<Params, Progress, Result> { }

AsyncTask 是一个抽象的泛型类,它有三个泛型参数,如果不需要某个参数,可以将其设置为 void

(1)Params:参数类型

(2)Progress:后台任务执行进度的类型

(3)Result:返回结果的类型

3.1 Android 3.0 之前的 AsyncTask
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
……  
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

(1)线程池使用的是 ThreadPoolExecutor ,核心线程数是5个,最大线程数是128,非核心线程空闲等待新任务的最长时间为1s

(2)采用的阻塞队列是 LinkedBlockingQueue,容量为10

(3)综合(1)(2),AsyncTask 最多同时容纳139个任务,超出后就会执行饱和策略,默认抛出 RejectedExecutionException

3.2 Android 7.0 版本的 AsyncTask

重要属性

(1)WorkerRunnable 实现了 Callable 接口,并实现了 call 方法,在 call 方法中调用了 doInBackground 方法来处理任务并得到结果,并最终调用 postResult 将结果投递出去

(2)FutureTask 是一个可管理的异步任务,实现了 Runnable 和 Future 接口,可以包装 Runnable 和 Callable,并提供给 Executor 执行,也可以调用线程直接执行(FutureTask.run())

(3)在构造函数中,将 WorkerRunnable 作为参数传给了 FutureTask

执行过程

(1)调用 AsyncTask 的 execute() 方法

(2)execute() 方法内会做下面三件事

  • 首先调用 onPreExecute() 方法
  • 将 AsyncTask 的参数传给 WorkerRunnable,再在构造函数中将 WorkerRunnable 作为参数传递到 FutureTask 中
  • 将 FutureTask 作为参数,调用 exec 的 execute() 方法

(3)这个 exec 是一个 SerialExecutor,完成对 FutureTask 列表进行串行执行,它的 execute() 方法内会完成下面几件事

  • 维护一个 ArrayDeque 的任务队列,将之前的 FutureTask 加到队列中
  • 处理这个 FutureTask,因为 FutureTask 是一个逐层封装的异步任务,所以接下来会依次完成
    • 调用 FutureTask 的 run() 方法
    • 由构造函数可知,run() 方法内部调用 WorkRunnable 的 call() 方法
    • 同样由构造函数可知,call() 方法内部会首先调用 doInBackground()
    • 执行完 doInBackground() 内部的操作后,会调用 postResult() 方法将执行结果(Callable)
    • 在 postResult() 方法内会创建一个标志为 MESSAGE_POST_RESULLT 的 Message,再通过 getHandler() 方法获取一个 InternalHandler 发送这个 Message
    • InternalHandler 中接收到标志为 MESSAGE_POST_RESULT 的 Message 后会调用 AsyncTask 的 finish() 方法
    • finish() 方法中会判断这个 AsyncTask 任务是否被取消,若被取消则执行 onCancelled() 方法,否则执行 onPostExecute() 方法
    • 通过这个 onPostExecute() 方法,我们才最终完成了这个任务的执行过程,并得到了异步任务执行后的结果

串行任务处理

SerialExecutor 实际上是一个 ThreadPoolExecutor,只不过对其做了处理,只能完成对 FutureTask 的串行处理,那是如何实现的呢?

(1)首先回到 exec 的 execute() 方法中,其中不仅维护了一个任务队列,还有一个指向队首的 FutureTask 的 Runnable 引用 mActive

(2)当处理队列中的第一个任务或 FutureTask 的 run() 方法执行完毕时,会调用synchronized修饰的 scheduleNext() 方法

(3)scheduleNext() 方法中会取出队首元素并赋值给 mActive,并由 ThreadPoolExecutor 调用 execute() 方法执行任务并返回结果

并行任务处理

那么有没有可能借助 AsyncTask 实现对任务的并行处理吗,答案是肯定的

Android 3.0及以上版本将 SerialExecutor 作为默认的线程,并没有完全删除原有的 ThreadPoolExecutor,因此可以通过如下代码实现

task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

也可以通过该方法传入其他类型的线程池,甚至是自定义的线程池

// 其他类型
task.executeOnExecutor(Executors.newCachedThreadPool(), "");

// 自定义类型
Execuror exec = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
                                       0L, TimeUnit.MILLISECONDS, 
                                       new LinkedBlockingQueue<Runnable>());
task.executeOnExecutor(exec, "");

onProgressUpdate

综合上面的叙述并没有发现这个方法的调用和执行过程,那么它是在什么情况下被调用的呢?

(1)首先明确可以在重写的 doInBackground() 方法中调用 publishProgress() 方法

(2)这个方法内部与 postResult() 方法类似,创建一个标志为 MESSAGE_POST_PROGRESS 的 Message 并通过 InternalHandler 发送

(3)InternalHandler 中接收到标志为 MESSAGE_POST_PROGRESS 的 Message 后就会调用 AsyncTask 的 onProgressUpdate() 方法

4.AsyncTask 使用要求

  • AsyncTask 类必须在UI线程中被加载(Android 4.1之后自动执行)
  • AsyncTask 实例对象,必须在UI线程中创建
  • 必须在 UI 线程中调用 execute() 来运行任务
  • 必须创建 AsyncTask 的子类,以它的子类形式来使用
  • 必须在子类中至少实现 doInBackground() 回调方法,通常我们还会实现 onPostExecute() 方法
  • 不要手动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 方法
  • AsyncTask 后台任务,只能执行一次,否则会抛出异常
  • 整个进程中的 AsyncTask 线程池及任务队列都是同一个(自定义的除外),大量的调用 AsyncTask 执行后台任务会造成任务队列整体执行时间的变长,导致任务执行的时间不可控
  • AsyncTask 的默认线程优先级是 Process.THREAD_PRIORITY_BACKGROUND(后台线程级别),优先级较低,分配的 CPU 资源会较少,不适合执行优先级较高的任务

5.AsyncTask 的问题

生命周期问题与内存泄露

AsyncTask会一直执行,直到doInBackground()方法执行完毕。如果AsyncTask被声明为Activity的非静态内部类,那么AsyncTask会持有对Activity的引用,如果在销毁activity的时候没有取消正在运行的AsyncTask,这会导致Activity没法被回收,从而导致内存泄露

结果丢失

屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效

处理不当容易导致任务阻塞

所以如果一个AsyncTask的doInBackGround方法中存在着一个无限循环没有正确处理,导致这个AsyncTask的doInBackGround方法一直运行,那么这会导致同一个进程中其他的AsyncTask的任务无法执行

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ConnorYan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值