前言
江湖盛传一句话,编程最好的老师就是看源码。然后当我们十分乖巧的准备好了一切
,小心翼翼的打开源码的时候,大部分人都是看不了几行就已经想着今晚要不要吃鸡了
?那么,为什么会出现这种情况呢?就拿AsyncTask举例:
1.源码中设计到很多你不懂的知识点:ThreadPoolExecutor,SerialExecutor,FutureTask,Callable等等。注意:先不要去想这4个单词是干什么用的,后面会详细讲解。
2.源码中不会给你一个完整的流程框架,你可能看着看着就跑偏了,就像男生第一次羞羞,根本找不到正确的方向。
那么,本文将带领你一步一步的摸清楚AsyncTask的来龙去脉,以及让你掌握以下知识:线程池,Future,FutureTask和Callable等。
如果你还不会使用AsynTask的用法,建议您先看一下这篇文章Android 多线程:AsyncTask最详细使用教程
目录
定义
- 一个Android 已封装好的轻量级异步类
- 属于抽象类,即使用时需实现子类
- AsyncTask对于执行耗时任务,然后更新UI是一把利器,当然也是替代Thread + Handler 的一种方式
储备知识点
线程池概念
如果你想详细了解这块的内容,可以去看这篇博客Android性能优化之使用线程池处理异步任务。
AsyncTask只用到一个固定线程数量的线程池。构造方法如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {//...}
线程池的构造参数很多,不要着急,不要恐慌,我们接下来就来一一介绍:
corePoolSize:线程池中的核心线程数量
maximumPoolSize:线程池中的最大线程数量
keepAliveTime:保持活动时间,也就是线程保活时间不过它起作用必须在一个前提下,就是当线程池中的线程数量超过了corePoolSize时,它表示多余的空闲线程的存活时间,即:多余的空闲线程在超过keepAliveTime时间内没有任务的话则被销毁。而这个主要应用在缓存线程池中
unit:它是一个枚举类型,表示keepAliveTime的单位,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)
workQueue:任务队列,主要用来存储已经提交但未被执行的任务,不同的线程池采用的排队策略不一样
threadFactory:线程工厂,用来创建线程池中的线程,通常用默认的即可
看完了构造方法和参数意义,可能你心里对线程池有个简单的了解了,本质就是:重用已有的线程,从而减少线程的创建。
Future,Callable和FutureTask的概念
- Future:可以获得一个任务的结果
- Callable:任务的实现,可以返回一个结果
- FutureTask:包装了Callable,并且提供一个任务结束时回调的done()方法
为了让大家更能够理解,我先来介绍一下线程池执行一个任务的代码:
任务描述:在一个线程池中算出1到10的和是多少
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor(); // 创建只有一个线程的线程池
CountRunnable work = new CountRunnable();
es.execute(work);
es.shutdown();
System.out.println("任务结束");
}
public static class CountRunnable implements Runnable{
private int sum;
@Override
public void run() {
for(int i=1 ; i<11 ; i++){
sum+=i;
}
System.out.println("sum="+sum);
}
}
打印结果:sum=55,任务结束
但是大家发现没有,run()方法只是一个简单的执行,实现Runnable没有办法返回结果,如果我们想把num返回给main()函数怎么办呢?那就就需要用Future来解决了。
Future:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
- Future是一个接口,他提供给了我们方法来检测当前的任务是否已经结束,还可以等待任务结束并且拿到一个结果(通过调用Future的get()方法可以当任务结束后返回一个结果值)。
- 当调用get()方法时,如果工作没有结束,则会阻塞当前线程,直到任务执行完毕。
- 我们可以通过调用cancel()方法来停止一个正在执行的任务,如果这个正在执行的任务停止了,则cancel()方法会返回true;
- 如果任务是已经完成或者已经停止了或者这个任务无法停止,则调用cancel()会返回一个false。
- 当一个任务被成功停止后,他无法再次执行。isDone()和isCancel()方法可以判断当前工作是否完成和是否取消。
线程池还有一个方法可以执行一个任务,那就是submit()方法
public Future<?> submit(Runnable task) {
return e.submit(task);
}
我们看到他会返回一个Future对象,这个Future对象的泛型里还用的是一个问号“?”,问号就是说我们不知道要返回的对象是什么类型,那么就返回一个null好了,如果执行的是一个Runnable对象,Runnable是没有返回值的,所以这里用一个问号,说明没有返回值,那么就返回一个null好了。
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
CountRunnable work = new CountRunnable();
Future<?> future = es.submit(work);
System.out.println("任务开始于"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
for(int i=0 ; i<10 ; i++){
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("主线程"+Thread.currentThread().getName()+"仍然可以执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Object object = future.get();
System.out.println("任务结束于"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" result="+object);
} catch (Exception e) {
e.printStackTrace();
}
es.shutdown();
System.out.println("关闭线程池"+es.isShutdown());
}
public static class CountRunnable implements Runnable{
private int sum;
@Override
public void run() {
for(int i=1 ; i<11 ; i++){
try {
TimeUnit.MILLISECONDS.sleep(1000);