android之Futuretask、Timer、AsyncTask的使用及原理解析

本文详细介绍了Android中FutureTask、Timer和AsyncTask的使用及原理,探讨了它们的优缺点。重点指出Timer同步执行任务的问题,推荐使用ScheduledExecutorService替代。同时,解释了FutureTask作为RunnableFuture接口实现的功能,以及AsyncTask如何扩展FutureTask并在后台执行任务,并通过Handler将结果传递到主线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前用写过这么一篇文章,但是写的很简单(那时候想的只是给自己做个笔记),但是后来想想还是有必要写的详细些,所以就把这前那篇删了,重写整理除了这篇文章。
还是先给出参考的博文地址:
Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等
Java并发编程:Callable、Future和FutureTask
Java 并发专题 : Timer的缺陷 用ScheduledExecutorService替代
AsyncTask解析
Android AsyncTask完全解析,带你从源码的角度彻底理解

其实大家去看下我参考的几篇文章基本就不用看我这篇了,我下面要做的只是简短的归纳整理,相当于一个总结。

  • FutureTask
  • Timer
  • AsyncTask

Timer

这里关于Timer要讲的就只是它的一些使用的注意事项,如果你有很多的定时任务并且使用的是timer.schedule(task1, 1000);方法启动的话,你会发现它是同步执行的,什么是同步执行呢,也就是先执行第一个,然后执行第二个,依此执行下去,这不是我们想要的效果,比如我们想要一个任务在第一秒启动,另一个在第三秒启动,结果却是第一个在第一秒启动没错,第二个却是在第四秒启动的。解决的方法就是使用ScheduledThreadPool。
第二个要注意的就是如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行,使用ScheduledExecutorService解决
最后一点,Timer执行周期任务时依赖系统时间,也就是说假如你有个任务1分钟执行一次,当前时间是9点整,然后过了59秒,系统时间在这个时间被修改成了9点整(本来是已经9点59秒),这个1分钟执行一次的task不会再下一秒执行,而是会在9点01分执行,但中间已经过了1分59秒,这也不是我们想要的效果,所以使用ScheduledExecutorService,这个不是基于系统时间。

FutureTask

FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable接口和Future接口,Future提供了三种功能:

  1)判断任务是否完成;

  2)能够中断任务;

  3)能够获取任务执行结果。
  
所以想想callable(创建FutureTask需要一个Callable对象,这个Callable有点和Runnable有点类似)的call方法在哪里被调用,大致就是在run方法中调用了。具体不再深究,因为这个我不太常用到。具体的使用:使用ExecutorService或者Thread启动。

AsyncTask

为什么在FutureTask讲完之后讲AsyncTask呢?因为它就是FutureTask的一个扩展。讲讲他是实现流程:其构造方法中,定义了一个FutureTask以及一个Callable对象,之后执行execute()方法,在execute方法中首先调用的就是onPreExecute(),随后就执行Callable对象的call方法

public Result call() throws Exception {
    mTaskInvoked.set(true);
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    return postResult(doInBackground(mParams));
}

看最后一行,doInBackground调用了,但是此时仔细想想还是在子线程中的,随后跟进postResult方法中发现handler,那就清晰了,doInBackground中的返回值(包括中间的进度更新)都会发送消息给主线程。这就是基本的实现流程,当然这里是往简单了说,不要忘记还有多个AsyncTask一起被启动的情况,不过原理也就是用了队列的方式调用了Callable对象的call方法,看到这里大家也会知道,问什么3.0之后开启多个AsyncTask的时候,它是同步执行的,也就是执行了一个之后再去执行下一个,因为它是队列的调度,不断的next

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

如果不想这样可以指定像下面的方式使用

Executor exec = new ThreadPoolExecutor(5, 200, 10,
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
new DownloadTask().executeOnExecutor(exec);

意思是如果你一开始启动了10个线程,其中5个会立马并发执行,另5个会进入队列,假如这里队列能容纳5个,那么当第11个启动的时候(前5个还在执行)他也会启动,因为最多能保证200个线程并发执行,所以这里能创建205个线程,其中200是在执行的,还有5个在队列中,当206个创建的时候(前205个还在没cancel掉)就会抛出异常。