这可能是最完善的AsyncTask源码分析了,看起来很舒服。

本文深入解析Android的AsyncTask源码,包括其定义、线程池概念、Future、Callable和FutureTask。介绍了如何理解线程池、Future获取任务结果,以及AsyncTask的串行执行特性。此外,还探讨了为何AsyncTask不适合长时间任务和其执行限制。通过源码分析,帮助读者全面掌握AsyncTask的工作原理。
摘要由CSDN通过智能技术生成

前言

江湖盛传一句话,编程最好的老师就是看源码害羞。然后当我们十分乖巧的准备好了一切微笑,小心翼翼的打开源码的时候,大部分人都是看不了几行就已经想着今晚要不要吃鸡了得意?那么,为什么会出现这种情况呢?就拿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的概念

如果你想详细了解这块的内容,可以去看这篇博客 Android并发编程之白话文详解Future,FutureTask和Callable
简单说一下:
  • 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);
                sum+=i;
                System.out.println("工作线程"+Thread.currentThread().getName()+"正在执行 sum="+sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        }    
    }
}

我们看到Future有一个get()方法,这个方法是一个阻塞方法,我们调用submit()执行一个任务的时候,会执行Runnable中的run()方法,当run()方法没有执行完的时候,get()就会阻塞主线程,直到run()方法执行结束后&#x

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值